Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index fd9bc0e9fced..e05c81366d9b 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,4803 +1,4830 @@
/*
* 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 "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) {
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;
} else if (os_strcmp(buf, "ssid2") == 0) {
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_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
- if (eapol_version < 1 || eapol_version > 3) {
+ int max_ver = 3;
#else /* CONFIG_MACSEC */
- if (eapol_version < 1 || eapol_version > 2) {
+ 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_rts_threshold") == 0) {
conf->he_op.he_rts_threshold = 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 {
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/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 62fa51e91c20..b39f40252f29 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,5164 +1,5197 @@
/*
* 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 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, " 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,
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_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 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_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;
char *tmp;
int ret;
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;
}
}
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;
}
}
if (!buf)
goto set;
if (os_strstr(buf, "stat"))
stationary = 1;
set:
ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
stationary);
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, "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_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_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/hostapd/hostapd.conf b/hostapd/hostapd.conf
index e3ee8b2a0c1b..7932cb862f48 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1,3005 +1,3025 @@
##### 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_rts_threshold: Duration of STA transmission
# 0 = not set (default)
# unsigned integer = duration in units of 16 us
#he_rts_threshold=0
# HE operating channel information; see matching vht_* parameters for details.
# 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
+# 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
+# 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.
# 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
##### 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/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
index 1e67f0d5996e..abe817c5b015 100644
--- a/src/ap/airtime_policy.c
+++ b/src/ap/airtime_policy.c
@@ -1,269 +1,273 @@
/*
* Airtime policy configuration
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
*
* 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 "hostapd.h"
#include "ap_drv_ops.h"
#include "sta_info.h"
#include "airtime_policy.h"
/* Idea:
* Two modes of airtime enforcement:
* 1. Static weights: specify weights per MAC address with a per-BSS default
* 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
* enforce relative total shares between BSSes.
*
* - Periodic per-station callback to update queue status.
*
* Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
* keep them updated in sta_info.
*
* - Separate periodic per-bss (or per-iface?) callback to update weights.
*
* Just need to loop through all interfaces, count sum the active stations (or
* should the per-STA callback just adjust that for the BSS?) and calculate new
* weights.
*/
static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
unsigned int *sec,
unsigned int *usec)
{
unsigned int update_int = iface->conf->airtime_update_interval;
if (!update_int) {
wpa_printf(MSG_ERROR,
"Airtime policy: Invalid airtime policy update interval %u",
update_int);
return -1;
}
*sec = update_int / 1000;
*usec = (update_int % 1000) * 1000;
return 0;
}
static void set_new_backlog_time(struct hostapd_data *hapd,
struct sta_info *sta,
struct os_reltime *now)
{
sta->backlogged_until = *now;
sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
AIRTIME_BACKLOG_EXPIRY_FACTOR;
while (sta->backlogged_until.usec >= 1000000) {
sta->backlogged_until.sec++;
sta->backlogged_until.usec -= 1000000;
}
}
static void count_backlogged_sta(struct hostapd_data *hapd)
{
struct sta_info *sta;
struct hostap_sta_driver_data data = {};
unsigned int num_backlogged = 0;
struct os_reltime now;
os_get_reltime(&now);
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
continue;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->force_backlog_bytes)
+ data.backlog_bytes = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
if (data.backlog_bytes > 0)
set_new_backlog_time(hapd, sta, &now);
if (os_reltime_before(&now, &sta->backlogged_until))
num_backlogged++;
}
hapd->num_backlogged_sta = num_backlogged;
}
static int sta_set_airtime_weight(struct hostapd_data *hapd,
struct sta_info *sta,
unsigned int weight)
{
int ret = 0;
if (weight != sta->airtime_weight &&
(ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
return ret;
sta->airtime_weight = weight;
return ret;
}
static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
{
struct sta_info *sta;
for (sta = hapd->sta_list; sta; sta = sta->next)
sta_set_airtime_weight(hapd, sta, weight);
}
static unsigned int get_airtime_quantum(unsigned int max_wt)
{
unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
if (quantum < AIRTIME_QUANTUM_MIN)
quantum = AIRTIME_QUANTUM_MIN;
else if (quantum > AIRTIME_QUANTUM_MAX)
quantum = AIRTIME_QUANTUM_MAX;
return quantum;
}
static void update_airtime_weights(void *eloop_data, void *user_data)
{
struct hostapd_iface *iface = eloop_data;
struct hostapd_data *bss;
unsigned int sec, usec;
unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
wt_sum = 0;
unsigned int quantum;
bool all_div_min = true;
bool apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
int wt, num_bss = 0, max_wt = 0;
size_t i;
for (i = 0; i < iface->num_bss; i++) {
bss = iface->bss[i];
if (!bss->started || !bss->conf->airtime_weight)
continue;
count_backlogged_sta(bss);
if (!bss->num_backlogged_sta)
continue;
if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
num_sta_min = bss->num_backlogged_sta;
num_sta_prod *= bss->num_backlogged_sta;
num_sta_sum += bss->num_backlogged_sta;
wt_sum += bss->conf->airtime_weight;
num_bss++;
}
if (num_sta_min) {
for (i = 0; i < iface->num_bss; i++) {
bss = iface->bss[i];
if (!bss->started || !bss->conf->airtime_weight)
continue;
/* Check if we can divide all sta numbers by the
* smallest number to keep weights as small as possible.
* This is a lazy way to avoid having to factor
* integers. */
if (bss->num_backlogged_sta &&
bss->num_backlogged_sta % num_sta_min > 0)
all_div_min = false;
/* If we're in LIMIT mode, we only apply the weight
* scaling when the BSS(es) marked as limited would a
* larger share than the relative BSS weights indicates
* it should. */
if (!apply_limit && bss->conf->airtime_limit) {
if (bss->num_backlogged_sta * wt_sum >
bss->conf->airtime_weight * num_sta_sum)
apply_limit = true;
}
}
if (all_div_min)
num_sta_prod /= num_sta_min;
}
for (i = 0; i < iface->num_bss; i++) {
bss = iface->bss[i];
if (!bss->started || !bss->conf->airtime_weight)
continue;
/* We only set the calculated weight if the BSS has active
* stations and there are other active interfaces as well -
* otherwise we just set a unit weight. This ensures that
* the weights are set reasonably when stations transition from
* inactive to active. */
if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
wt = bss->conf->airtime_weight * num_sta_prod /
bss->num_backlogged_sta;
else
wt = 1;
bss->airtime_weight = wt;
if (wt > max_wt)
max_wt = wt;
}
quantum = get_airtime_quantum(max_wt);
for (i = 0; i < iface->num_bss; i++) {
bss = iface->bss[i];
if (!bss->started || !bss->conf->airtime_weight)
continue;
set_sta_weights(bss, bss->airtime_weight * quantum);
}
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
return;
eloop_register_timeout(sec, usec, update_airtime_weights, iface,
NULL);
}
static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
{
struct airtime_sta_weight *wt;
wt = hapd->conf->airtime_weight_list;
while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
wt = wt->next;
return wt ? wt->weight : hapd->conf->airtime_weight;
}
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
unsigned int weight;
if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
weight = get_weight_for_sta(hapd, sta->addr);
if (weight)
return sta_set_airtime_weight(hapd, sta, weight);
}
return 0;
}
int airtime_policy_update_init(struct hostapd_iface *iface)
{
unsigned int sec, usec;
if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
return 0;
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
return -1;
eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
return 0;
}
void airtime_policy_update_deinit(struct hostapd_iface *iface)
{
eloop_cancel_timeout(update_airtime_weights, iface, NULL);
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 452386b7e5de..7b6249bbe5cf 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,1633 +1,1638 @@
/*
* 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_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 */
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/src/ap/ap_config.h b/src/ap/ap_config.h
index 8aeb03107af2..95bd79873a59 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,1181 +1,1192 @@
/*
* 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];
};
/**
* 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;
u16 he_rts_threshold;
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/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index a42070116771..61c8f64eb471 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -1,406 +1,419 @@
/*
* hostapd - Driver operations
* Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AP_DRV_OPS
#define AP_DRV_OPS
enum wpa_driver_if_type;
struct wpa_bss_params;
struct wpa_driver_scan_params;
struct ieee80211_ht_capabilities;
struct ieee80211_vht_capabilities;
struct hostapd_freq_params;
u32 hostapd_sta_flags_to_drv(u32 flags);
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
struct wpabuf **beacon,
struct wpabuf **proberesp,
struct wpabuf **assocresp);
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
struct wpabuf *proberesp,
struct wpabuf *assocresp);
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
int enabled);
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
const u8 *addr, int aid, int val);
int hostapd_sta_add(struct hostapd_data *hapd,
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_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,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
int hostapd_if_add(struct hostapd_data *hapd, 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 hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
struct wpa_bss_params *params);
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
const u8 *addr, int idx, u8 *seq);
int hostapd_flush(struct hostapd_data *hapd);
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int freq, int channel, int 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);
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
int total_flags, int flags_or, int flags_and);
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
unsigned int weight);
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time);
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags, u8 *dfs_domain);
int hostapd_driver_commit(struct hostapd_data *hapd);
int hostapd_drv_none(struct hostapd_data *hapd);
bool hostapd_drv_nl80211(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params);
struct wpa_scan_results * hostapd_driver_get_scan_results(
struct hostapd_data *hapd);
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
int duration);
int hostapd_drv_set_key(const char *ifname,
struct hostapd_data *hapd,
enum wpa_alg alg, const u8 *addr,
int key_idx, int vlan_id, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag);
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
const void *msg, size_t len, int noack,
const u16 *csa_offs, size_t csa_offs_len,
int no_encrypt);
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
const u8 *addr, int reason);
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
const u8 *addr, int reason);
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
unsigned int wait, const u8 *dst, const u8 *data,
size_t len);
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
unsigned int freq,
unsigned int wait, const u8 *dst,
const u8 *data, size_t len);
static inline void
hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
{
if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
!hapd->drv_priv)
return;
hapd->driver->send_action_cancel_wait(hapd->drv_priv);
}
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
u16 auth_alg);
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
u16 seq, u16 status, const u8 *ie, size_t len);
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
int reassoc, u16 status, const u8 *ie, size_t len);
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
u8 *tspec_ie, size_t tspec_ielen);
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
enum hostapd_hw_mode mode, int freq,
int channel, int ht_enabled, int vht_enabled,
int he_enabled,
int sec_channel_offset, int oper_chwidth,
int center_segment0, int center_segment1);
int hostapd_drv_do_acs(struct hostapd_data *hapd);
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
u16 reason_code, const u8 *ie, size_t ielen);
int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
#include "drivers/driver.h"
int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
enum wnm_oper oper, const u8 *peer,
u8 *buf, u16 *buf_len);
int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
u8 qos_map_set_len);
void hostapd_get_ext_capa(struct hostapd_iface *iface);
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
int enabled)
{
if (hapd->driver == NULL ||
hapd->driver->hapd_set_countermeasures == NULL)
return 0;
return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
}
static inline int hostapd_drv_set_sta_vlan(const char *ifname,
struct hostapd_data *hapd,
const u8 *addr, int vlan_id)
{
if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
return 0;
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
vlan_id);
}
static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
const u8 *addr)
{
if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
return 0;
return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
}
static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
const u8 *addr)
{
if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
return 0;
return hapd->driver->sta_remove(hapd->drv_priv, addr);
}
static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
u32 flags)
{
if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
return 0;
return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
data_len, encrypt,
hapd->own_addr, flags);
}
static inline int hostapd_drv_read_sta_data(
struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
const u8 *addr)
{
if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
return -1;
return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
}
static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
const u8 *addr)
{
if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
return 0;
return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
}
static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
struct hostapd_acl_params *params)
{
if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
return 0;
return hapd->driver->set_acl(hapd->drv_priv, params);
}
static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
return 0;
return hapd->driver->set_ap(hapd->drv_priv, params);
}
static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
const u8 *mac, int accepted,
u32 session_timeout)
{
if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
return 0;
return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
session_timeout);
}
static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
const u8 *mac)
{
if (hapd->driver == NULL ||
hapd->driver->set_radius_acl_expire == NULL)
return 0;
return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
}
static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
int auth_algs)
{
if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
return 0;
return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
}
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
const u8 *own_addr, const u8 *addr,
int qos)
{
if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
return;
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
}
static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
unsigned int freq)
{
if (hapd->driver == NULL)
return -1;
if (!hapd->driver->get_survey)
return -1;
return hapd->driver->get_survey(hapd->drv_priv, freq);
}
static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
{
if (hapd->driver == NULL || hapd->driver->get_country == NULL)
return -1;
return hapd->driver->get_country(hapd->drv_priv, alpha2);
}
static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->get_radio_name == NULL)
return NULL;
return hapd->driver->get_radio_name(hapd->drv_priv);
}
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
hapd->drv_priv == NULL)
return -1;
return hapd->driver->switch_channel(hapd->drv_priv, settings);
}
static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
size_t buflen)
{
if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
return -1;
return hapd->driver->status(hapd->drv_priv, buf, buflen);
}
static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
int version, const u8 *ipaddr,
int prefixlen, const u8 *addr)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->br_add_ip_neigh == NULL)
return -1;
return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
prefixlen, addr);
}
static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
u8 version, const u8 *ipaddr)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->br_delete_ip_neigh == NULL)
return -1;
return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
ipaddr);
}
static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
enum drv_br_port_attr attr,
unsigned int val)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->br_port_set_attr == NULL)
return -1;
return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
}
static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
enum drv_br_net_param param,
unsigned int val)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->br_set_net_param == NULL)
return -1;
return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
}
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
int vendor_id, int subcmd,
const u8 *data, size_t data_len,
enum nested_attr nested_attr_flag,
struct wpabuf *buf)
{
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
return -1;
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
data_len, nested_attr_flag, buf);
}
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
{
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
return 0;
return hapd->driver->stop_ap(hapd->drv_priv);
}
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
struct wpa_channel_info *ci)
{
if (!hapd->driver || !hapd->driver->channel_info)
return -1;
return hapd->driver->channel_info(hapd->drv_priv, ci);
}
static inline int
hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
struct external_auth *params)
{
if (!hapd->driver || !hapd->drv_priv ||
!hapd->driver->send_external_auth_status)
return -1;
return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
}
static inline int
hostapd_drv_set_band(struct hostapd_data *hapd, u32 band_mask)
{
if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
return -1;
return hapd->driver->set_band(hapd->drv_priv, band_mask);
}
#ifdef ANDROID
static inline int hostapd_drv_driver_cmd(struct hostapd_data *hapd,
char *cmd, char *buf, size_t buf_len)
{
if (!hapd->driver->driver_cmd)
return -1;
return hapd->driver->driver_cmd(hapd->drv_priv, cmd, buf, buf_len);
}
#endif /* ANDROID */
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int
+hostapd_drv_register_frame(struct hostapd_data *hapd, u16 type,
+ const u8 *match, size_t match_len,
+ bool multicast)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->register_frame)
+ return -1;
+ return hapd->driver->register_frame(hapd->drv_priv, type, match,
+ match_len, multicast);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index e1e5a3ac4bb3..aaeb94c2f53b 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1,2626 +1,2641 @@
/*
* 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) == 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 (!resp || status_code != WLAN_STATUS_SUCCESS) {
+ 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) == 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) == 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) == 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);
#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;
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_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/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 290d354a016c..ec5abf166b23 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1,2111 +1,2111 @@
/*
* hostapd / Callback functions for driver wrappers
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "radius/radius.h"
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/dpp.h"
#include "common/sae.h"
#include "common/hw_features_common.h"
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
#include "fst/fst.h"
#include "wnm_ap.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "ieee802_11_auth.h"
#include "sta_info.h"
#include "accounting.h"
#include "tkip_countermeasures.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "ap_mlme.h"
#include "hw_features.h"
#include "dfs.h"
#include "beacon.h"
#include "mbo_ap.h"
#include "dpp_hostapd.h"
#include "fils_hlp.h"
#include "neighbor_db.h"
#ifdef CONFIG_FILS
void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta)
{
u16 reply_res = WLAN_STATUS_SUCCESS;
struct ieee802_11_elems elems;
u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
int new_assoc;
wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
__func__, MAC2STR(sta->addr));
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
if (!sta->fils_pending_assoc_req)
return;
ieee802_11_parse_elems(sta->fils_pending_assoc_req,
sta->fils_pending_assoc_req_len, &elems, 0);
if (!elems.fils_session) {
wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
__func__);
return;
}
p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
elems.fils_session,
sta->fils_hlp_resp);
reply_res = hostapd_sta_assoc(hapd, sta->addr,
sta->fils_pending_assoc_is_reassoc,
WLAN_STATUS_SUCCESS,
buf, p - buf);
ap_sta_set_authorized(hapd, sta, 1);
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
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;
fils_hlp_deinit(hapd);
/*
* 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 (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
}
#endif /* CONFIG_FILS */
static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
{
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 && (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;
}
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *req_ies, size_t req_ies_len, int reassoc)
{
struct sta_info *sta;
int new_assoc;
enum wpa_validate_result res;
struct ieee802_11_elems elems;
const u8 *ie;
size_t ielen;
u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
u8 *p = buf;
u16 reason = WLAN_REASON_UNSPECIFIED;
int status = WLAN_STATUS_SUCCESS;
const u8 *p2p_dev_addr = NULL;
if (addr == NULL) {
/*
* This could potentially happen with unexpected event from the
* driver wrapper. This was seen at least in one case where the
* driver ended up being set to station mode while hostapd was
* running, so better make sure we stop processing such an
* event here.
*/
wpa_printf(MSG_DEBUG,
"hostapd_notif_assoc: Skip event with no address");
return -1;
}
if (is_multicast_ether_addr(addr) ||
is_zero_ether_addr(addr) ||
os_memcmp(addr, 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, "%s: Invalid SA=" MACSTR
" in received indication - ignore this indication silently",
__func__, MAC2STR(addr));
return 0;
}
random_add_randomness(addr, ETH_ALEN);
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "associated");
ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
if (elems.wps_ie) {
ie = elems.wps_ie - 2;
ielen = elems.wps_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
} else if (elems.rsn_ie) {
ie = elems.rsn_ie - 2;
ielen = elems.rsn_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
} else if (elems.wpa_ie) {
ie = elems.wpa_ie - 2;
ielen = elems.wpa_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
#ifdef CONFIG_HS20
} else if (elems.osen) {
ie = elems.osen - 2;
ielen = elems.osen_len + 2;
wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
#endif /* CONFIG_HS20 */
} else {
ie = NULL;
ielen = 0;
wpa_printf(MSG_DEBUG,
"STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
}
sta = ap_get_sta(hapd, addr);
if (sta) {
ap_sta_no_session_timeout(hapd, sta);
accounting_sta_stop(hapd, sta);
/*
* Make sure that the previously registered inactivity timer
* will not remove the STA immediately.
*/
sta->timeout_next = STA_NULLFUNC;
} else {
sta = ap_sta_add(hapd, addr);
if (sta == NULL) {
hostapd_drv_sta_disassoc(hapd, addr,
WLAN_REASON_DISASSOC_AP_BUSY);
return -1;
}
}
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
/*
* ACL configurations to the drivers (implementing AP SME and ACL
* offload) without hostapd's knowledge, can result in a disconnection
* though the driver accepts the connection. Skip the hostapd check for
* ACL if the driver supports ACL offload to avoid potentially
* conflicting ACL rules.
*/
if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
MAC2STR(addr));
reason = WLAN_REASON_UNSPECIFIED;
goto fail;
}
#ifdef CONFIG_P2P
if (elems.p2p) {
wpabuf_free(sta->p2p_ie);
sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
P2P_IE_VENDOR_TYPE);
if (sta->p2p_ie)
p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
}
#endif /* CONFIG_P2P */
#ifdef NEED_AP_MLME
if (elems.ht_capabilities &&
(hapd->iface->conf->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
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)
ht40_intolerant_add(hapd->iface, sta);
}
#endif /* NEED_AP_MLME */
#ifdef CONFIG_INTERWORKING
if (elems.ext_capab && elems.ext_capab_len > 4) {
if (elems.ext_capab[4] & 0x01)
sta->qos_map_enabled = 1;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
elems.hs20_len - 4);
} 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 */
mbo_ap_check_sta_assoc(hapd, sta, &elems);
ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
elems.supp_op_classes_len);
if (hapd->conf->wpa) {
if (ie == NULL || ielen == 0) {
#ifdef CONFIG_WPS
if (hapd->conf->wps_state) {
wpa_printf(MSG_DEBUG,
"STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
sta->flags |= WLAN_STA_MAYBE_WPS;
goto skip_wpa_check;
}
#endif /* CONFIG_WPS */
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
goto fail;
}
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
struct wpabuf *wps;
if (check_sa_query_need(hapd, sta)) {
status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
p = hostapd_eid_assoc_comeback_time(hapd, sta,
p);
hostapd_sta_assoc(hapd, addr, reassoc, status,
buf, p - buf);
return 0;
}
sta->flags |= WLAN_STA_WPS;
wps = ieee802_11_vendor_ie_concat(ie, ielen,
WPS_IE_VENDOR_TYPE);
if (wps) {
if (wps_is_20(wps)) {
wpa_printf(MSG_DEBUG,
"WPS: STA supports WPS 2.0");
sta->flags |= WLAN_STA_WPS2;
}
wpabuf_free(wps);
}
goto skip_wpa_check;
}
#endif /* CONFIG_WPS */
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_ERROR,
"Failed to initialize WPA state machine");
return -1;
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
ie, ielen,
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);
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
switch (res) {
case WPA_IE_OK:
reason = WLAN_REASON_UNSPECIFIED;
status = WLAN_STATUS_SUCCESS;
break;
case WPA_INVALID_IE:
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
break;
case WPA_INVALID_GROUP:
reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
break;
case WPA_INVALID_PAIRWISE:
reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
break;
case WPA_INVALID_AKMP:
reason = WLAN_REASON_AKMP_NOT_VALID;
status = WLAN_STATUS_AKMP_NOT_VALID;
break;
case WPA_NOT_ENABLED:
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
break;
case WPA_ALLOC_FAIL:
reason = WLAN_REASON_UNSPECIFIED;
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
break;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
break;
case WPA_INVALID_MGMT_GROUP_CIPHER:
reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
break;
case WPA_INVALID_MDIE:
reason = WLAN_REASON_INVALID_MDE;
status = WLAN_STATUS_INVALID_MDIE;
break;
case WPA_INVALID_PROTO:
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
break;
case WPA_INVALID_PMKID:
reason = WLAN_REASON_INVALID_PMKID;
status = WLAN_STATUS_INVALID_PMKID;
break;
case WPA_DENIED_OTHER_REASON:
reason = WLAN_REASON_UNSPECIFIED;
status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
break;
}
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"WPA/RSN information element rejected? (res %u)",
res);
wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
goto fail;
}
if (check_sa_query_need(hapd, sta)) {
status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
p - buf);
return 0;
}
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) {
status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
req_ies_len);
if (status != WLAN_STATUS_SUCCESS) {
if (status == WLAN_STATUS_INVALID_PMKID)
reason = WLAN_REASON_INVALID_IE;
if (status == WLAN_STATUS_INVALID_MDIE)
reason = WLAN_REASON_INVALID_IE;
if (status == WLAN_STATUS_INVALID_FTIE)
reason = WLAN_REASON_INVALID_IE;
goto fail;
}
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
if (hapd->conf->sae_pwe == 2 &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
- elems.rsnxe && elems.rsnxe_len >= 1 &&
- (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_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));
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
reason = WLAN_REASON_UNSPECIFIED;
goto fail;
}
#endif /* CONFIG_SAE */
} else if (hapd->conf->wps_state) {
#ifdef CONFIG_WPS
struct wpabuf *wps;
if (req_ies)
wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
WPS_IE_VENDOR_TYPE);
else
wps = NULL;
#ifdef CONFIG_WPS_STRICT
if (wps && wps_validate_assoc_req(wps) < 0) {
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
wpabuf_free(wps);
goto fail;
}
#endif /* CONFIG_WPS_STRICT */
if (wps) {
sta->flags |= WLAN_STA_WPS;
if (wps_is_20(wps)) {
wpa_printf(MSG_DEBUG,
"WPS: STA supports WPS 2.0");
sta->flags |= WLAN_STA_WPS2;
}
} else
sta->flags |= WLAN_STA_MAYBE_WPS;
wpabuf_free(wps);
#endif /* CONFIG_WPS */
#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 */
}
#ifdef CONFIG_WPS
skip_wpa_check:
#endif /* CONFIG_WPS */
#ifdef CONFIG_MBO
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 */
#ifdef CONFIG_IEEE80211R_AP
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
sta->auth_alg, req_ies, req_ies_len,
!elems.rsnxe);
if (!p) {
wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
#endif /* CONFIG_IEEE80211R_AP */
#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 delay_assoc = 0;
if (!req_ies)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
req_ies_len,
sta->fils_session)) {
wpa_printf(MSG_DEBUG,
"FILS: Session validation failed");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
req_ies_len);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"FILS: Key Confirm validation failed");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
wpa_printf(MSG_DEBUG,
"FILS: Delaying Assoc Response (HLP)");
delay_assoc = 1;
} else {
wpa_printf(MSG_DEBUG,
"FILS: Going ahead with Assoc Response (no HLP)");
}
if (sta) {
wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
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;
sta->fils_drv_assoc_finish = 0;
}
if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
u8 *req_tmp;
req_tmp = os_malloc(req_ies_len);
if (!req_tmp) {
wpa_printf(MSG_DEBUG,
"FILS: buffer allocation failed for assoc req");
goto fail;
}
os_memcpy(req_tmp, req_ies, req_ies_len);
sta->fils_pending_assoc_req = req_tmp;
sta->fils_pending_assoc_req_len = req_ies_len;
sta->fils_pending_assoc_is_reassoc = reassoc;
sta->fils_drv_assoc_finish = 1;
wpa_printf(MSG_DEBUG,
"FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
MACSTR, MAC2STR(sta->addr));
eloop_register_timeout(
0, hapd->conf->fils_hlp_wait_time * 1024,
fils_hlp_timeout, hapd, sta);
return 0;
}
p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
elems.fils_session,
sta->fils_hlp_resp);
wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
buf, p - buf);
}
#endif /* CONFIG_FILS */
#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) {
u8 *npos;
u16 ret_status;
npos = owe_assoc_req_process(hapd, sta,
elems.owe_dh, elems.owe_dh_len,
p, sizeof(buf) - (p - buf),
&ret_status);
status = ret_status;
if (npos)
p = npos;
if (!npos &&
status == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
hostapd_sta_assoc(hapd, addr, reassoc, ret_status, buf,
p - buf);
return 0;
}
if (!npos || status != WLAN_STATUS_SUCCESS)
goto fail;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
if ((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;
reason = WLAN_REASON_UNSPECIFIED;
goto fail;
}
}
wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
sta->dpp_pfs->secret : NULL);
pfs_fail:
#endif /* CONFIG_DPP2 */
if (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 defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
if (sta->auth_alg == WLAN_AUTH_FT ||
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)
ap_sta_set_authorized(hapd, sta, 1);
#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
/* Keep compiler silent about unused variables */
if (status) {
}
#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
#ifdef CONFIG_FILS
else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
#endif /* CONFIG_FILS */
else
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
#ifdef CONFIG_P2P
if (req_ies) {
p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
req_ies, req_ies_len);
}
#endif /* CONFIG_P2P */
return 0;
fail:
#ifdef CONFIG_IEEE80211R_AP
if (status >= 0)
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
#endif /* CONFIG_IEEE80211R_AP */
hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
ap_free_sta(hapd, sta);
return -1;
}
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
if (addr == NULL) {
/*
* This could potentially happen with unexpected event from the
* driver wrapper. This was seen at least in one case where the
* driver ended up reporting a station mode event while hostapd
* was running, so better make sure we stop processing such an
* event here.
*/
wpa_printf(MSG_DEBUG,
"hostapd_notif_disassoc: Skip event with no address");
return;
}
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG,
"Disassociation notification for unknown STA "
MACSTR, MAC2STR(addr));
return;
}
ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
hostapd_set_sta_flags(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
ap_free_sta(hapd, sta);
}
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
return;
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"disconnected due to excessive missing ACKs");
hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
}
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)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
const char *txt;
if (!sta)
return;
switch (smps_mode) {
case SMPS_AUTOMATIC:
txt = "automatic";
break;
case SMPS_OFF:
txt = "off";
break;
case SMPS_DYNAMIC:
txt = "dynamic";
break;
case SMPS_STATIC:
txt = "static";
break;
default:
txt = NULL;
break;
}
if (txt) {
wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
MACSTR " %s", MAC2STR(addr), txt);
}
switch (chan_width) {
case CHAN_WIDTH_20_NOHT:
txt = "20(no-HT)";
break;
case CHAN_WIDTH_20:
txt = "20";
break;
case CHAN_WIDTH_40:
txt = "40";
break;
case CHAN_WIDTH_80:
txt = "80";
break;
case CHAN_WIDTH_80P80:
txt = "80+80";
break;
case CHAN_WIDTH_160:
txt = "160";
break;
default:
txt = NULL;
break;
}
if (txt) {
wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
MACSTR " %s", MAC2STR(addr), txt);
}
if (rx_nss != 0xff) {
wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
MACSTR " %d", MAC2STR(addr), rx_nss);
}
}
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
int offset, int width, int cf1, int cf2,
int finished)
{
#ifdef NEED_AP_MLME
int channel, chwidth, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
finished ? "had" : "starting",
freq, ht, hapd->iconf->ch_switch_vht_config,
hapd->iconf->ch_switch_he_config, offset,
width, channel_width_to_string(width), cf1, cf2);
if (!hapd->iface->current_mode) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"ignore channel switch since the interface is not yet ready");
return;
}
hapd->iface->freq = freq;
channel = hostapd_hw_get_channel(hapd, freq);
if (!channel) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"driver switched to bad channel!");
return;
}
switch (width) {
case CHAN_WIDTH_80:
chwidth = CHANWIDTH_80MHZ;
break;
case CHAN_WIDTH_80P80:
chwidth = CHANWIDTH_80P80MHZ;
break;
case CHAN_WIDTH_160:
chwidth = CHANWIDTH_160MHZ;
break;
case CHAN_WIDTH_20_NOHT:
case CHAN_WIDTH_20:
case CHAN_WIDTH_40:
default:
chwidth = CHANWIDTH_USE_HT;
break;
}
switch (hapd->iface->current_mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
if (cf1 == 5935)
seg0_idx = (cf1 - 5925) / 5;
else if (cf1 > 5950)
seg0_idx = (cf1 - 5950) / 5;
else if (cf1 > 5000)
seg0_idx = (cf1 - 5000) / 5;
if (cf2 == 5935)
seg1_idx = (cf2 - 5925) / 5;
else if (cf2 > 5950)
seg1_idx = (cf2 - 5950) / 5;
else if (cf2 > 5000)
seg1_idx = (cf2 - 5000) / 5;
break;
default:
ieee80211_freq_to_chan(cf1, &seg0_idx);
ieee80211_freq_to_chan(cf2, &seg1_idx);
break;
}
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
if (!ht) {
hapd->iconf->ieee80211ac = 0;
} else if (hapd->iconf->ch_switch_vht_config) {
/* CHAN_SWITCH VHT config */
if (hapd->iconf->ch_switch_vht_config &
CH_SWITCH_VHT_ENABLED)
hapd->iconf->ieee80211ac = 1;
else if (hapd->iconf->ch_switch_vht_config &
CH_SWITCH_VHT_DISABLED)
hapd->iconf->ieee80211ac = 0;
} else if (hapd->iconf->ch_switch_he_config) {
/* CHAN_SWITCH HE config */
if (hapd->iconf->ch_switch_he_config &
CH_SWITCH_HE_ENABLED)
hapd->iconf->ieee80211ax = 1;
else if (hapd->iconf->ch_switch_he_config &
CH_SWITCH_HE_DISABLED)
hapd->iconf->ieee80211ax = 0;
}
hapd->iconf->ch_switch_vht_config = 0;
hapd->iconf->ch_switch_he_config = 0;
hapd->iconf->secondary_channel = offset;
hostapd_set_oper_chwidth(hapd->iconf, chwidth);
hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
if (hapd->iconf->ieee80211ac) {
hapd->iconf->vht_capab &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
if (chwidth == CHANWIDTH_160MHZ)
hapd->iconf->vht_capab |=
VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
else if (chwidth == CHANWIDTH_80P80MHZ)
hapd->iconf->vht_capab |=
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
}
is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
hapd->iface->num_hw_features);
wpa_msg(hapd->msg_ctx, MSG_INFO,
"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
finished ? WPA_EVENT_CHANNEL_SWITCH :
WPA_EVENT_CHANNEL_SWITCH_STARTED,
freq, ht, offset, channel_width_to_string(width),
cf1, cf2, is_dfs);
if (!finished)
return;
if (hapd->csa_in_progress &&
freq == hapd->cs_freq_params.freq) {
hostapd_cleanup_cs_params(hapd);
ieee802_11_set_beacon(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
} else if (is_dfs &&
hostapd_is_dfs_required(hapd->iface) &&
!hostapd_is_dfs_chan_available(hapd->iface) &&
!hapd->iface->cac_started) {
hostapd_disable_iface(hapd->iface);
hostapd_enable_iface(hapd->iface);
}
for (i = 0; i < hapd->iface->num_bss; i++)
hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
#ifdef CONFIG_OCV
if (hapd->conf->ocv) {
struct sta_info *sta;
bool check_sa_query = false;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (wpa_auth_uses_ocv(sta->wpa_sm) &&
!(sta->flags & WLAN_STA_WNM_SLEEP_MODE)) {
sta->post_csa_sa_query = 1;
check_sa_query = true;
}
}
if (check_sa_query) {
wpa_printf(MSG_DEBUG,
"OCV: Check post-CSA SA Query initiation in 15 seconds");
eloop_register_timeout(15, 0,
hostapd_ocv_check_csa_sa_query,
hapd, NULL);
}
}
#endif /* CONFIG_OCV */
#endif /* NEED_AP_MLME */
}
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
const u8 *addr, int reason_code)
{
switch (reason_code) {
case MAX_CLIENT_REACHED:
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
MAC2STR(addr));
break;
case BLOCKED_CLIENT:
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
MAC2STR(addr));
break;
}
}
#ifdef CONFIG_ACS
void hostapd_acs_channel_selected(struct hostapd_data *hapd,
struct acs_selected_channels *acs_res)
{
int ret, i;
int err = 0;
struct hostapd_channel_data *pri_chan;
if (hapd->iconf->channel) {
wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
hapd->iconf->channel);
return;
}
hapd->iface->freq = acs_res->pri_freq;
if (!hapd->iface->current_mode) {
for (i = 0; i < hapd->iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode =
&hapd->iface->hw_features[i];
if (mode->mode == acs_res->hw_mode) {
if (hapd->iface->freq > 0 &&
!hw_get_chan(mode->mode,
hapd->iface->freq,
hapd->iface->hw_features,
hapd->iface->num_hw_features))
continue;
hapd->iface->current_mode = mode;
break;
}
}
if (!hapd->iface->current_mode) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"driver selected to bad hw_mode");
err = 1;
goto out;
}
}
if (!acs_res->pri_freq) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"driver switched to bad channel");
err = 1;
goto out;
}
pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
acs_res->pri_freq, NULL,
hapd->iface->hw_features,
hapd->iface->num_hw_features);
if (!pri_chan) {
wpa_printf(MSG_ERROR,
"ACS: Could not determine primary channel number from pri_freq %u",
acs_res->pri_freq);
err = 1;
goto out;
}
hapd->iconf->channel = pri_chan->chan;
hapd->iconf->acs = 1;
if (acs_res->sec_freq == 0)
hapd->iconf->secondary_channel = 0;
else if (acs_res->sec_freq < acs_res->pri_freq)
hapd->iconf->secondary_channel = -1;
else if (acs_res->sec_freq > acs_res->pri_freq)
hapd->iconf->secondary_channel = 1;
else {
wpa_printf(MSG_ERROR, "Invalid secondary channel!");
err = 1;
goto out;
}
hapd->iconf->edmg_channel = acs_res->edmg_channel;
if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) {
/* set defaults for backwards compatibility */
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_USE_HT);
if (acs_res->ch_width == 40) {
if (is_6ghz_freq(acs_res->pri_freq))
hostapd_set_oper_centr_freq_seg0_idx(
hapd->iconf,
acs_res->vht_seg0_center_ch);
} else if (acs_res->ch_width == 80) {
hostapd_set_oper_centr_freq_seg0_idx(
hapd->iconf, acs_res->vht_seg0_center_ch);
if (acs_res->vht_seg1_center_ch == 0) {
hostapd_set_oper_chwidth(hapd->iconf,
CHANWIDTH_80MHZ);
} else {
hostapd_set_oper_chwidth(hapd->iconf,
CHANWIDTH_80P80MHZ);
hostapd_set_oper_centr_freq_seg1_idx(
hapd->iconf,
acs_res->vht_seg1_center_ch);
}
} else if (acs_res->ch_width == 160) {
hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_160MHZ);
hostapd_set_oper_centr_freq_seg0_idx(
hapd->iconf, acs_res->vht_seg1_center_ch);
}
}
out:
ret = hostapd_acs_completed(hapd->iface, err);
if (ret) {
wpa_printf(MSG_ERROR,
"ACS: Possibly channel configuration is invalid");
}
}
#endif /* CONFIG_ACS */
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)
{
size_t i;
int ret = 0;
if (sa == NULL || ie == NULL)
return -1;
random_add_randomness(sa, ETH_ALEN);
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
sa, da, bssid, ie, ie_len,
ssi_signal) > 0) {
ret = 1;
break;
}
}
return ret;
}
#ifdef HOSTAPD
#ifdef CONFIG_IEEE80211R_AP
static void hostapd_notify_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;
sta = ap_get_sta(hapd, dst);
if (sta == NULL)
return;
hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
sta->flags |= WLAN_STA_AUTH;
hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_FILS
static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta, u16 resp,
struct wpabuf *data, int pub)
{
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 = WLAN_AUTH_FILS_SK;
mlme_authenticate_indication(hapd, sta);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication failed (FILS)");
}
hostapd_sta_auth(hapd, sta->addr, 2, resp,
data ? wpabuf_head(data) : NULL,
data ? wpabuf_len(data) : 0);
wpabuf_free(data);
}
#endif /* CONFIG_FILS */
static void hostapd_notif_auth(struct hostapd_data *hapd,
struct auth_info *rx_auth)
{
struct sta_info *sta;
u16 status = WLAN_STATUS_SUCCESS;
u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
size_t resp_ies_len = 0;
sta = ap_get_sta(hapd, rx_auth->peer);
if (!sta) {
sta = ap_sta_add(hapd, rx_auth->peer);
if (sta == NULL) {
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
#ifdef CONFIG_IEEE80211R_AP
if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
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");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
rx_auth->auth_transaction, rx_auth->ies,
rx_auth->ies_len,
hostapd_notify_auth_ft_finish, hapd);
return;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_FILS
if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
sta->auth_alg = WLAN_AUTH_FILS_SK;
handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
rx_auth->auth_type, rx_auth->auth_transaction,
rx_auth->status_code,
hostapd_notify_auth_fils_finish);
return;
}
#endif /* CONFIG_FILS */
fail:
hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
status, resp_ies, resp_ies_len);
}
#ifndef NEED_AP_MLME
static void hostapd_action_rx(struct hostapd_data *hapd,
struct rx_mgmt *drv_mgmt)
{
struct ieee80211_mgmt *mgmt;
struct sta_info *sta;
size_t plen __maybe_unused;
u16 fc;
u8 *action __maybe_unused;
if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
return;
plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
fc = le_to_host16(mgmt->frame_control);
if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
return; /* handled by the driver */
action = (u8 *) &mgmt->u.action.u;
wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
" da " MACSTR " plen %d",
mgmt->u.action.category, *action,
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
return;
}
#ifdef CONFIG_IEEE80211R_AP
if (mgmt->u.action.category == WLAN_ACTION_FT) {
wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
return;
}
#endif /* CONFIG_IEEE80211R_AP */
if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
return;
}
#ifdef CONFIG_WNM_AP
if (mgmt->u.action.category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
return;
}
#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
return;
}
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
if (plen >= 2 + 4 &&
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 = drv_mgmt->frame + drv_mgmt->frame_len;
hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
drv_mgmt->freq);
return;
}
#endif /* CONFIG_DPP */
}
#endif /* NEED_AP_MLME */
#ifdef NEED_AP_MLME
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
const u8 *bssid)
{
size_t i;
if (bssid == NULL)
return NULL;
if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
return HAPD_BROADCAST;
for (i = 0; i < iface->num_bss; i++) {
if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
return iface->bss[i];
}
return NULL;
}
static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
const u8 *bssid, const u8 *addr,
int wds)
{
hapd = get_hapd_bssid(hapd->iface, bssid);
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
ieee802_11_rx_from_unknown(hapd, addr, wds);
}
static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
{
struct hostapd_iface *iface = hapd->iface;
const struct ieee80211_hdr *hdr;
const u8 *bssid;
struct hostapd_frame_info fi;
int ret;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_mgmt_frame_handling) {
size_t hex_len = 2 * rx_mgmt->frame_len + 1;
char *hex = os_malloc(hex_len);
if (hex) {
wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
rx_mgmt->frame_len);
wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
os_free(hex);
}
return 1;
}
#endif /* CONFIG_TESTING_OPTIONS */
hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
if (bssid == NULL)
return 0;
hapd = get_hapd_bssid(iface, bssid);
if (hapd == NULL) {
u16 fc = le_to_host16(hdr->frame_control);
/*
* Drop frames to unknown BSSIDs except for Beacon frames which
* could be used to update neighbor information.
*/
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
hapd = iface->bss[0];
else
return 0;
}
os_memset(&fi, 0, sizeof(fi));
fi.freq = rx_mgmt->freq;
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
if (hapd == HAPD_BROADCAST) {
size_t i;
ret = 0;
for (i = 0; i < iface->num_bss; i++) {
/* if bss is set, driver will call this function for
* each bss individually. */
if (rx_mgmt->drv_priv &&
(iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
continue;
if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
rx_mgmt->frame_len, &fi) > 0)
ret = 1;
}
} else
ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
&fi);
random_add_randomness(&fi, sizeof(fi));
return ret;
}
static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
size_t len, u16 stype, int ok)
{
struct ieee80211_hdr *hdr;
struct hostapd_data *orig_hapd = hapd;
hdr = (struct ieee80211_hdr *) buf;
hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
if (!hapd)
return;
if (hapd == HAPD_BROADCAST) {
if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
buf[24] != WLAN_ACTION_PUBLIC)
return;
hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
if (!hapd || hapd == HAPD_BROADCAST)
return;
/*
* Allow processing of TX status for a Public Action frame that
* used wildcard BBSID.
*/
}
ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
}
#endif /* NEED_AP_MLME */
static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta)
return 0;
wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
" - adding a new STA", MAC2STR(addr));
sta = ap_sta_add(hapd, addr);
if (sta) {
hostapd_new_assoc_sta(hapd, sta, 0);
} else {
wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
MAC2STR(addr));
return -1;
}
return 0;
}
static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
const u8 *data, size_t data_len)
{
struct hostapd_iface *iface = hapd->iface;
struct sta_info *sta;
size_t j;
for (j = 0; j < iface->num_bss; j++) {
sta = ap_get_sta(iface->bss[j], src);
if (sta && sta->flags & WLAN_STA_ASSOC) {
hapd = iface->bss[j];
break;
}
}
ieee802_1x_receive(hapd, src, data, data_len);
}
#endif /* HOSTAPD */
static struct hostapd_channel_data *
hostapd_get_mode_chan(struct hostapd_hw_modes *mode, unsigned int freq)
{
int i;
struct hostapd_channel_data *chan;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if ((unsigned int) chan->freq == freq)
return chan;
}
return NULL;
}
static struct hostapd_channel_data * hostapd_get_mode_channel(
struct hostapd_iface *iface, unsigned int freq)
{
int i;
struct hostapd_channel_data *chan;
for (i = 0; i < iface->num_hw_features; i++) {
if (hostapd_hw_skip_mode(iface, &iface->hw_features[i]))
continue;
chan = hostapd_get_mode_chan(&iface->hw_features[i], freq);
if (chan)
return chan;
}
return NULL;
}
static void hostapd_update_nf(struct hostapd_iface *iface,
struct hostapd_channel_data *chan,
struct freq_survey *survey)
{
if (!iface->chans_surveyed) {
chan->min_nf = survey->nf;
iface->lowest_nf = survey->nf;
} else {
if (dl_list_empty(&chan->survey_list))
chan->min_nf = survey->nf;
else if (survey->nf < chan->min_nf)
chan->min_nf = survey->nf;
if (survey->nf < iface->lowest_nf)
iface->lowest_nf = survey->nf;
}
}
static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
struct survey_results *survey_res)
{
struct hostapd_channel_data *chan;
struct freq_survey *survey;
u64 divisor, dividend;
survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
list);
if (!survey || !survey->freq)
return;
chan = hostapd_get_mode_channel(iface, survey->freq);
if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
return;
wpa_printf(MSG_DEBUG,
"Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
survey->freq,
(unsigned long int) survey->channel_time,
(unsigned long int) survey->channel_time_busy);
if (survey->channel_time > iface->last_channel_time &&
survey->channel_time > survey->channel_time_busy) {
dividend = survey->channel_time_busy -
iface->last_channel_time_busy;
divisor = survey->channel_time - iface->last_channel_time;
iface->channel_utilization = dividend * 255 / divisor;
wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
iface->channel_utilization);
}
iface->last_channel_time = survey->channel_time;
iface->last_channel_time_busy = survey->channel_time_busy;
}
void hostapd_event_get_survey(struct hostapd_iface *iface,
struct survey_results *survey_results)
{
struct freq_survey *survey, *tmp;
struct hostapd_channel_data *chan;
if (dl_list_empty(&survey_results->survey_list)) {
wpa_printf(MSG_DEBUG, "No survey data received");
return;
}
if (survey_results->freq_filter) {
hostapd_single_channel_get_survey(iface, survey_results);
return;
}
dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
struct freq_survey, list) {
chan = hostapd_get_mode_channel(iface, survey->freq);
if (!chan)
continue;
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
dl_list_del(&survey->list);
dl_list_add_tail(&chan->survey_list, &survey->list);
hostapd_update_nf(iface, chan, survey);
iface->chans_surveyed++;
}
}
#ifdef HOSTAPD
#ifdef NEED_AP_MLME
static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
hapd->conf->iface);
if (hapd->csa_in_progress) {
wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
hapd->conf->iface);
hostapd_switch_channel_fallback(hapd->iface,
&hapd->cs_freq_params);
}
}
static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
struct dfs_event *radar)
{
wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
radar->chan_offset, radar->chan_width,
radar->cf1, radar->cf2);
}
#endif /* NEED_AP_MLME */
static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
int istatus,
const char *ifname,
const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta) {
os_free(sta->ifname_wds);
if (istatus == INTERFACE_ADDED)
sta->ifname_wds = os_strdup(ifname);
else
sta->ifname_wds = NULL;
}
wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
istatus == INTERFACE_ADDED ?
WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
ifname, MAC2STR(addr));
}
#ifdef CONFIG_OWE
static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
const u8 *peer, const u8 *ie,
size_t ie_len)
{
u16 status;
struct sta_info *sta;
struct ieee802_11_elems elems;
if (!hapd || !hapd->wpa_auth) {
wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context");
return -1;
}
if (!peer) {
wpa_printf(MSG_DEBUG, "OWE: Peer unknown");
return -1;
}
if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
status = WLAN_STATUS_AKMP_NOT_VALID;
goto err;
}
if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for "
MACSTR, MAC2STR(peer));
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto err;
}
status = owe_validate_request(hapd, peer, elems.rsn_ie,
elems.rsn_ie_len,
elems.owe_dh, elems.owe_dh_len);
if (status != WLAN_STATUS_SUCCESS)
goto err;
sta = ap_get_sta(hapd, peer);
if (sta) {
ap_sta_no_session_timeout(hapd, sta);
accounting_sta_stop(hapd, sta);
/*
* Make sure that the previously registered inactivity timer
* will not remove the STA immediately.
*/
sta->timeout_next = STA_NULLFUNC;
} else {
sta = ap_sta_add(hapd, peer);
if (!sta) {
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto err;
}
}
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
elems.rsn_ie_len, elems.owe_dh,
elems.owe_dh_len);
if (status != WLAN_STATUS_SUCCESS)
ap_free_sta(hapd, sta);
return 0;
err:
hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
return 0;
}
#endif /* CONFIG_OWE */
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct hostapd_data *hapd = ctx;
#ifndef CONFIG_NO_STDOUT_DEBUG
int level = MSG_DEBUG;
if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
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;
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
level = MSG_EXCESSIVE;
}
wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
event_to_string(event), event);
#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (event) {
case EVENT_MICHAEL_MIC_FAILURE:
michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
break;
case EVENT_SCAN_RESULTS:
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
break;
case EVENT_WPS_BUTTON_PUSHED:
hostapd_wps_button_pushed(hapd, NULL);
break;
#ifdef NEED_AP_MLME
case EVENT_TX_STATUS:
switch (data->tx_status.type) {
case WLAN_FC_TYPE_MGMT:
hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
data->tx_status.data_len,
data->tx_status.stype,
data->tx_status.ack);
break;
case WLAN_FC_TYPE_DATA:
hostapd_tx_status(hapd, data->tx_status.dst,
data->tx_status.data,
data->tx_status.data_len,
data->tx_status.ack);
break;
}
break;
case EVENT_EAPOL_TX_STATUS:
hostapd_eapol_tx_status(hapd, 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:
hostapd_client_poll_ok(hapd, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
break;
#endif /* NEED_AP_MLME */
case EVENT_RX_MGMT:
if (!data->rx_mgmt.frame)
break;
#ifdef NEED_AP_MLME
hostapd_mgmt_rx(hapd, &data->rx_mgmt);
#else /* NEED_AP_MLME */
hostapd_action_rx(hapd, &data->rx_mgmt);
#endif /* NEED_AP_MLME */
break;
case EVENT_RX_PROBE_REQ:
if (data->rx_probe_req.sa == NULL ||
data->rx_probe_req.ie == NULL)
break;
hostapd_probe_req_rx(hapd, 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;
case EVENT_NEW_STA:
hostapd_event_new_sta(hapd, data->new_sta.addr);
break;
case EVENT_EAPOL_RX:
hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
data->eapol_rx.data,
data->eapol_rx.data_len);
break;
case EVENT_ASSOC:
if (!data)
return;
hostapd_notif_assoc(hapd, data->assoc_info.addr,
data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
data->assoc_info.reassoc);
break;
#ifdef CONFIG_OWE
case EVENT_UPDATE_DH:
if (!data)
return;
hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
data->update_dh.ie,
data->update_dh.ie_len);
break;
#endif /* CONFIG_OWE */
case EVENT_DISASSOC:
if (data)
hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
break;
case EVENT_DEAUTH:
if (data)
hostapd_notif_disassoc(hapd, data->deauth_info.addr);
break;
case EVENT_STATION_LOW_ACK:
if (!data)
break;
hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
break;
case EVENT_AUTH:
hostapd_notif_auth(hapd, &data->auth);
break;
case EVENT_CH_SWITCH_STARTED:
case EVENT_CH_SWITCH:
if (!data)
break;
hostapd_event_ch_switch(hapd, 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,
event == EVENT_CH_SWITCH);
break;
case EVENT_CONNECT_FAILED_REASON:
if (!data)
break;
hostapd_event_connect_failed_reason(
hapd, data->connect_failed_reason.addr,
data->connect_failed_reason.code);
break;
case EVENT_SURVEY:
hostapd_event_get_survey(hapd->iface, &data->survey_results);
break;
#ifdef NEED_AP_MLME
case EVENT_INTERFACE_UNAVAILABLE:
hostapd_event_iface_unavailable(hapd);
break;
case EVENT_DFS_RADAR_DETECTED:
if (!data)
break;
hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
break;
case EVENT_DFS_PRE_CAC_EXPIRED:
if (!data)
break;
hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_FINISHED:
if (!data)
break;
hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_ABORTED:
if (!data)
break;
hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
break;
case EVENT_DFS_NOP_FINISHED:
if (!data)
break;
hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
break;
case EVENT_CHANNEL_LIST_CHANGED:
/* channel list changed (regulatory?), update channel list */
/* TODO: check this. hostapd_get_hw_features() initializes
* too much stuff. */
/* hostapd_get_hw_features(hapd->iface); */
hostapd_channel_list_updated(
hapd->iface, data->channel_list_changed.initiator);
break;
case EVENT_DFS_CAC_STARTED:
if (!data)
break;
hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
break;
#endif /* NEED_AP_MLME */
case EVENT_INTERFACE_ENABLED:
wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
if (hapd->disabled && hapd->started) {
hapd->disabled = 0;
/*
* Try to re-enable interface if the driver stopped it
* when the interface got disabled.
*/
if (hapd->wpa_auth)
wpa_auth_reconfig_group_keys(hapd->wpa_auth);
else
hostapd_reconfig_encryption(hapd);
hapd->reenable_beacon = 1;
ieee802_11_set_beacon(hapd);
#ifdef NEED_AP_MLME
} else if (hapd->disabled && hapd->iface->cac_started) {
wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
hostapd_handle_dfs(hapd->iface);
#endif /* NEED_AP_MLME */
}
break;
case EVENT_INTERFACE_DISABLED:
hostapd_free_stas(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
hapd->disabled = 1;
break;
#ifdef CONFIG_ACS
case EVENT_ACS_CHANNEL_SELECTED:
hostapd_acs_channel_selected(hapd,
&data->acs_selected_channels);
break;
#endif /* CONFIG_ACS */
case EVENT_STATION_OPMODE_CHANGED:
hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
data->sta_opmode.smps_mode,
data->sta_opmode.chan_width,
data->sta_opmode.rx_nss);
break;
case EVENT_WDS_STA_INTERFACE_STATUS:
hostapd_event_wds_sta_interface_status(
hapd, data->wds_sta_interface.istatus,
data->wds_sta_interface.ifname,
data->wds_sta_interface.sta_addr);
break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
}
}
void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct hapd_interfaces *interfaces = ctx;
struct hostapd_data *hapd;
if (event != EVENT_INTERFACE_STATUS)
return;
hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
if (hapd && hapd->driver && hapd->driver->get_ifindex &&
hapd->drv_priv) {
unsigned int ifindex;
ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
if (ifindex != data->interface_status.ifindex) {
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"interface status ifindex %d mismatch (%d)",
ifindex, data->interface_status.ifindex);
return;
}
}
if (hapd)
wpa_supplicant_event(hapd, event, data);
}
#endif /* HOSTAPD */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7bb0f097669b..e9aae6dcf2f5 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,3732 +1,3734 @@
/*
* hostapd / Initialization and configuration
* Copyright (c) 2002-2021, 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_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/crc32.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/hw_features_common.h"
#include "radius/radius_client.h"
#include "radius/radius_das.h"
#include "eap_server/tncs.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "fst/fst.h"
#include "hostapd.h"
#include "authsrv.h"
#include "sta_info.h"
#include "accounting.h"
#include "ap_list.h"
#include "beacon.h"
#include "ieee802_1x.h"
#include "ieee802_11_auth.h"
#include "vlan_init.h"
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "p2p_hostapd.h"
#include "gas_serv.h"
#include "dfs.h"
#include "ieee802_11.h"
#include "bss_load.h"
#include "x_snoop.h"
#include "dhcp_snoop.h"
#include "ndisc_snoop.h"
#include "neighbor_db.h"
#include "rrm.h"
#include "fils_hlp.h"
#include "acs.h"
#include "hs20.h"
#include "airtime_policy.h"
#include "wpa_auth_kay.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
#ifdef CONFIG_WEP
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
#endif /* CONFIG_WEP */
static int setup_interface2(struct hostapd_iface *iface);
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
void *timeout_ctx);
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
int (*cb)(struct hostapd_iface *iface,
void *ctx), void *ctx)
{
size_t i;
int ret;
for (i = 0; i < interfaces->count; i++) {
if (!interfaces->iface[i])
continue;
ret = cb(interfaces->iface[i], ctx);
if (ret)
return ret;
}
return 0;
}
void hostapd_reconfig_encryption(struct hostapd_data *hapd)
{
if (hapd->wpa_auth)
return;
hostapd_set_privacy(hapd, 0);
#ifdef CONFIG_WEP
hostapd_setup_encryption(hapd->conf->iface, hapd);
#endif /* CONFIG_WEP */
}
static void hostapd_reload_bss(struct hostapd_data *hapd)
{
struct hostapd_ssid *ssid;
if (!hapd->started)
return;
if (hapd->conf->wmm_enabled < 0)
- hapd->conf->wmm_enabled = hapd->iconf->ieee80211n;
+ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
#ifndef CONFIG_NO_RADIUS
radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
ssid = &hapd->conf->ssid;
if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
/*
* Force PSK to be derived again since SSID or passphrase may
* have changed.
*/
hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
}
if (hostapd_setup_wpa_psk(hapd->conf)) {
wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
"after reloading configuration");
}
if (hapd->conf->ieee802_1x || hapd->conf->wpa)
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
else
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
if (hapd->wpa_auth)
wpa_init_keys(hapd->wpa_auth);
} else if (hapd->conf->wpa) {
const u8 *wpa_ie;
size_t wpa_ie_len;
hostapd_reconfig_wpa(hapd);
wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
"the kernel driver.");
} else if (hapd->wpa_auth) {
wpa_deinit(hapd->wpa_auth);
hapd->wpa_auth = NULL;
hostapd_set_privacy(hapd, 0);
#ifdef CONFIG_WEP
hostapd_setup_encryption(hapd->conf->iface, hapd);
#endif /* CONFIG_WEP */
hostapd_set_generic_elem(hapd, (u8 *) "", 0);
}
ieee802_11_set_beacon(hapd);
hostapd_update_wps(hapd);
if (hapd->conf->ssid.ssid_set &&
hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
hapd->conf->ssid.ssid_len)) {
wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
/* try to continue */
}
wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
}
static void hostapd_clear_old(struct hostapd_iface *iface)
{
size_t j;
/*
* Deauthenticate all stations since the new configuration may not
* allow them to use the BSS anymore.
*/
for (j = 0; j < iface->num_bss; j++) {
hostapd_flush_old_stations(iface->bss[j],
WLAN_REASON_PREV_AUTH_NOT_VALID);
#ifdef CONFIG_WEP
hostapd_broadcast_wep_clear(iface->bss[j]);
#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_RADIUS
/* TODO: update dynamic data based on changed configuration
* items (e.g., open/close sockets, etc.) */
radius_client_flush(iface->bss[j]->radius, 0);
#endif /* CONFIG_NO_RADIUS */
}
}
static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
struct hostapd_config *oldconf)
{
size_t i;
if (newconf->num_bss != oldconf->num_bss)
return 1;
for (i = 0; i < newconf->num_bss; i++) {
if (os_strcmp(newconf->bss[i]->iface,
oldconf->bss[i]->iface) != 0)
return 1;
}
return 0;
}
int hostapd_reload_config(struct hostapd_iface *iface)
{
struct hapd_interfaces *interfaces = iface->interfaces;
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_config *newconf, *oldconf;
size_t j;
if (iface->config_fname == NULL) {
/* Only in-memory config in use - assume it has been updated */
hostapd_clear_old(iface);
for (j = 0; j < iface->num_bss; j++)
hostapd_reload_bss(iface->bss[j]);
return 0;
}
if (iface->interfaces == NULL ||
iface->interfaces->config_read_cb == NULL)
return -1;
newconf = iface->interfaces->config_read_cb(iface->config_fname);
if (newconf == NULL)
return -1;
hostapd_clear_old(iface);
oldconf = hapd->iconf;
if (hostapd_iface_conf_changed(newconf, oldconf)) {
char *fname;
int res;
wpa_printf(MSG_DEBUG,
"Configuration changes include interface/BSS modification - force full disable+enable sequence");
fname = os_strdup(iface->config_fname);
if (!fname) {
hostapd_config_free(newconf);
return -1;
}
hostapd_remove_iface(interfaces, hapd->conf->iface);
iface = hostapd_init(interfaces, fname);
os_free(fname);
hostapd_config_free(newconf);
if (!iface) {
wpa_printf(MSG_ERROR,
"Failed to initialize interface on config reload");
return -1;
}
iface->interfaces = interfaces;
interfaces->iface[interfaces->count] = iface;
interfaces->count++;
res = hostapd_enable_iface(iface);
if (res < 0)
wpa_printf(MSG_ERROR,
"Failed to enable interface on config reload");
return res;
}
iface->conf = newconf;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
hapd->iconf = newconf;
hapd->iconf->channel = oldconf->channel;
hapd->iconf->acs = oldconf->acs;
hapd->iconf->secondary_channel = oldconf->secondary_channel;
hapd->iconf->ieee80211n = oldconf->ieee80211n;
hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
hapd->iconf->ht_capab = oldconf->ht_capab;
hapd->iconf->vht_capab = oldconf->vht_capab;
hostapd_set_oper_chwidth(hapd->iconf,
hostapd_get_oper_chwidth(oldconf));
hostapd_set_oper_centr_freq_seg0_idx(
hapd->iconf,
hostapd_get_oper_centr_freq_seg0_idx(oldconf));
hostapd_set_oper_centr_freq_seg1_idx(
hapd->iconf,
hostapd_get_oper_centr_freq_seg1_idx(oldconf));
hapd->conf = newconf->bss[j];
hostapd_reload_bss(hapd);
}
hostapd_config_free(oldconf);
return 0;
}
#ifdef CONFIG_WEP
static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
const char *ifname)
{
int i;
if (!ifname || !hapd->drv_priv)
return;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0,
0, NULL, 0, NULL, 0, KEY_FLAG_GROUP)) {
wpa_printf(MSG_DEBUG, "Failed to clear default "
"encryption keys (ifname=%s keyidx=%d)",
ifname, i);
}
}
if (hapd->conf->ieee80211w) {
for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
NULL, i, 0, 0, NULL,
0, NULL, 0, KEY_FLAG_GROUP)) {
wpa_printf(MSG_DEBUG, "Failed to clear "
"default mgmt encryption keys "
"(ifname=%s keyidx=%d)", ifname, i);
}
}
}
}
static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
{
hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
return 0;
}
static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
{
int errors = 0, idx;
struct hostapd_ssid *ssid = &hapd->conf->ssid;
idx = ssid->wep.idx;
if (ssid->wep.default_len && ssid->wep.key[idx] &&
hostapd_drv_set_key(hapd->conf->iface,
hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, 0,
1, NULL, 0, ssid->wep.key[idx],
ssid->wep.len[idx],
KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
errors++;
}
return errors;
}
#endif /* CONFIG_WEP */
void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
os_free(hapd->probereq_cb);
hapd->probereq_cb = NULL;
hapd->num_probereq_cb = 0;
#ifdef CONFIG_P2P
wpabuf_free(hapd->p2p_beacon_ie);
hapd->p2p_beacon_ie = NULL;
wpabuf_free(hapd->p2p_probe_resp_ie);
hapd->p2p_probe_resp_ie = NULL;
#endif /* CONFIG_P2P */
if (!hapd->started) {
wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
__func__, hapd->conf ? hapd->conf->iface : "N/A");
return;
}
hapd->started = 0;
hapd->beacon_set_done = 0;
wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
accounting_deinit(hapd);
hostapd_deinit_wpa(hapd);
vlan_deinit(hapd);
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
radius_client_deinit(hapd->radius);
hapd->radius = NULL;
radius_das_deinit(hapd->radius_das);
hapd->radius_das = NULL;
#endif /* CONFIG_NO_RADIUS */
hostapd_deinit_wps(hapd);
ieee802_1x_dealloc_kay_sm_hapd(hapd);
#ifdef CONFIG_DPP
hostapd_dpp_deinit(hapd);
gas_query_ap_deinit(hapd->gas);
hapd->gas = NULL;
#endif /* CONFIG_DPP */
authsrv_deinit(hapd);
if (hapd->interface_added) {
hapd->interface_added = 0;
if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
wpa_printf(MSG_WARNING,
"Failed to remove BSS interface %s",
hapd->conf->iface);
hapd->interface_added = 1;
} else {
/*
* Since this was a dynamically added interface, the
* driver wrapper may have removed its internal instance
* and hapd->drv_priv is not valid anymore.
*/
hapd->drv_priv = NULL;
}
}
wpabuf_free(hapd->time_adv);
hapd->time_adv = NULL;
#ifdef CONFIG_INTERWORKING
gas_serv_deinit(hapd);
#endif /* CONFIG_INTERWORKING */
bss_load_update_deinit(hapd);
ndisc_snoop_deinit(hapd);
dhcp_snoop_deinit(hapd);
x_snoop_deinit(hapd);
#ifdef CONFIG_SQLITE
bin_clear_free(hapd->tmp_eap_user.identity,
hapd->tmp_eap_user.identity_len);
bin_clear_free(hapd->tmp_eap_user.password,
hapd->tmp_eap_user.password_len);
os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
#endif /* CONFIG_SQLITE */
#ifdef CONFIG_MESH
wpabuf_free(hapd->mesh_pending_auth);
hapd->mesh_pending_auth = NULL;
/* handling setup failure is already done */
hapd->setup_complete_cb = NULL;
#endif /* CONFIG_MESH */
hostapd_clean_rrm(hapd);
fils_hlp_deinit(hapd);
#ifdef CONFIG_OCV
eloop_cancel_timeout(hostapd_ocv_check_csa_sa_query, hapd, NULL);
#endif /* CONFIG_OCV */
#ifdef CONFIG_SAE
{
struct hostapd_sae_commit_queue *q;
while ((q = dl_list_first(&hapd->sae_commit_queue,
struct hostapd_sae_commit_queue,
list))) {
dl_list_del(&q->list);
os_free(q);
}
}
eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
#endif /* CONFIG_SAE */
}
/**
* hostapd_cleanup - Per-BSS cleanup (deinitialization)
* @hapd: Pointer to BSS data
*
* This function is used to free all per-BSS data structures and resources.
* Most of the modules that are initialized in hostapd_setup_bss() are
* deinitialized here.
*/
static void hostapd_cleanup(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
hapd->conf ? hapd->conf->iface : "N/A");
if (hapd->iface->interfaces &&
hapd->iface->interfaces->ctrl_iface_deinit) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
hapd->iface->interfaces->ctrl_iface_deinit(hapd);
}
hostapd_free_hapd_data(hapd);
}
static void sta_track_deinit(struct hostapd_iface *iface)
{
struct hostapd_sta_info *info;
if (!iface->num_sta_seen)
return;
while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
list))) {
dl_list_del(&info->list);
iface->num_sta_seen--;
sta_track_del(info);
}
}
void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
#ifdef NEED_AP_MLME
hostapd_stop_setup_timers(iface);
#endif /* NEED_AP_MLME */
if (iface->current_mode)
acs_cleanup(iface);
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = NULL;
iface->current_mode = NULL;
os_free(iface->current_rates);
iface->current_rates = NULL;
os_free(iface->basic_rates);
iface->basic_rates = NULL;
ap_list_deinit(iface);
sta_track_deinit(iface);
airtime_policy_update_deinit(iface);
}
/**
* hostapd_cleanup_iface - Complete per-interface cleanup
* @iface: Pointer to interface data
*
* This function is called after per-BSS data structures are deinitialized
* with hostapd_cleanup().
*/
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
NULL);
hostapd_cleanup_iface_partial(iface);
hostapd_config_free(iface->conf);
iface->conf = NULL;
os_free(iface->config_fname);
os_free(iface->bss);
wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
os_free(iface);
}
#ifdef CONFIG_WEP
static void hostapd_clear_wep(struct hostapd_data *hapd)
{
if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
}
}
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
{
int i;
hostapd_broadcast_wep_set(hapd);
if (hapd->conf->ssid.wep.default_len) {
hostapd_set_privacy(hapd, 1);
return 0;
}
/*
* When IEEE 802.1X is not enabled, the driver may need to know how to
* set authentication algorithms for static WEP.
*/
hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
for (i = 0; i < 4; i++) {
if (hapd->conf->ssid.wep.key[i] &&
hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, 0,
i == hapd->conf->ssid.wep.idx, NULL, 0,
hapd->conf->ssid.wep.key[i],
hapd->conf->ssid.wep.len[i],
i == hapd->conf->ssid.wep.idx ?
KEY_FLAG_GROUP_RX_TX_DEFAULT :
KEY_FLAG_GROUP_RX_TX)) {
wpa_printf(MSG_WARNING, "Could not set WEP "
"encryption.");
return -1;
}
if (hapd->conf->ssid.wep.key[i] &&
i == hapd->conf->ssid.wep.idx)
hostapd_set_privacy(hapd, 1);
}
return 0;
}
#endif /* CONFIG_WEP */
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
{
int ret = 0;
u8 addr[ETH_ALEN];
if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
return 0;
if (!hapd->iface->driver_ap_teardown) {
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"Flushing old station entries");
if (hostapd_flush(hapd)) {
wpa_msg(hapd->msg_ctx, MSG_WARNING,
"Could not connect to kernel driver");
ret = -1;
}
}
if (hapd->conf && hapd->conf->broadcast_deauth) {
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"Deauthenticate all stations");
os_memset(addr, 0xff, ETH_ALEN);
hostapd_drv_sta_deauth(hapd, addr, reason);
}
hostapd_free_stas(hapd);
return ret;
}
void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
{
hostapd_free_stas(hapd);
hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
#ifdef CONFIG_WEP
hostapd_clear_wep(hapd);
#endif /* CONFIG_WEP */
}
/**
* hostapd_validate_bssid_configuration - Validate BSSID configuration
* @iface: Pointer to interface data
* Returns: 0 on success, -1 on failure
*
* This function is used to validate that the configured BSSIDs are valid.
*/
static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
{
u8 mask[ETH_ALEN] = { 0 };
struct hostapd_data *hapd = iface->bss[0];
unsigned int i = iface->conf->num_bss, bits = 0, j;
int auto_addr = 0;
if (hostapd_drv_none(hapd))
return 0;
if (iface->conf->use_driver_iface_addr)
return 0;
/* Generate BSSID mask that is large enough to cover the BSSIDs. */
/* Determine the bits necessary to cover the number of BSSIDs. */
for (i--; i; i >>= 1)
bits++;
/* Determine the bits necessary to any configured BSSIDs,
if they are higher than the number of BSSIDs. */
for (j = 0; j < iface->conf->num_bss; j++) {
if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) {
if (j)
auto_addr++;
continue;
}
for (i = 0; i < ETH_ALEN; i++) {
mask[i] |=
iface->conf->bss[j]->bssid[i] ^
hapd->own_addr[i];
}
}
if (!auto_addr)
goto skip_mask_ext;
for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
;
j = 0;
if (i < ETH_ALEN) {
j = (5 - i) * 8;
while (mask[i] != 0) {
mask[i] >>= 1;
j++;
}
}
if (bits < j)
bits = j;
if (bits > 40) {
wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
bits);
return -1;
}
os_memset(mask, 0xff, ETH_ALEN);
j = bits / 8;
for (i = 5; i > 5 - j; i--)
mask[i] = 0;
j = bits % 8;
while (j) {
j--;
mask[i] <<= 1;
}
skip_mask_ext:
wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
(unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
if (!auto_addr)
return 0;
for (i = 0; i < ETH_ALEN; i++) {
if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
" for start address " MACSTR ".",
MAC2STR(mask), MAC2STR(hapd->own_addr));
wpa_printf(MSG_ERROR, "Start address must be the "
"first address in the block (i.e., addr "
"AND mask == addr).");
return -1;
}
}
return 0;
}
static int mac_in_conf(struct hostapd_config *conf, const void *a)
{
size_t i;
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
return 1;
}
}
return 0;
}
#ifndef CONFIG_NO_RADIUS
static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
struct radius_das_attrs *attr)
{
if (attr->nas_identifier &&
(!hapd->conf->nas_identifier ||
os_strlen(hapd->conf->nas_identifier) !=
attr->nas_identifier_len ||
os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
attr->nas_identifier_len) != 0)) {
wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
return 1;
}
if (attr->nas_ip_addr &&
(hapd->conf->own_ip_addr.af != AF_INET ||
os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
0)) {
wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
return 1;
}
#ifdef CONFIG_IPV6
if (attr->nas_ipv6_addr &&
(hapd->conf->own_ip_addr.af != AF_INET6 ||
os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
!= 0)) {
wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
return 1;
}
#endif /* CONFIG_IPV6 */
return 0;
}
static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
struct radius_das_attrs *attr,
int *multi)
{
struct sta_info *selected, *sta;
char buf[128];
int num_attr = 0;
int count;
*multi = 0;
for (sta = hapd->sta_list; sta; sta = sta->next)
sta->radius_das_match = 1;
if (attr->sta_addr) {
num_attr++;
sta = ap_get_sta(hapd, attr->sta_addr);
if (!sta) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No Calling-Station-Id match");
return NULL;
}
selected = sta;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta != selected)
sta->radius_das_match = 0;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
}
if (attr->acct_session_id) {
num_attr++;
if (attr->acct_session_id_len != 16) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Acct-Session-Id cannot match");
return NULL;
}
count = 0;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!sta->radius_das_match)
continue;
os_snprintf(buf, sizeof(buf), "%016llX",
(unsigned long long) sta->acct_session_id);
if (os_memcmp(attr->acct_session_id, buf, 16) != 0)
sta->radius_das_match = 0;
else
count++;
}
if (count == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No matches remaining after Acct-Session-Id check");
return NULL;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
}
if (attr->acct_multi_session_id) {
num_attr++;
if (attr->acct_multi_session_id_len != 16) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Acct-Multi-Session-Id cannot match");
return NULL;
}
count = 0;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!sta->radius_das_match)
continue;
if (!sta->eapol_sm ||
!sta->eapol_sm->acct_multi_session_id) {
sta->radius_das_match = 0;
continue;
}
os_snprintf(buf, sizeof(buf), "%016llX",
(unsigned long long)
sta->eapol_sm->acct_multi_session_id);
if (os_memcmp(attr->acct_multi_session_id, buf, 16) !=
0)
sta->radius_das_match = 0;
else
count++;
}
if (count == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
return NULL;
}
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Acct-Multi-Session-Id match");
}
if (attr->cui) {
num_attr++;
count = 0;
for (sta = hapd->sta_list; sta; sta = sta->next) {
struct wpabuf *cui;
if (!sta->radius_das_match)
continue;
cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
if (!cui || wpabuf_len(cui) != attr->cui_len ||
os_memcmp(wpabuf_head(cui), attr->cui,
attr->cui_len) != 0)
sta->radius_das_match = 0;
else
count++;
}
if (count == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
return NULL;
}
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Chargeable-User-Identity match");
}
if (attr->user_name) {
num_attr++;
count = 0;
for (sta = hapd->sta_list; sta; sta = sta->next) {
u8 *identity;
size_t identity_len;
if (!sta->radius_das_match)
continue;
identity = ieee802_1x_get_identity(sta->eapol_sm,
&identity_len);
if (!identity ||
identity_len != attr->user_name_len ||
os_memcmp(identity, attr->user_name, identity_len)
!= 0)
sta->radius_das_match = 0;
else
count++;
}
if (count == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No matches remaining after User-Name check");
return NULL;
}
wpa_printf(MSG_DEBUG,
"RADIUS DAS: User-Name match");
}
if (num_attr == 0) {
/*
* In theory, we could match all current associations, but it
* seems safer to just reject requests that do not include any
* session identification attributes.
*/
wpa_printf(MSG_DEBUG,
"RADIUS DAS: No session identification attributes included");
return NULL;
}
selected = NULL;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->radius_das_match) {
if (selected) {
*multi = 1;
return NULL;
}
selected = sta;
}
}
return selected;
}
static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
struct radius_das_attrs *attr)
{
if (!hapd->wpa_auth)
return -1;
return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
}
static enum radius_das_res
hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
int multi;
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
sta = hostapd_das_find_sta(hapd, attr, &multi);
if (sta == NULL) {
if (multi) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Multiple sessions match - not supported");
return RADIUS_DAS_MULTI_SESSION_MATCH;
}
if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: PMKSA cache entry matched");
return RADIUS_DAS_SUCCESS;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
return RADIUS_DAS_SESSION_NOT_FOUND;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
" - disconnecting", MAC2STR(sta->addr));
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
return RADIUS_DAS_SUCCESS;
}
#ifdef CONFIG_HS20
static enum radius_das_res
hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
int multi;
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
sta = hostapd_das_find_sta(hapd, attr, &multi);
if (!sta) {
if (multi) {
wpa_printf(MSG_DEBUG,
"RADIUS DAS: Multiple sessions match - not supported");
return RADIUS_DAS_MULTI_SESSION_MATCH;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
return RADIUS_DAS_SESSION_NOT_FOUND;
}
wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
" - CoA", MAC2STR(sta->addr));
if (attr->hs20_t_c_filtering) {
if (attr->hs20_t_c_filtering[0] & BIT(0)) {
wpa_printf(MSG_DEBUG,
"HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
return RADIUS_DAS_COA_FAILED;
}
hs20_t_c_filtering(hapd, sta, 0);
}
return RADIUS_DAS_SUCCESS;
}
#else /* CONFIG_HS20 */
#define hostapd_das_coa NULL
#endif /* CONFIG_HS20 */
#ifdef CONFIG_SQLITE
static int db_table_exists(sqlite3 *db, const char *name)
{
char cmd[128];
os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
}
static int db_table_create_radius_attributes(sqlite3 *db)
{
char *err = NULL;
const char *sql =
"CREATE TABLE radius_attributes("
" id INTEGER PRIMARY KEY,"
" sta TEXT,"
" reqtype TEXT,"
" attr TEXT"
");"
"CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);";
wpa_printf(MSG_DEBUG,
"Adding database table for RADIUS attribute information");
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
wpa_printf(MSG_ERROR, "SQLite error: %s", err);
sqlite3_free(err);
return -1;
}
return 0;
}
#endif /* CONFIG_SQLITE */
#endif /* CONFIG_NO_RADIUS */
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
* @first: Whether this BSS is the first BSS of an interface; -1 = not first,
* but interface may exist
*
* This function is used to initialize all per-BSS data structures and
* resources. This gets called in a loop for each BSS when an interface is
* initialized. Most of the modules that are initialized here will be
* deinitialized in hostapd_cleanup().
*/
static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
{
struct hostapd_bss_config *conf = hapd->conf;
u8 ssid[SSID_MAX_LEN + 1];
int ssid_len, set_ssid;
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
int flush_old_stations = 1;
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
__func__, hapd, conf->iface, first);
#ifdef EAP_SERVER_TNC
if (conf->tnc && tncs_global_init() < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
return -1;
}
#endif /* EAP_SERVER_TNC */
if (hapd->started) {
wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
__func__, conf->iface);
return -1;
}
hapd->started = 1;
if (!first || first == -1) {
u8 *addr = hapd->own_addr;
if (!is_zero_ether_addr(conf->bssid)) {
/* Allocate the configured BSSID. */
os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
if (hostapd_mac_comp(hapd->own_addr,
hapd->iface->bss[0]->own_addr) ==
0) {
wpa_printf(MSG_ERROR, "BSS '%s' may not have "
"BSSID set to the MAC address of "
"the radio", conf->iface);
return -1;
}
} else if (hapd->iconf->use_driver_iface_addr) {
addr = NULL;
} else {
/* Allocate the next available BSSID. */
do {
inc_byte_array(hapd->own_addr, ETH_ALEN);
} while (mac_in_conf(hapd->iconf, hapd->own_addr));
}
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
conf->iface, addr, hapd,
&hapd->drv_priv, force_ifname, if_addr,
conf->bridge[0] ? conf->bridge : NULL,
first == -1)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
hapd->interface_added = 0;
return -1;
}
if (!addr)
os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
}
if (conf->wmm_enabled < 0)
- conf->wmm_enabled = hapd->iconf->ieee80211n;
+ conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
#ifdef CONFIG_IEEE80211R_AP
if (is_zero_ether_addr(conf->r1_key_holder))
os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_MESH
if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
flush_old_stations = 0;
#endif /* CONFIG_MESH */
if (flush_old_stations)
hostapd_flush(hapd);
hostapd_set_privacy(hapd, 0);
#ifdef CONFIG_WEP
if (!hostapd_drv_nl80211(hapd))
hostapd_broadcast_wep_clear(hapd);
if (hostapd_setup_encryption(conf->iface, hapd))
return -1;
#endif /* CONFIG_WEP */
/*
* Fetch the SSID from the system and use it or,
* if one was specified in the config file, verify they
* match.
*/
ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
if (ssid_len < 0) {
wpa_printf(MSG_ERROR, "Could not read SSID from system");
return -1;
}
if (conf->ssid.ssid_set) {
/*
* If SSID is specified in the config file and it differs
* from what is being used then force installation of the
* new SSID.
*/
set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
} else {
/*
* No SSID in the config file; just use the one we got
* from the system.
*/
set_ssid = 0;
conf->ssid.ssid_len = ssid_len;
os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
}
/*
* Short SSID calculation is identical to FCS and it is defined in
* IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
*/
conf->ssid.short_ssid = crc32(conf->ssid.ssid, conf->ssid.ssid_len);
if (!hostapd_drv_none(hapd)) {
wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
" and ssid \"%s\"",
conf->iface, MAC2STR(hapd->own_addr),
wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
}
if (hostapd_setup_wpa_psk(conf)) {
wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
return -1;
}
/* Set SSID for the kernel driver (to be used in beacon and probe
* response frames) */
if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
conf->ssid.ssid_len)) {
wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
return -1;
}
if (wpa_debug_level <= MSG_MSGDUMP)
conf->radius->msg_dumps = 1;
#ifndef CONFIG_NO_RADIUS
#ifdef CONFIG_SQLITE
if (conf->radius_req_attr_sqlite) {
if (sqlite3_open(conf->radius_req_attr_sqlite,
&hapd->rad_attr_db)) {
wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'",
conf->radius_req_attr_sqlite);
return -1;
}
wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s",
conf->radius_req_attr_sqlite);
if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") &&
db_table_create_radius_attributes(hapd->rad_attr_db) < 0)
return -1;
}
#endif /* CONFIG_SQLITE */
hapd->radius = radius_client_init(hapd, conf->radius);
if (hapd->radius == NULL) {
wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
return -1;
}
if (conf->radius_das_port) {
struct radius_das_conf das_conf;
os_memset(&das_conf, 0, sizeof(das_conf));
das_conf.port = conf->radius_das_port;
das_conf.shared_secret = conf->radius_das_shared_secret;
das_conf.shared_secret_len =
conf->radius_das_shared_secret_len;
das_conf.client_addr = &conf->radius_das_client_addr;
das_conf.time_window = conf->radius_das_time_window;
das_conf.require_event_timestamp =
conf->radius_das_require_event_timestamp;
das_conf.require_message_authenticator =
conf->radius_das_require_message_authenticator;
das_conf.ctx = hapd;
das_conf.disconnect = hostapd_das_disconnect;
das_conf.coa = hostapd_das_coa;
hapd->radius_das = radius_das_init(&das_conf);
if (hapd->radius_das == NULL) {
wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
"failed.");
return -1;
}
}
#endif /* CONFIG_NO_RADIUS */
if (hostapd_acl_init(hapd)) {
wpa_printf(MSG_ERROR, "ACL initialization failed.");
return -1;
}
if (hostapd_init_wps(hapd, conf))
return -1;
#ifdef CONFIG_DPP
hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
if (!hapd->gas)
return -1;
if (hostapd_dpp_init(hapd))
return -1;
#endif /* CONFIG_DPP */
if (authsrv_init(hapd) < 0)
return -1;
if (ieee802_1x_init(hapd)) {
wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
return -1;
}
if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
return -1;
if (accounting_init(hapd)) {
wpa_printf(MSG_ERROR, "Accounting initialization failed.");
return -1;
}
#ifdef CONFIG_INTERWORKING
if (gas_serv_init(hapd)) {
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
if (conf->qos_map_set_len &&
hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
conf->qos_map_set_len)) {
wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
return -1;
}
#endif /* CONFIG_INTERWORKING */
if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
wpa_printf(MSG_ERROR, "BSS Load initialization failed");
return -1;
}
if (conf->proxy_arp) {
if (x_snoop_init(hapd)) {
wpa_printf(MSG_ERROR,
"Generic snooping infrastructure initialization failed");
return -1;
}
if (dhcp_snoop_init(hapd)) {
wpa_printf(MSG_ERROR,
"DHCP snooping initialization failed");
return -1;
}
if (ndisc_snoop_init(hapd)) {
wpa_printf(MSG_ERROR,
"Neighbor Discovery snooping initialization failed");
return -1;
}
}
if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
wpa_printf(MSG_ERROR, "VLAN initialization failed.");
return -1;
}
if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
return -1;
if (flush_old_stations && !conf->start_disabled &&
conf->broadcast_deauth) {
u8 addr[ETH_ALEN];
/* Should any previously associated STA not have noticed that
* the AP had stopped and restarted, send one more
* deauthentication notification now that the AP is ready to
* operate. */
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"Deauthenticate all stations at BSS start");
os_memset(addr, 0xff, ETH_ALEN);
hostapd_drv_sta_deauth(hapd, addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
}
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
if (hapd->driver && hapd->driver->set_operstate)
hapd->driver->set_operstate(hapd->drv_priv, 1);
return 0;
}
static void hostapd_tx_queue_params(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
int i;
struct hostapd_tx_queue_params *p;
#ifdef CONFIG_MESH
if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
return;
#endif /* CONFIG_MESH */
for (i = 0; i < NUM_TX_QUEUES; i++) {
p = &iface->conf->tx_queue[i];
if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
p->cwmax, p->burst)) {
wpa_printf(MSG_DEBUG, "Failed to set TX queue "
"parameters for queue %d.", i);
/* Continue anyway */
}
}
}
static int hostapd_set_acl_list(struct hostapd_data *hapd,
struct mac_acl_entry *mac_acl,
int n_entries, u8 accept_acl)
{
struct hostapd_acl_params *acl_params;
int i, err;
acl_params = os_zalloc(sizeof(*acl_params) +
(n_entries * sizeof(acl_params->mac_acl[0])));
if (!acl_params)
return -ENOMEM;
for (i = 0; i < n_entries; i++)
os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
ETH_ALEN);
acl_params->acl_policy = accept_acl;
acl_params->num_mac_acl = n_entries;
err = hostapd_drv_set_acl(hapd, acl_params);
os_free(acl_params);
return err;
}
static void hostapd_set_acl(struct hostapd_data *hapd)
{
struct hostapd_config *conf = hapd->iconf;
int err;
u8 accept_acl;
if (hapd->iface->drv_max_acl_mac_addrs == 0)
return;
if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
accept_acl = 1;
err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
conf->bss[0]->num_accept_mac,
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set accept acl");
return;
}
} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
accept_acl = 0;
err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
conf->bss[0]->num_deny_mac,
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set deny acl");
return;
}
}
}
static int start_ctrl_iface_bss(struct hostapd_data *hapd)
{
if (!hapd->iface->interfaces ||
!hapd->iface->interfaces->ctrl_iface_init)
return 0;
if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
wpa_printf(MSG_ERROR,
"Failed to setup control interface for %s",
hapd->conf->iface);
return -1;
}
return 0;
}
static int start_ctrl_iface(struct hostapd_iface *iface)
{
size_t i;
if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
return 0;
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
if (iface->interfaces->ctrl_iface_init(hapd)) {
wpa_printf(MSG_ERROR,
"Failed to setup control interface for %s",
hapd->conf->iface);
return -1;
}
}
return 0;
}
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
if (!iface->wait_channel_update) {
wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
return;
}
/*
* It is possible that the existing channel list is acceptable, so try
* to proceed.
*/
wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
setup_interface2(iface);
}
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
{
if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
return;
wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
setup_interface2(iface);
}
static int setup_interface(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
size_t i;
/*
* It is possible that setup_interface() is called after the interface
* was disabled etc., in which case driver_ap_teardown is possibly set
* to 1. Clear it here so any other key/station deletion, which is not
* part of a teardown flow, would also call the relevant driver
* callbacks.
*/
iface->driver_ap_teardown = 0;
if (!iface->phy[0]) {
const char *phy = hostapd_drv_get_radio_name(hapd);
if (phy) {
wpa_printf(MSG_DEBUG, "phy: %s", phy);
os_strlcpy(iface->phy, phy, sizeof(iface->phy));
}
}
/*
* Make sure that all BSSes get configured with a pointer to the same
* driver interface.
*/
for (i = 1; i < iface->num_bss; i++) {
iface->bss[i]->driver = hapd->driver;
iface->bss[i]->drv_priv = hapd->drv_priv;
}
if (hostapd_validate_bssid_configuration(iface))
return -1;
/*
* Initialize control interfaces early to allow external monitoring of
* channel setup operations that may take considerable amount of time
* especially for DFS cases.
*/
if (start_ctrl_iface(iface))
return -1;
if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
char country[4], previous_country[4];
hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
if (hostapd_get_country(hapd, previous_country) < 0)
previous_country[0] = '\0';
os_memcpy(country, hapd->iconf->country, 3);
country[3] = '\0';
if (hostapd_set_country(hapd, country) < 0) {
wpa_printf(MSG_ERROR, "Failed to set country code");
return -1;
}
wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
previous_country, country);
if (os_strncmp(previous_country, country, 2) != 0) {
wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
iface->wait_channel_update = 1;
eloop_register_timeout(5, 0,
channel_list_update_timeout,
iface, NULL);
return 0;
}
}
return setup_interface2(iface);
}
static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
{
int freq, i, j;
if (!iface->conf->channel)
return 0;
if (iface->conf->op_class) {
freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
iface->conf->channel);
if (freq < 0) {
wpa_printf(MSG_INFO,
"Could not convert op_class %u channel %u to operating frequency",
iface->conf->op_class, iface->conf->channel);
return -1;
}
iface->freq = freq;
return 0;
}
/* Old configurations using only 2.4/5/60 GHz bands may not specify the
* op_class parameter. Select a matching channel from the configured
* mode using the channel parameter for these cases.
*/
for (j = 0; j < iface->num_hw_features; j++) {
struct hostapd_hw_modes *mode = &iface->hw_features[j];
if (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
iface->conf->hw_mode != mode->mode)
continue;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->chan == iface->conf->channel &&
!is_6ghz_freq(chan->freq)) {
iface->freq = chan->freq;
return 0;
}
}
}
wpa_printf(MSG_INFO, "Could not determine operating frequency");
return -1;
}
static int setup_interface2(struct hostapd_iface *iface)
{
iface->wait_channel_update = 0;
if (hostapd_get_hw_features(iface)) {
/* Not all drivers support this yet, so continue without hw
* feature data. */
} else {
int ret;
ret = configured_fixed_chan_to_freq(iface);
if (ret < 0)
goto fail;
if (iface->conf->op_class) {
int ch_width;
ch_width = op_class_to_ch_width(iface->conf->op_class);
hostapd_set_oper_chwidth(iface->conf, ch_width);
}
ret = hostapd_select_hw_mode(iface);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Could not select hw_mode and "
"channel. (%d)", ret);
goto fail;
}
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
return 0;
}
ret = hostapd_check_edmg_capab(iface);
if (ret < 0)
goto fail;
ret = hostapd_check_he_6ghz_capab(iface);
if (ret < 0)
goto fail;
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
goto fail;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will "
"be completed in a callback");
return 0;
}
if (iface->conf->ieee80211h)
wpa_printf(MSG_DEBUG, "DFS support is enabled");
}
return hostapd_setup_interface_complete(iface, 0);
fail:
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
if (iface->interfaces && iface->interfaces->terminate_on_error)
eloop_terminate();
return -1;
}
#ifdef CONFIG_FST
static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
{
struct hostapd_data *hapd = ctx;
return hapd->own_addr;
}
static void fst_hostapd_get_channel_info_cb(void *ctx,
enum hostapd_hw_mode *hw_mode,
u8 *channel)
{
struct hostapd_data *hapd = ctx;
*hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
}
static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
{
struct hostapd_data *hapd = ctx;
if (hapd->iface->fst_ies != fst_ies) {
hapd->iface->fst_ies = fst_ies;
if (ieee802_11_set_beacon(hapd))
wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
}
}
static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
struct wpabuf *buf)
{
struct hostapd_data *hapd = ctx;
return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
wpabuf_head(buf), wpabuf_len(buf));
}
static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
return sta ? sta->mb_ies : NULL;
}
static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
const u8 *buf, size_t size)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta) {
struct mb_ies_info info;
if (!mb_ies_info_by_ies(&info, buf, size)) {
wpabuf_free(sta->mb_ies);
sta->mb_ies = mb_ies_by_info(&info);
}
}
}
static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
bool mb_only)
{
struct sta_info *s = (struct sta_info *) *get_ctx;
if (mb_only) {
for (; s && !s->mb_ies; s = s->next)
;
}
if (s) {
*get_ctx = (struct fst_get_peer_ctx *) s->next;
return s->addr;
}
*get_ctx = NULL;
return NULL;
}
static const u8 * fst_hostapd_get_peer_first(void *ctx,
struct fst_get_peer_ctx **get_ctx,
bool mb_only)
{
struct hostapd_data *hapd = ctx;
*get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
return fst_hostapd_get_sta(get_ctx, mb_only);
}
static const u8 * fst_hostapd_get_peer_next(void *ctx,
struct fst_get_peer_ctx **get_ctx,
bool mb_only)
{
return fst_hostapd_get_sta(get_ctx, mb_only);
}
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
struct fst_wpa_obj *iface_obj)
{
iface_obj->ctx = hapd;
iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
iface_obj->set_ies = fst_hostapd_set_ies_cb;
iface_obj->send_action = fst_hostapd_send_action_cb;
iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
iface_obj->get_peer_first = fst_hostapd_get_peer_first;
iface_obj->get_peer_next = fst_hostapd_get_peer_next;
}
#endif /* CONFIG_FST */
#ifdef CONFIG_OWE
static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
{
struct hostapd_data *hapd = ctx;
size_t i;
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
if (os_strcmp(hapd->conf->owe_transition_ifname,
bss->conf->iface) != 0)
continue;
wpa_printf(MSG_DEBUG,
"OWE: ifname=%s found transition mode ifname=%s BSSID "
MACSTR " SSID %s",
hapd->conf->iface, bss->conf->iface,
MAC2STR(bss->own_addr),
wpa_ssid_txt(bss->conf->ssid.ssid,
bss->conf->ssid.ssid_len));
if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
is_zero_ether_addr(bss->own_addr))
continue;
os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
ETH_ALEN);
os_memcpy(hapd->conf->owe_transition_ssid,
bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
wpa_printf(MSG_DEBUG,
"OWE: Copied transition mode information");
return 1;
}
return 0;
}
int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
{
if (hapd->conf->owe_transition_ssid_len > 0 &&
!is_zero_ether_addr(hapd->conf->owe_transition_bssid))
return 0;
/* Find transition mode SSID/BSSID information from a BSS operated by
* this hostapd instance. */
if (!hapd->iface->interfaces ||
!hapd->iface->interfaces->for_each_interface)
return hostapd_owe_iface_iter(hapd->iface, hapd);
else
return hapd->iface->interfaces->for_each_interface(
hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
}
static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
{
size_t i;
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
int res;
if (!bss->conf->owe_transition_ifname[0])
continue;
if (bss->iface->state != HAPD_IFACE_ENABLED) {
wpa_printf(MSG_DEBUG,
"OWE: Interface %s state %s - defer beacon update",
bss->conf->iface,
hostapd_state_text(bss->iface->state));
continue;
}
res = hostapd_owe_trans_get_info(bss);
if (res == 0)
continue;
wpa_printf(MSG_DEBUG,
"OWE: Matching transition mode interface enabled - update beacon data for %s",
bss->conf->iface);
ieee802_11_set_beacon(bss);
}
return 0;
}
#endif /* CONFIG_OWE */
static void hostapd_owe_update_trans(struct hostapd_iface *iface)
{
#ifdef CONFIG_OWE
/* Check whether the enabled BSS can complete OWE transition mode
* configuration for any pending interface. */
if (!iface->interfaces ||
!iface->interfaces->for_each_interface)
hostapd_owe_iface_iter2(iface, NULL);
else
iface->interfaces->for_each_interface(
iface->interfaces, hostapd_owe_iface_iter2, NULL);
#endif /* CONFIG_OWE */
}
static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
struct hostapd_data *hapd;
if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
return;
hapd = iface->bss[0];
if (hapd->setup_complete_cb)
hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
}
static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
int err)
{
struct hostapd_data *hapd = iface->bss[0];
size_t j;
u8 *prev_addr;
int delay_apply_cfg = 0;
int res_dfs_offload = 0;
if (err)
goto fail;
wpa_printf(MSG_DEBUG, "Completing interface initialization");
if (iface->freq) {
#ifdef NEED_AP_MLME
int res;
#endif /* NEED_AP_MLME */
wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
"Frequency: %d MHz",
hostapd_hw_mode_txt(iface->conf->hw_mode),
iface->conf->channel, iface->freq);
#ifdef NEED_AP_MLME
/* Handle DFS only if it is not offloaded to the driver */
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
/* Check DFS */
res = hostapd_handle_dfs(iface);
if (res <= 0) {
if (res < 0)
goto fail;
return res;
}
} else {
/* If DFS is offloaded to the driver */
res_dfs_offload = hostapd_handle_dfs_offload(iface);
if (res_dfs_offload <= 0) {
if (res_dfs_offload < 0)
goto fail;
} else {
wpa_printf(MSG_DEBUG,
"Proceed with AP/channel setup");
/*
* If this is a DFS channel, move to completing
* AP setup.
*/
if (res_dfs_offload == 1)
goto dfs_offload;
/* Otherwise fall through. */
}
}
#endif /* NEED_AP_MLME */
#ifdef CONFIG_MESH
if (iface->mconf != NULL) {
wpa_printf(MSG_DEBUG,
"%s: Mesh configuration will be applied while joining the mesh network",
iface->bss[0]->conf->iface);
delay_apply_cfg = 1;
}
#endif /* CONFIG_MESH */
if (!delay_apply_cfg &&
hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
hapd->iconf->channel,
hapd->iconf->enable_edmg,
hapd->iconf->edmg_channel,
hapd->iconf->ieee80211n,
hapd->iconf->ieee80211ac,
hapd->iconf->ieee80211ax,
hapd->iconf->secondary_channel,
hostapd_get_oper_chwidth(hapd->iconf),
hostapd_get_oper_centr_freq_seg0_idx(
hapd->iconf),
hostapd_get_oper_centr_freq_seg1_idx(
hapd->iconf))) {
wpa_printf(MSG_ERROR, "Could not set channel for "
"kernel driver");
goto fail;
}
}
if (iface->current_mode) {
if (hostapd_prepare_rates(iface, iface->current_mode)) {
wpa_printf(MSG_ERROR, "Failed to prepare rates "
"table.");
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Failed to prepare rates table.");
goto fail;
}
}
if (hapd->iconf->rts_threshold >= -1 &&
hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
hapd->iconf->rts_threshold >= -1) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
"kernel driver");
goto fail;
}
if (hapd->iconf->fragm_threshold >= -1 &&
hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
hapd->iconf->fragm_threshold != -1) {
wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
"for kernel driver");
goto fail;
}
prev_addr = hapd->own_addr;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
if (hostapd_setup_bss(hapd, j == 0)) {
for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
if (j == 0)
break;
j--;
}
goto fail;
}
if (is_zero_ether_addr(hapd->conf->bssid))
prev_addr = hapd->own_addr;
}
hapd = iface->bss[0];
hostapd_tx_queue_params(iface);
ap_list_init(iface);
hostapd_set_acl(hapd);
if (hostapd_driver_commit(hapd) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
"configuration", __func__);
goto fail;
}
/*
* WPS UPnP module can be initialized only when the "upnp_iface" is up.
* If "interface" and "upnp_iface" are the same (e.g., non-bridge
* mode), the interface is up only after driver_commit, so initialize
* WPS after driver_commit.
*/
for (j = 0; j < iface->num_bss; j++) {
if (hostapd_init_wps_complete(iface->bss[j]))
goto fail;
}
if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
!res_dfs_offload) {
/*
* If freq is DFS, and DFS is offloaded to the driver, then wait
* for CAC to complete.
*/
wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
return res_dfs_offload;
}
#ifdef NEED_AP_MLME
dfs_offload:
#endif /* NEED_AP_MLME */
#ifdef CONFIG_FST
if (hapd->iconf->fst_cfg.group_id[0]) {
struct fst_wpa_obj iface_obj;
fst_hostapd_fill_iface_obj(hapd, &iface_obj);
iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
&iface_obj, &hapd->iconf->fst_cfg);
if (!iface->fst) {
wpa_printf(MSG_ERROR, "Could not attach to FST %s",
hapd->iconf->fst_cfg.group_id);
goto fail;
}
}
#endif /* CONFIG_FST */
hostapd_set_state(iface, HAPD_IFACE_ENABLED);
hostapd_owe_update_trans(iface);
airtime_policy_update_init(iface);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
if (hapd->setup_complete_cb)
hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
#ifdef CONFIG_MESH
if (delay_apply_cfg && !iface->mconf) {
wpa_printf(MSG_ERROR, "Error while completing mesh init");
goto fail;
}
#endif /* CONFIG_MESH */
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
iface->interfaces->terminate_on_error--;
for (j = 0; j < iface->num_bss; j++)
hostapd_neighbor_set_own_report(iface->bss[j]);
return 0;
fail:
wpa_printf(MSG_ERROR, "Interface initialization failed");
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
#ifdef CONFIG_FST
if (iface->fst) {
fst_detach(iface->fst);
iface->fst = NULL;
}
#endif /* CONFIG_FST */
if (iface->interfaces && iface->interfaces->terminate_on_error) {
eloop_terminate();
} else if (hapd->setup_complete_cb) {
/*
* Calling hapd->setup_complete_cb directly may cause iface
* deinitialization which may be accessed later by the caller.
*/
eloop_register_timeout(0, 0,
hostapd_interface_setup_failure_handler,
iface, NULL);
}
return -1;
}
/**
* hostapd_setup_interface_complete - Complete interface setup
*
* This function is called when previous steps in the interface setup has been
* completed. This can also start operations, e.g., DFS, that will require
* additional processing before interface is ready to be enabled. Such
* operations will call this function from eloop callbacks when finished.
*/
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
{
struct hapd_interfaces *interfaces = iface->interfaces;
struct hostapd_data *hapd = iface->bss[0];
unsigned int i;
int not_ready_in_sync_ifaces = 0;
if (!iface->need_to_start_in_sync)
return hostapd_setup_interface_complete_sync(iface, err);
if (err) {
wpa_printf(MSG_ERROR, "Interface initialization failed");
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
iface->need_to_start_in_sync = 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
if (interfaces && interfaces->terminate_on_error)
eloop_terminate();
return -1;
}
if (iface->ready_to_start_in_sync) {
/* Already in ready and waiting. should never happpen */
return 0;
}
for (i = 0; i < interfaces->count; i++) {
if (interfaces->iface[i]->need_to_start_in_sync &&
!interfaces->iface[i]->ready_to_start_in_sync)
not_ready_in_sync_ifaces++;
}
/*
* Check if this is the last interface, if yes then start all the other
* waiting interfaces. If not, add this interface to the waiting list.
*/
if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
/*
* If this interface went through CAC, do not synchronize, just
* start immediately.
*/
iface->need_to_start_in_sync = 0;
wpa_printf(MSG_INFO,
"%s: Finished CAC - bypass sync and start interface",
iface->bss[0]->conf->iface);
return hostapd_setup_interface_complete_sync(iface, err);
}
if (not_ready_in_sync_ifaces > 1) {
/* need to wait as there are other interfaces still coming up */
iface->ready_to_start_in_sync = 1;
wpa_printf(MSG_INFO,
"%s: Interface waiting to sync with other interfaces",
iface->bss[0]->conf->iface);
return 0;
}
wpa_printf(MSG_INFO,
"%s: Last interface to sync - starting all interfaces",
iface->bss[0]->conf->iface);
iface->need_to_start_in_sync = 0;
hostapd_setup_interface_complete_sync(iface, err);
for (i = 0; i < interfaces->count; i++) {
if (interfaces->iface[i]->need_to_start_in_sync &&
interfaces->iface[i]->ready_to_start_in_sync) {
hostapd_setup_interface_complete_sync(
interfaces->iface[i], 0);
/* Only once the interfaces are sync started */
interfaces->iface[i]->need_to_start_in_sync = 0;
}
}
return 0;
}
/**
* hostapd_setup_interface - Setup of an interface
* @iface: Pointer to interface data.
* Returns: 0 on success, -1 on failure
*
* Initializes the driver interface, validates the configuration,
* and sets driver parameters based on the configuration.
* Flushes old stations, sets the channel, encryption,
* beacons, and WDS links based on the configuration.
*
* If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
* or DFS operations, this function returns 0 before such operations have been
* completed. The pending operations are registered into eloop and will be
* completed from eloop callbacks. Those callbacks end up calling
* hostapd_setup_interface_complete() once setup has been completed.
*/
int hostapd_setup_interface(struct hostapd_iface *iface)
{
int ret;
if (!iface->conf)
return -1;
ret = setup_interface(iface);
if (ret) {
wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
iface->conf->bss[0]->iface);
return -1;
}
return 0;
}
/**
* hostapd_alloc_bss_data - Allocate and initialize per-BSS data
* @hapd_iface: Pointer to interface data
* @conf: Pointer to per-interface configuration
* @bss: Pointer to per-BSS configuration for this BSS
* Returns: Pointer to allocated BSS data
*
* This function is used to allocate per-BSS data structure. This data will be
* freed after hostapd_cleanup() is called for it during interface
* deinitialization.
*/
struct hostapd_data *
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
struct hostapd_config *conf,
struct hostapd_bss_config *bss)
{
struct hostapd_data *hapd;
hapd = os_zalloc(sizeof(*hapd));
if (hapd == NULL)
return NULL;
hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
hapd->iconf = conf;
hapd->conf = bss;
hapd->iface = hapd_iface;
if (conf)
hapd->driver = conf->driver;
hapd->ctrl_sock = -1;
dl_list_init(&hapd->ctrl_dst);
dl_list_init(&hapd->nr_db);
hapd->dhcp_sock = -1;
#ifdef CONFIG_IEEE80211R_AP
dl_list_init(&hapd->l2_queue);
dl_list_init(&hapd->l2_oui_queue);
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
dl_list_init(&hapd->sae_commit_queue);
#endif /* CONFIG_SAE */
return hapd;
}
static void hostapd_bss_deinit(struct hostapd_data *hapd)
{
if (!hapd)
return;
wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
hapd->conf ? hapd->conf->iface : "N/A");
hostapd_bss_deinit_no_free(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
#ifdef CONFIG_SQLITE
if (hapd->rad_attr_db) {
sqlite3_close(hapd->rad_attr_db);
hapd->rad_attr_db = NULL;
}
#endif /* CONFIG_SQLITE */
hostapd_cleanup(hapd);
}
void hostapd_interface_deinit(struct hostapd_iface *iface)
{
int j;
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
if (iface == NULL)
return;
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
iface->wait_channel_update = 0;
#ifdef CONFIG_FST
if (iface->fst) {
fst_detach(iface->fst);
iface->fst = NULL;
}
#endif /* CONFIG_FST */
for (j = (int) iface->num_bss - 1; j >= 0; j--) {
if (!iface->bss)
break;
hostapd_bss_deinit(iface->bss[j]);
}
#ifdef NEED_AP_MLME
hostapd_stop_setup_timers(iface);
eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
#endif /* NEED_AP_MLME */
}
void hostapd_interface_free(struct hostapd_iface *iface)
{
size_t j;
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
for (j = 0; j < iface->num_bss; j++) {
if (!iface->bss)
break;
wpa_printf(MSG_DEBUG, "%s: free hapd %p",
__func__, iface->bss[j]);
os_free(iface->bss[j]);
}
hostapd_cleanup_iface(iface);
}
struct hostapd_iface * hostapd_alloc_iface(void)
{
struct hostapd_iface *hapd_iface;
hapd_iface = os_zalloc(sizeof(*hapd_iface));
if (!hapd_iface)
return NULL;
dl_list_init(&hapd_iface->sta_seen);
return hapd_iface;
}
/**
* hostapd_init - Allocate and initialize per-interface data
* @config_file: Path to the configuration file
* Returns: Pointer to the allocated interface data or %NULL on failure
*
* This function is used to allocate main data structures for per-interface
* data. The allocated data buffer will be freed by calling
* hostapd_cleanup_iface().
*/
struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
const char *config_file)
{
struct hostapd_iface *hapd_iface = NULL;
struct hostapd_config *conf = NULL;
struct hostapd_data *hapd;
size_t i;
hapd_iface = hostapd_alloc_iface();
if (hapd_iface == NULL)
goto fail;
hapd_iface->config_fname = os_strdup(config_file);
if (hapd_iface->config_fname == NULL)
goto fail;
conf = interfaces->config_read_cb(hapd_iface->config_fname);
if (conf == NULL)
goto fail;
hapd_iface->conf = conf;
hapd_iface->num_bss = conf->num_bss;
hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL)
goto fail;
for (i = 0; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] =
hostapd_alloc_bss_data(hapd_iface, conf,
conf->bss[i]);
if (hapd == NULL)
goto fail;
hapd->msg_ctx = hapd;
}
return hapd_iface;
fail:
wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
config_file);
if (conf)
hostapd_config_free(conf);
if (hapd_iface) {
os_free(hapd_iface->config_fname);
os_free(hapd_iface->bss);
wpa_printf(MSG_DEBUG, "%s: free iface %p",
__func__, hapd_iface);
os_free(hapd_iface);
}
return NULL;
}
static int ifname_in_use(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 = iface->bss[j];
if (os_strcmp(ifname, hapd->conf->iface) == 0)
return 1;
}
}
return 0;
}
/**
* hostapd_interface_init_bss - Read configuration file and init BSS data
*
* This function is used to parse configuration file for a BSS. This BSS is
* added to an existing interface sharing the same radio (if any) or a new
* interface is created if this is the first interface on a radio. This
* allocate memory for the BSS. No actual driver operations are started.
*
* This is similar to hostapd_interface_init(), but for a case where the
* configuration is used to add a single BSS instead of all BSSes for a radio.
*/
struct hostapd_iface *
hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
const char *config_fname, int debug)
{
struct hostapd_iface *new_iface = NULL, *iface = NULL;
struct hostapd_data *hapd;
int k;
size_t i, bss_idx;
if (!phy || !*phy)
return NULL;
for (i = 0; i < interfaces->count; i++) {
if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
iface = interfaces->iface[i];
break;
}
}
wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
config_fname, phy, iface ? "" : " --> new PHY");
if (iface) {
struct hostapd_config *conf;
struct hostapd_bss_config **tmp_conf;
struct hostapd_data **tmp_bss;
struct hostapd_bss_config *bss;
const char *ifname;
/* Add new BSS to existing iface */
conf = interfaces->config_read_cb(config_fname);
if (conf == NULL)
return NULL;
if (conf->num_bss > 1) {
wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
hostapd_config_free(conf);
return NULL;
}
ifname = conf->bss[0]->iface;
if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
wpa_printf(MSG_ERROR,
"Interface name %s already in use", ifname);
hostapd_config_free(conf);
return NULL;
}
tmp_conf = os_realloc_array(
iface->conf->bss, iface->conf->num_bss + 1,
sizeof(struct hostapd_bss_config *));
tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
sizeof(struct hostapd_data *));
if (tmp_bss)
iface->bss = tmp_bss;
if (tmp_conf) {
iface->conf->bss = tmp_conf;
iface->conf->last_bss = tmp_conf[0];
}
if (tmp_bss == NULL || tmp_conf == NULL) {
hostapd_config_free(conf);
return NULL;
}
bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
iface->conf->num_bss++;
hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
if (hapd == NULL) {
iface->conf->num_bss--;
hostapd_config_free(conf);
return NULL;
}
iface->conf->last_bss = bss;
iface->bss[iface->num_bss] = hapd;
hapd->msg_ctx = hapd;
bss_idx = iface->num_bss++;
conf->num_bss--;
conf->bss[0] = NULL;
hostapd_config_free(conf);
} else {
/* Add a new iface with the first BSS */
new_iface = iface = hostapd_init(interfaces, config_fname);
if (!iface)
return NULL;
os_strlcpy(iface->phy, phy, sizeof(iface->phy));
iface->interfaces = interfaces;
bss_idx = 0;
}
for (k = 0; k < debug; k++) {
if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
iface->bss[bss_idx]->conf->logger_stdout_level--;
}
if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
!hostapd_drv_none(iface->bss[bss_idx])) {
wpa_printf(MSG_ERROR, "Interface name not specified in %s",
config_fname);
if (new_iface)
hostapd_interface_deinit_free(new_iface);
return NULL;
}
return iface;
}
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
const struct wpa_driver_ops *driver;
void *drv_priv;
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
if (iface == NULL)
return;
wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
__func__, (unsigned int) iface->num_bss,
(unsigned int) iface->conf->num_bss);
driver = iface->bss[0]->driver;
drv_priv = iface->bss[0]->drv_priv;
hostapd_interface_deinit(iface);
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
if (driver && driver->hapd_deinit && drv_priv) {
driver->hapd_deinit(drv_priv);
iface->bss[0]->drv_priv = NULL;
}
hostapd_interface_free(iface);
}
static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
void *drv_priv,
struct hostapd_iface *hapd_iface)
{
size_t j;
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
if (driver && driver->hapd_deinit && drv_priv) {
driver->hapd_deinit(drv_priv);
for (j = 0; j < hapd_iface->num_bss; j++) {
wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
__func__, (int) j,
hapd_iface->bss[j]->drv_priv);
if (hapd_iface->bss[j]->drv_priv == drv_priv) {
hapd_iface->bss[j]->drv_priv = NULL;
hapd_iface->extended_capa = NULL;
hapd_iface->extended_capa_mask = NULL;
hapd_iface->extended_capa_len = 0;
}
}
}
}
int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
if (!hapd_iface)
return -1;
if (hapd_iface->enable_iface_cb)
return hapd_iface->enable_iface_cb(hapd_iface);
if (hapd_iface->bss[0]->drv_priv != NULL) {
wpa_printf(MSG_ERROR, "Interface %s already enabled",
hapd_iface->conf->bss[0]->iface);
return -1;
}
wpa_printf(MSG_DEBUG, "Enable interface %s",
hapd_iface->conf->bss[0]->iface);
for (j = 0; j < hapd_iface->num_bss; j++)
hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
return -1;
}
if (hapd_iface->interfaces == NULL ||
hapd_iface->interfaces->driver_init == NULL ||
hapd_iface->interfaces->driver_init(hapd_iface))
return -1;
if (hostapd_setup_interface(hapd_iface)) {
hostapd_deinit_driver(hapd_iface->bss[0]->driver,
hapd_iface->bss[0]->drv_priv,
hapd_iface);
return -1;
}
return 0;
}
int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
wpa_printf(MSG_DEBUG, "Reload interface %s",
hapd_iface->conf->bss[0]->iface);
for (j = 0; j < hapd_iface->num_bss; j++)
hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
wpa_printf(MSG_ERROR, "Updated configuration is invalid");
return -1;
}
hostapd_clear_old(hapd_iface);
for (j = 0; j < hapd_iface->num_bss; j++)
hostapd_reload_bss(hapd_iface->bss[j]);
return 0;
}
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
const struct wpa_driver_ops *driver;
void *drv_priv;
if (hapd_iface == NULL)
return -1;
if (hapd_iface->disable_iface_cb)
return hapd_iface->disable_iface_cb(hapd_iface);
if (hapd_iface->bss[0]->drv_priv == NULL) {
wpa_printf(MSG_INFO, "Interface %s already disabled",
hapd_iface->conf->bss[0]->iface);
return -1;
}
wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
driver = hapd_iface->bss[0]->driver;
drv_priv = hapd_iface->bss[0]->drv_priv;
hapd_iface->driver_ap_teardown =
!!(hapd_iface->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
#ifdef NEED_AP_MLME
for (j = 0; j < hapd_iface->num_bss; j++)
hostapd_cleanup_cs_params(hapd_iface->bss[j]);
#endif /* NEED_AP_MLME */
/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
}
hostapd_deinit_driver(driver, drv_priv, hapd_iface);
/* From hostapd_cleanup_iface: These were initialized in
* hostapd_setup_interface and hostapd_setup_interface_complete
*/
hostapd_cleanup_iface_partial(hapd_iface);
wpa_printf(MSG_DEBUG, "Interface %s disabled",
hapd_iface->bss[0]->conf->iface);
hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
return 0;
}
static struct hostapd_iface *
hostapd_iface_alloc(struct hapd_interfaces *interfaces)
{
struct hostapd_iface **iface, *hapd_iface;
iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
sizeof(struct hostapd_iface *));
if (iface == NULL)
return NULL;
interfaces->iface = iface;
hapd_iface = interfaces->iface[interfaces->count] =
hostapd_alloc_iface();
if (hapd_iface == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
"the interface", __func__);
return NULL;
}
interfaces->count++;
hapd_iface->interfaces = interfaces;
return hapd_iface;
}
static struct hostapd_config *
hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
const char *ctrl_iface, const char *driver)
{
struct hostapd_bss_config *bss;
struct hostapd_config *conf;
/* Allocates memory for bss and conf */
conf = hostapd_config_defaults();
if (conf == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
"configuration", __func__);
return NULL;
}
if (driver) {
int j;
for (j = 0; wpa_drivers[j]; j++) {
if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
conf->driver = wpa_drivers[j];
goto skip;
}
}
wpa_printf(MSG_ERROR,
"Invalid/unknown driver '%s' - registering the default driver",
driver);
}
conf->driver = wpa_drivers[0];
if (conf->driver == NULL) {
wpa_printf(MSG_ERROR, "No driver wrappers registered!");
hostapd_config_free(conf);
return NULL;
}
skip:
bss = conf->last_bss = conf->bss[0];
os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
bss->ctrl_interface = os_strdup(ctrl_iface);
if (bss->ctrl_interface == NULL) {
hostapd_config_free(conf);
return NULL;
}
/* Reading configuration file skipped, will be done in SET!
* From reading the configuration till the end has to be done in
* SET
*/
return conf;
}
static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
struct hostapd_config *conf)
{
size_t i;
struct hostapd_data *hapd;
hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL)
return -1;
for (i = 0; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] =
hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
if (hapd == NULL) {
while (i > 0) {
i--;
os_free(hapd_iface->bss[i]);
hapd_iface->bss[i] = NULL;
}
os_free(hapd_iface->bss);
hapd_iface->bss = NULL;
return -1;
}
hapd->msg_ctx = hapd;
}
hapd_iface->conf = conf;
hapd_iface->num_bss = conf->num_bss;
return 0;
}
int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_config *conf = NULL;
struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
struct hostapd_data *hapd;
char *ptr;
size_t i, j;
const char *conf_file = NULL, *phy_name = NULL;
if (os_strncmp(buf, "bss_config=", 11) == 0) {
char *pos;
phy_name = buf + 11;
pos = os_strchr(phy_name, ':');
if (!pos)
return -1;
*pos++ = '\0';
conf_file = pos;
if (!os_strlen(conf_file))
return -1;
hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
conf_file, 0);
if (!hapd_iface)
return -1;
for (j = 0; j < interfaces->count; j++) {
if (interfaces->iface[j] == hapd_iface)
break;
}
if (j == interfaces->count) {
struct hostapd_iface **tmp;
tmp = os_realloc_array(interfaces->iface,
interfaces->count + 1,
sizeof(struct hostapd_iface *));
if (!tmp) {
hostapd_interface_deinit_free(hapd_iface);
return -1;
}
interfaces->iface = tmp;
interfaces->iface[interfaces->count++] = hapd_iface;
new_iface = hapd_iface;
}
if (new_iface) {
if (interfaces->driver_init(hapd_iface))
goto fail;
if (hostapd_setup_interface(hapd_iface)) {
hostapd_deinit_driver(
hapd_iface->bss[0]->driver,
hapd_iface->bss[0]->drv_priv,
hapd_iface);
goto fail;
}
} else {
/* Assign new BSS with bss[0]'s driver info */
hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
hapd->driver = hapd_iface->bss[0]->driver;
hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
ETH_ALEN);
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
hostapd_setup_bss(hapd, -1))) {
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
hapd_iface->conf->num_bss--;
hapd_iface->num_bss--;
wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
os_free(hapd);
return -1;
}
}
hostapd_owe_update_trans(hapd_iface);
return 0;
}
ptr = os_strchr(buf, ' ');
if (ptr == NULL)
return -1;
*ptr++ = '\0';
if (os_strncmp(ptr, "config=", 7) == 0)
conf_file = ptr + 7;
for (i = 0; i < interfaces->count; i++) {
if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
buf)) {
wpa_printf(MSG_INFO, "Cannot add interface - it "
"already exists");
return -1;
}
}
hapd_iface = hostapd_iface_alloc(interfaces);
if (hapd_iface == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for interface", __func__);
goto fail;
}
new_iface = hapd_iface;
if (conf_file && interfaces->config_read_cb) {
conf = interfaces->config_read_cb(conf_file);
if (conf && conf->bss)
os_strlcpy(conf->bss[0]->iface, buf,
sizeof(conf->bss[0]->iface));
} else {
char *driver = os_strchr(ptr, ' ');
if (driver)
*driver++ = '\0';
conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
}
if (conf == NULL || conf->bss == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for configuration", __func__);
goto fail;
}
if (hostapd_data_alloc(hapd_iface, conf) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for hostapd", __func__);
goto fail;
}
conf = NULL;
if (start_ctrl_iface(hapd_iface) < 0)
goto fail;
wpa_printf(MSG_INFO, "Add interface '%s'",
hapd_iface->conf->bss[0]->iface);
return 0;
fail:
if (conf)
hostapd_config_free(conf);
if (hapd_iface) {
if (hapd_iface->bss) {
for (i = 0; i < hapd_iface->num_bss; i++) {
hapd = hapd_iface->bss[i];
if (!hapd)
continue;
if (hapd_iface->interfaces &&
hapd_iface->interfaces->ctrl_iface_deinit)
hapd_iface->interfaces->
ctrl_iface_deinit(hapd);
wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
__func__, hapd_iface->bss[i],
hapd->conf->iface);
hostapd_cleanup(hapd);
os_free(hapd);
hapd_iface->bss[i] = NULL;
}
os_free(hapd_iface->bss);
hapd_iface->bss = NULL;
}
if (new_iface) {
interfaces->count--;
interfaces->iface[interfaces->count] = NULL;
}
hostapd_cleanup_iface(hapd_iface);
}
return -1;
}
static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
{
size_t i;
wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
/* Remove hostapd_data only if it has already been initialized */
if (idx < iface->num_bss) {
struct hostapd_data *hapd = iface->bss[idx];
hostapd_bss_deinit(hapd);
wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
os_free(hapd);
iface->num_bss--;
for (i = idx; i < iface->num_bss; i++)
iface->bss[i] = iface->bss[i + 1];
} else {
hostapd_config_free_bss(iface->conf->bss[idx]);
iface->conf->bss[idx] = NULL;
}
iface->conf->num_bss--;
for (i = idx; i < iface->conf->num_bss; i++)
iface->conf->bss[i] = iface->conf->bss[i + 1];
return 0;
}
int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_iface *hapd_iface;
size_t i, j, k = 0;
for (i = 0; i < interfaces->count; i++) {
hapd_iface = interfaces->iface[i];
if (hapd_iface == NULL)
return -1;
if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
hapd_iface->driver_ap_teardown =
!!(hapd_iface->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
hostapd_interface_deinit_free(hapd_iface);
k = i;
while (k < (interfaces->count - 1)) {
interfaces->iface[k] =
interfaces->iface[k + 1];
k++;
}
interfaces->count--;
return 0;
}
for (j = 0; j < hapd_iface->conf->num_bss; j++) {
if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
hapd_iface->driver_ap_teardown =
!(hapd_iface->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
return hostapd_remove_bss(hapd_iface, j);
}
}
}
return -1;
}
/**
* hostapd_new_assoc_sta - Notify that a new station associated with the AP
* @hapd: Pointer to BSS data
* @sta: Pointer to the associated STA data
* @reassoc: 1 to indicate this was a re-association; 0 = first association
*
* This function will be called whenever a station associates with the AP. It
* can be called from ieee802_11.c for drivers that export MLME to hostapd and
* from drv_callbacks.c based on driver events for drivers that take care of
* management frames (IEEE 802.11 authentication and association) internally.
*/
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc)
{
if (hapd->tkip_countermeasures) {
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_MICHAEL_MIC_FAILURE);
return;
}
hostapd_prune_associations(hapd, sta->addr);
ap_sta_clear_disconnect_timeouts(hapd, sta);
sta->post_csa_sa_query = 0;
#ifdef CONFIG_P2P
if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
sta->no_p2p_set = 1;
hapd->num_sta_no_p2p++;
if (hapd->num_sta_no_p2p == 1)
hostapd_p2p_non_p2p_sta_connected(hapd);
}
#endif /* CONFIG_P2P */
airtime_policy_new_sta(hapd, sta);
/* Start accounting here, if IEEE 802.1X and WPA are not used.
* IEEE 802.1X/WPA code will start accounting after the station has
* been authorized. */
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
ap_sta_set_authorized(hapd, sta, 1);
os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
/* Start IEEE 802.1X authentication process for new stations */
ieee802_1x_new_station(hapd, sta);
if (reassoc) {
if (sta->auth_alg != WLAN_AUTH_FT &&
sta->auth_alg != WLAN_AUTH_FILS_SK &&
sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
sta->auth_alg != WLAN_AUTH_FILS_PK &&
!(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
} else
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
wpa_printf(MSG_DEBUG,
"%s: %s: canceled wired ap_handle_timer timeout for "
MACSTR,
hapd->conf->iface, __func__,
MAC2STR(sta->addr));
}
} else if (!(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG,
"%s: %s: reschedule ap_handle_timer timeout for "
MACSTR " (%d seconds - ap_max_inactivity)",
hapd->conf->iface, __func__, MAC2STR(sta->addr),
hapd->conf->ap_max_inactivity);
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
ap_handle_timer, hapd, sta);
}
#ifdef CONFIG_MACSEC
if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
hapd->conf->mka_psk_set)
ieee802_1x_create_preshared_mka_hapd(hapd, sta);
else
ieee802_1x_alloc_kay_sm_hapd(hapd, sta);
#endif /* CONFIG_MACSEC */
}
const char * hostapd_state_text(enum hostapd_iface_state s)
{
switch (s) {
case HAPD_IFACE_UNINITIALIZED:
return "UNINITIALIZED";
case HAPD_IFACE_DISABLED:
return "DISABLED";
case HAPD_IFACE_COUNTRY_UPDATE:
return "COUNTRY_UPDATE";
case HAPD_IFACE_ACS:
return "ACS";
case HAPD_IFACE_HT_SCAN:
return "HT_SCAN";
case HAPD_IFACE_DFS:
return "DFS";
case HAPD_IFACE_ENABLED:
return "ENABLED";
}
return "UNKNOWN";
}
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
{
wpa_printf(MSG_INFO, "%s: interface state %s->%s",
iface->conf ? iface->conf->bss[0]->iface : "N/A",
hostapd_state_text(iface->state), hostapd_state_text(s));
iface->state = s;
}
int hostapd_csa_in_progress(struct hostapd_iface *iface)
{
unsigned int i;
for (i = 0; i < iface->num_bss; i++)
if (iface->bss[i]->csa_in_progress)
return 1;
return 0;
}
#ifdef NEED_AP_MLME
static void free_beacon_data(struct beacon_data *beacon)
{
os_free(beacon->head);
beacon->head = NULL;
os_free(beacon->tail);
beacon->tail = NULL;
os_free(beacon->probe_resp);
beacon->probe_resp = NULL;
os_free(beacon->beacon_ies);
beacon->beacon_ies = NULL;
os_free(beacon->proberesp_ies);
beacon->proberesp_ies = NULL;
os_free(beacon->assocresp_ies);
beacon->assocresp_ies = NULL;
}
static int hostapd_build_beacon_data(struct hostapd_data *hapd,
struct beacon_data *beacon)
{
struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
struct wpa_driver_ap_params params;
int ret;
os_memset(beacon, 0, sizeof(*beacon));
ret = ieee802_11_build_ap_params(hapd, &params);
if (ret < 0)
return ret;
ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
&proberesp_extra,
&assocresp_extra);
if (ret)
goto free_ap_params;
ret = -1;
beacon->head = os_memdup(params.head, params.head_len);
if (!beacon->head)
goto free_ap_extra_ies;
beacon->head_len = params.head_len;
beacon->tail = os_memdup(params.tail, params.tail_len);
if (!beacon->tail)
goto free_beacon;
beacon->tail_len = params.tail_len;
if (params.proberesp != NULL) {
beacon->probe_resp = os_memdup(params.proberesp,
params.proberesp_len);
if (!beacon->probe_resp)
goto free_beacon;
beacon->probe_resp_len = params.proberesp_len;
}
/* copy the extra ies */
if (beacon_extra) {
beacon->beacon_ies = os_memdup(beacon_extra->buf,
wpabuf_len(beacon_extra));
if (!beacon->beacon_ies)
goto free_beacon;
beacon->beacon_ies_len = wpabuf_len(beacon_extra);
}
if (proberesp_extra) {
beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
wpabuf_len(proberesp_extra));
if (!beacon->proberesp_ies)
goto free_beacon;
beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
}
if (assocresp_extra) {
beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
wpabuf_len(assocresp_extra));
if (!beacon->assocresp_ies)
goto free_beacon;
beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
}
ret = 0;
free_beacon:
/* if the function fails, the caller should not free beacon data */
if (ret)
free_beacon_data(beacon);
free_ap_extra_ies:
hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
assocresp_extra);
free_ap_params:
ieee802_11_free_ap_params(&params);
return ret;
}
/*
* TODO: This flow currently supports only changing channel and width within
* the same hw_mode. Any other changes to MAC parameters or provided settings
* are not supported.
*/
static int hostapd_change_config_freq(struct hostapd_data *hapd,
struct hostapd_config *conf,
struct hostapd_freq_params *params,
struct hostapd_freq_params *old_params)
{
int channel;
u8 seg0, seg1;
struct hostapd_hw_modes *mode;
if (!params->channel) {
/* check if the new channel is supported by hw */
params->channel = hostapd_hw_get_channel(hapd, params->freq);
}
channel = params->channel;
if (!channel)
return -1;
mode = hapd->iface->current_mode;
/* if a pointer to old_params is provided we save previous state */
if (old_params &&
hostapd_set_freq_params(old_params, conf->hw_mode,
hostapd_hw_get_freq(hapd, conf->channel),
conf->channel, conf->enable_edmg,
conf->edmg_channel, conf->ieee80211n,
conf->ieee80211ac, conf->ieee80211ax,
conf->secondary_channel,
hostapd_get_oper_chwidth(conf),
hostapd_get_oper_centr_freq_seg0_idx(conf),
hostapd_get_oper_centr_freq_seg1_idx(conf),
conf->vht_capab,
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL))
return -1;
switch (params->bandwidth) {
case 0:
case 20:
case 40:
hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
break;
case 80:
if (params->center_freq2)
hostapd_set_oper_chwidth(conf, CHANWIDTH_80P80MHZ);
else
hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
break;
case 160:
hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
break;
default:
return -1;
}
conf->channel = channel;
conf->ieee80211n = params->ht_enabled;
conf->secondary_channel = params->sec_channel_offset;
ieee80211_freq_to_chan(params->center_freq1,
&seg0);
ieee80211_freq_to_chan(params->center_freq2,
&seg1);
hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
/* TODO: maybe call here hostapd_config_check here? */
return 0;
}
static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
struct csa_settings *settings)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_freq_params old_freq;
int ret;
u8 chan, bandwidth;
os_memset(&old_freq, 0, sizeof(old_freq));
if (!iface || !iface->freq || hapd->csa_in_progress)
return -1;
switch (settings->freq_params.bandwidth) {
case 80:
if (settings->freq_params.center_freq2)
bandwidth = CHANWIDTH_80P80MHZ;
else
bandwidth = CHANWIDTH_80MHZ;
break;
case 160:
bandwidth = CHANWIDTH_160MHZ;
break;
default:
bandwidth = CHANWIDTH_USE_HT;
break;
}
if (ieee80211_freq_to_channel_ext(
settings->freq_params.freq,
settings->freq_params.sec_channel_offset,
bandwidth,
&hapd->iface->cs_oper_class,
&chan) == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_DEBUG,
"invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
settings->freq_params.freq,
settings->freq_params.sec_channel_offset,
settings->freq_params.vht_enabled,
settings->freq_params.he_enabled);
return -1;
}
settings->freq_params.channel = chan;
ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
&settings->freq_params,
&old_freq);
if (ret)
return ret;
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
/* change back the configuration */
hostapd_change_config_freq(iface->bss[0], iface->conf,
&old_freq, NULL);
if (ret)
return ret;
/* set channel switch parameters for csa ie */
hapd->cs_freq_params = settings->freq_params;
hapd->cs_count = settings->cs_count;
hapd->cs_block_tx = settings->block_tx;
ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
if (ret) {
free_beacon_data(&settings->beacon_after);
return ret;
}
settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
return 0;
}
void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
{
os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
hapd->cs_count = 0;
hapd->cs_block_tx = 0;
hapd->cs_c_off_beacon = 0;
hapd->cs_c_off_proberesp = 0;
hapd->csa_in_progress = 0;
hapd->cs_c_off_ecsa_beacon = 0;
hapd->cs_c_off_ecsa_proberesp = 0;
}
void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params)
{
if (freq_params->he_enabled)
hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
else
hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_DISABLED;
if (freq_params->vht_enabled)
hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
else
hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"CHAN_SWITCH HE config 0x%x VHT config 0x%x",
hapd->iconf->ch_switch_he_config,
hapd->iconf->ch_switch_vht_config);
}
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
int ret;
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
wpa_printf(MSG_INFO, "CSA is not supported");
return -1;
}
ret = hostapd_fill_csa_settings(hapd, settings);
if (ret)
return ret;
ret = hostapd_drv_switch_channel(hapd, settings);
free_beacon_data(&settings->beacon_csa);
free_beacon_data(&settings->beacon_after);
if (ret) {
/* if we failed, clean cs parameters */
hostapd_cleanup_cs_params(hapd);
return ret;
}
hapd->csa_in_progress = 1;
return 0;
}
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params)
{
int seg0_idx = 0, seg1_idx = 0, bw = CHANWIDTH_USE_HT;
wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
if (freq_params->center_freq1)
seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
if (freq_params->center_freq2)
seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
switch (freq_params->bandwidth) {
case 0:
case 20:
case 40:
bw = CHANWIDTH_USE_HT;
break;
case 80:
if (freq_params->center_freq2)
bw = CHANWIDTH_80P80MHZ;
else
bw = CHANWIDTH_80MHZ;
break;
case 160:
bw = CHANWIDTH_160MHZ;
break;
default:
wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
freq_params->bandwidth);
break;
}
iface->freq = freq_params->freq;
iface->conf->channel = freq_params->channel;
iface->conf->secondary_channel = freq_params->sec_channel_offset;
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
hostapd_set_oper_chwidth(iface->conf, bw);
iface->conf->ieee80211n = freq_params->ht_enabled;
iface->conf->ieee80211ac = freq_params->vht_enabled;
iface->conf->ieee80211ax = freq_params->he_enabled;
/*
* cs_params must not be cleared earlier because the freq_params
* argument may actually point to one of these.
* These params will be cleared during interface disable below.
*/
hostapd_disable_iface(iface);
hostapd_enable_iface(iface);
}
#endif /* NEED_AP_MLME */
struct hostapd_data * hostapd_get_iface(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 = iface->bss[j];
if (os_strcmp(ifname, hapd->conf->iface) == 0)
return hapd;
}
}
return NULL;
}
void hostapd_periodic_iface(struct hostapd_iface *iface)
{
size_t i;
ap_list_timer(iface);
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
if (!hapd->started)
continue;
#ifndef CONFIG_NO_RADIUS
hostapd_acl_expire(hapd);
#endif /* CONFIG_NO_RADIUS */
}
}
#ifdef CONFIG_OCV
void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta;
wpa_printf(MSG_DEBUG, "OCV: Post-CSA SA Query initiation check");
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!sta->post_csa_sa_query)
continue;
wpa_printf(MSG_DEBUG, "OCV: OCVC STA " MACSTR
" did not start SA Query after CSA - disconnect",
MAC2STR(sta->addr));
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
}
}
#endif /* CONFIG_OCV */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 44f566a42403..07d0aaa92100 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -1,691 +1,693 @@
/*
* 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;
};
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/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 72d102f44e21..b404e84affe5 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6993 +1,7072 @@
/*
* 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), rx_id,
+ (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)
- continue;
- if (sta->sae->state != SAE_COMMITTED &&
- sta->sae->state != SAE_CONFIRMED)
- continue;
- open++;
+ 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;
- wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+ 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 = NULL;
+ 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",
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);
- /* TODO: SAE H2E */
- if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+ 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, NULL, NULL);
- if (!password) {
- wpa_printf(MSG_DEBUG, "PASN: No SAE password found");
+ 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(hapd->own_addr, sta->addr,
- (const u8 *) password, os_strlen(password), 0,
- &pasn->sae);
+ 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",
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_SUCCESS);
+ 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, WPA_KDK_MAX_LEN);
+ 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",
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, WPA_KDK_MAX_LEN);
+ 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_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 &&
- elems.rsnxe && elems.rsnxe_len >= 1 &&
- (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_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;
}
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index d0074f6ba220..4bff9e591883 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,1150 +1,1095 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-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 "common/ieee802_11_defs.h"
#include "common/ocv.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "wpa_auth.h"
#include "ieee802_11.h"
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid)
{
u8 *pos = eid;
u32 timeout, tu;
struct os_reltime now, passed;
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
os_get_reltime(&now);
os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
if (hapd->conf->assoc_sa_query_max_timeout > tu)
timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
else
timeout = 0;
if (timeout < hapd->conf->assoc_sa_query_max_timeout)
timeout++; /* add some extra time for local timers */
WPA_PUT_LE32(pos, timeout);
pos += 4;
return pos;
}
/* MLME-SAQuery.request */
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
#ifdef CONFIG_OCV
struct sta_info *sta;
#endif /* CONFIG_OCV */
struct ieee80211_mgmt *mgmt;
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
MACSTR, MAC2STR(addr));
wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
sta = ap_get_sta(hapd, addr);
if (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 for OCI element in SA Query Request");
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->oci_freq_override_saquery_req) {
wpa_printf(MSG_INFO,
"TEST: Override OCI frequency %d -> %u MHz",
ci.frequency,
hapd->conf->oci_freq_override_saquery_req);
ci.frequency =
hapd->conf->oci_freq_override_saquery_req;
}
#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 SA Query Request");
return;
}
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
os_free(oci_ie);
return;
}
}
#endif /* CONFIG_OCV */
mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
if (!mgmt) {
wpa_printf(MSG_DEBUG,
"Failed to allocate buffer for SA Query Response frame");
os_free(oci_ie);
return;
}
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
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.action.category = WLAN_ACTION_SA_QUERY;
mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
end = mgmt->u.action.u.sa_query_req.variable;
#ifdef CONFIG_OCV
if (oci_ie_len > 0) {
os_memcpy(end, oci_ie, oci_ie_len);
end += oci_ie_len;
}
#endif /* CONFIG_OCV */
if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0, NULL, 0, 0)
< 0)
wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
os_free(mgmt);
os_free(oci_ie);
}
static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
const u8 *sa, const u8 *trans_id)
{
struct sta_info *sta;
struct ieee80211_mgmt *resp;
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
MACSTR, MAC2STR(sa));
wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
sta = ap_get_sta(hapd, sa);
if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
"from unassociated STA " MACSTR, MAC2STR(sa));
return;
}
#ifdef CONFIG_OCV
if (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 SA Query Response");
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->oci_freq_override_saquery_resp) {
wpa_printf(MSG_INFO,
"TEST: Override OCI frequency %d -> %u MHz",
ci.frequency,
hapd->conf->oci_freq_override_saquery_resp);
ci.frequency =
hapd->conf->oci_freq_override_saquery_resp;
}
#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 for OCI element in SA Query Response");
return;
}
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
os_free(oci_ie);
return;
}
}
#endif /* CONFIG_OCV */
resp = os_zalloc(sizeof(*resp) + oci_ie_len);
if (!resp) {
wpa_printf(MSG_DEBUG,
"Failed to allocate buffer for SA Query Response frame");
os_free(oci_ie);
return;
}
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(resp->da, 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 = WLAN_ACTION_SA_QUERY;
resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
end = resp->u.action.u.sa_query_req.variable;
#ifdef CONFIG_OCV
if (oci_ie_len > 0) {
os_memcpy(end, oci_ie, oci_ie_len);
end += oci_ie_len;
}
#endif /* CONFIG_OCV */
if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0, NULL, 0, 0)
< 0)
wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
os_free(resp);
os_free(oci_ie);
}
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len)
{
struct sta_info *sta;
int i;
const u8 *sa = mgmt->sa;
const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
if (((const u8 *) mgmt) + len <
mgmt->u.action.u.sa_query_resp.variable) {
wpa_printf(MSG_DEBUG,
"IEEE 802.11: Too short SA Query Action frame (len=%lu)",
(unsigned long) len);
return;
}
if (is_multicast_ether_addr(mgmt->da)) {
wpa_printf(MSG_DEBUG,
"IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")",
MAC2STR(mgmt->da), MAC2STR(mgmt->sa));
return;
}
sta = ap_get_sta(hapd, sa);
#ifdef CONFIG_OCV
if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct ieee802_11_elems elems;
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
size_t ies_len;
const u8 *ies;
ies = mgmt->u.action.u.sa_query_resp.variable;
ies_len = len - (ies - (u8 *) mgmt);
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
ParseFailed) {
wpa_printf(MSG_DEBUG,
"SA Query: Failed to parse elements");
return;
}
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in SA Query Action frame");
return;
}
if (get_sta_tx_parameters(sta->wpa_sm,
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_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
MACSTR " frame=saquery%s error=%s",
MAC2STR(sa),
action_type == WLAN_SA_QUERY_REQUEST ?
"req" : "resp", ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (action_type == WLAN_SA_QUERY_REQUEST) {
if (sta)
sta->post_csa_sa_query = 0;
ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
return;
}
if (action_type != WLAN_SA_QUERY_RESPONSE) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
"Action %d", action_type);
return;
}
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
MACSTR, MAC2STR(sa));
wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
/* MLME-SAQuery.confirm */
if (sta == NULL || sta->sa_query_trans_id == NULL) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
"pending SA Query request found");
return;
}
for (i = 0; i < sta->sa_query_count; i++) {
if (os_memcmp(sta->sa_query_trans_id +
i * WLAN_SA_QUERY_TR_ID_LEN,
trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
break;
}
if (i >= sta->sa_query_count) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
"transaction identifier found");
return;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Reply to pending SA Query received");
ap_sta_stop_sa_query(hapd, sta);
}
static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
{
*pos = 0x00;
switch (idx) {
case 0: /* Bits 0-7 */
if (hapd->iconf->obss_interval)
*pos |= 0x01; /* Bit 0 - Coexistence management */
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
*pos |= 0x04; /* Bit 2 - Extended Channel Switching */
break;
case 1: /* Bits 8-15 */
if (hapd->conf->proxy_arp)
*pos |= 0x10; /* Bit 12 - Proxy ARP */
if (hapd->conf->coloc_intf_reporting) {
/* Bit 13 - Collocated Interference Reporting */
*pos |= 0x20;
}
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
if (hapd->conf->bss_transition)
*pos |= 0x08; /* Bit 19 - BSS Transition */
break;
case 3: /* Bits 24-31 */
#ifdef CONFIG_WNM_AP
*pos |= 0x02; /* Bit 25 - SSID List */
#endif /* CONFIG_WNM_AP */
if (hapd->conf->time_advertisement == 2)
*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
if (hapd->conf->interworking)
*pos |= 0x80; /* Bit 31 - Interworking */
break;
case 4: /* Bits 32-39 */
if (hapd->conf->qos_map_set_len)
*pos |= 0x01; /* Bit 32 - QoS Map */
if (hapd->conf->tdls & TDLS_PROHIBIT)
*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
/* Bit 39 - TDLS Channel Switching Prohibited */
*pos |= 0x80;
}
break;
case 5: /* Bits 40-47 */
#ifdef CONFIG_HS20
if (hapd->conf->hs20)
*pos |= 0x40; /* Bit 46 - WNM-Notification */
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
if (hapd->conf->mbo_enabled)
*pos |= 0x40; /* Bit 46 - WNM-Notification */
#endif /* CONFIG_MBO */
break;
case 6: /* Bits 48-55 */
if (hapd->conf->ssid.utf8_ssid)
*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
break;
case 7: /* Bits 56-63 */
break;
case 8: /* Bits 64-71 */
if (hapd->conf->ftm_responder)
*pos |= 0x40; /* Bit 70 - FTM responder */
if (hapd->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
break;
case 9: /* Bits 72-79 */
#ifdef CONFIG_FILS
if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
*pos |= 0x01;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax &&
hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
*pos |= 0x40; /* Bit 78 - TWT responder */
#endif /* CONFIG_IEEE80211AX */
break;
case 10: /* Bits 80-87 */
#ifdef CONFIG_SAE
if (hapd->conf->wpa &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
if (in_use)
*pos |= 0x02; /* Bit 81 - SAE Password
* Identifiers In Use */
if (in_use == 2)
*pos |= 0x04; /* Bit 82 - SAE Password
* Identifiers Used Exclusively */
}
#endif /* CONFIG_SAE */
if (hapd->conf->beacon_prot &&
(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_BEACON_PROTECTION))
*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
break;
case 11: /* Bits 88-95 */
#ifdef CONFIG_SAE_PK
if (hapd->conf->wpa &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
hostapd_sae_pk_exclusively(hapd->conf))
*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
#endif /* CONFIG_SAE_PK */
break;
}
}
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
- u8 len = 0, i;
-
- if (hapd->conf->qos_map_set_len ||
- (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)))
- len = 5;
- if (len < 4 &&
- (hapd->conf->time_advertisement == 2 || hapd->conf->interworking))
- len = 4;
- if (len < 3 &&
- (hapd->conf->wnm_sleep_mode || hapd->conf->bss_transition))
- len = 3;
- if (len < 1 &&
- (hapd->iconf->obss_interval ||
- (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)))
- len = 1;
- if (len < 2 &&
- (hapd->conf->proxy_arp || hapd->conf->coloc_intf_reporting))
- len = 2;
- if (len < 7 && hapd->conf->ssid.utf8_ssid)
- len = 7;
- if (len < 9 &&
- (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
- len = 9;
-#ifdef CONFIG_WNM_AP
- if (len < 4)
- len = 4;
-#endif /* CONFIG_WNM_AP */
-#ifdef CONFIG_HS20
- if (hapd->conf->hs20 && len < 6)
- len = 6;
-#endif /* CONFIG_HS20 */
-#ifdef CONFIG_MBO
- if (hapd->conf->mbo_enabled && len < 6)
- len = 6;
-#endif /* CONFIG_MBO */
-#ifdef CONFIG_FILS
- if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
- !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
- len = 10;
-#endif /* CONFIG_FILS */
-#ifdef CONFIG_IEEE80211AX
- if (len < 10 && hapd->iconf->ieee80211ax &&
- hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
- len = 10;
-#endif /* CONFIG_IEEE80211AX */
-#ifdef CONFIG_SAE
- if (len < 11 && hapd->conf->wpa &&
- wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
- hostapd_sae_pw_id_in_use(hapd->conf))
- len = 11;
-#endif /* CONFIG_SAE */
- if (len < 11 && hapd->conf->beacon_prot &&
- (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
- len = 11;
-#ifdef CONFIG_SAE_PK
- if (len < 12 && hapd->conf->wpa &&
- wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
- hostapd_sae_pk_exclusively(hapd->conf))
- len = 12;
-#endif /* CONFIG_SAE_PK */
+ u8 len = EXT_CAPA_MAX_LEN, i;
+
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
- if (len == 0)
- return eid;
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
hostapd_ext_capab_byte(hapd, pos, i);
if (i < hapd->iface->extended_capa_len) {
*pos &= ~hapd->iface->extended_capa_mask[i];
*pos |= hapd->iface->extended_capa[i];
}
+
+ if (i < EXT_CAPA_MAX_LEN) {
+ *pos &= ~hapd->conf->ext_capa_mask[i];
+ *pos |= hapd->conf->ext_capa[i];
+ }
}
while (len > 0 && eid[1 + len] == 0) {
len--;
eid[1] = len;
}
if (len == 0)
return eid;
return eid + 2 + len;
}
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
u8 len = hapd->conf->qos_map_set_len;
if (!len)
return eid;
*pos++ = WLAN_EID_QOS_MAP_SET;
*pos++ = len;
os_memcpy(pos, hapd->conf->qos_map_set, len);
pos += len;
return pos;
}
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
#ifdef CONFIG_INTERWORKING
u8 *len;
if (!hapd->conf->interworking)
return eid;
*pos++ = WLAN_EID_INTERWORKING;
len = pos++;
*pos = hapd->conf->access_network_type;
if (hapd->conf->internet)
*pos |= INTERWORKING_ANO_INTERNET;
if (hapd->conf->asra)
*pos |= INTERWORKING_ANO_ASRA;
if (hapd->conf->esr)
*pos |= INTERWORKING_ANO_ESR;
if (hapd->conf->uesa)
*pos |= INTERWORKING_ANO_UESA;
pos++;
if (hapd->conf->venue_info_set) {
*pos++ = hapd->conf->venue_group;
*pos++ = hapd->conf->venue_type;
}
if (!is_zero_ether_addr(hapd->conf->hessid)) {
os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
pos += ETH_ALEN;
}
*len = pos - len - 1;
#endif /* CONFIG_INTERWORKING */
return pos;
}
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
#ifdef CONFIG_INTERWORKING
/* TODO: Separate configuration for ANQP? */
if (!hapd->conf->interworking)
return eid;
*pos++ = WLAN_EID_ADV_PROTO;
*pos++ = 2;
*pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
*pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
#endif /* CONFIG_INTERWORKING */
return pos;
}
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
#ifdef CONFIG_INTERWORKING
u8 *len;
unsigned int i, count;
if (!hapd->conf->interworking ||
hapd->conf->roaming_consortium == NULL ||
hapd->conf->roaming_consortium_count == 0)
return eid;
*pos++ = WLAN_EID_ROAMING_CONSORTIUM;
len = pos++;
/* Number of ANQP OIs (in addition to the max 3 listed here) */
if (hapd->conf->roaming_consortium_count > 3 + 255)
*pos++ = 255;
else if (hapd->conf->roaming_consortium_count > 3)
*pos++ = hapd->conf->roaming_consortium_count - 3;
else
*pos++ = 0;
/* OU #1 and #2 Lengths */
*pos = hapd->conf->roaming_consortium[0].len;
if (hapd->conf->roaming_consortium_count > 1)
*pos |= hapd->conf->roaming_consortium[1].len << 4;
pos++;
if (hapd->conf->roaming_consortium_count > 3)
count = 3;
else
count = hapd->conf->roaming_consortium_count;
for (i = 0; i < count; i++) {
os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
hapd->conf->roaming_consortium[i].len);
pos += hapd->conf->roaming_consortium[i].len;
}
*len = pos - len - 1;
#endif /* CONFIG_INTERWORKING */
return pos;
}
u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
{
if (hapd->conf->time_advertisement != 2)
return eid;
if (hapd->time_adv == NULL &&
hostapd_update_time_adv(hapd) < 0)
return eid;
if (hapd->time_adv == NULL)
return eid;
os_memcpy(eid, wpabuf_head(hapd->time_adv),
wpabuf_len(hapd->time_adv));
eid += wpabuf_len(hapd->time_adv);
return eid;
}
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
{
size_t len;
if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
return eid;
len = os_strlen(hapd->conf->time_zone);
*eid++ = WLAN_EID_TIME_ZONE;
*eid++ = len;
os_memcpy(eid, hapd->conf->time_zone, len);
eid += len;
return eid;
}
int hostapd_update_time_adv(struct hostapd_data *hapd)
{
const int elen = 2 + 1 + 10 + 5 + 1;
struct os_time t;
struct os_tm tm;
u8 *pos;
if (hapd->conf->time_advertisement != 2)
return 0;
if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
return -1;
if (!hapd->time_adv) {
hapd->time_adv = wpabuf_alloc(elen);
if (hapd->time_adv == NULL)
return -1;
pos = wpabuf_put(hapd->time_adv, elen);
} else
pos = wpabuf_mhead_u8(hapd->time_adv);
*pos++ = WLAN_EID_TIME_ADVERTISEMENT;
*pos++ = 1 + 10 + 5 + 1;
*pos++ = 2; /* UTC time at which the TSF timer is 0 */
/* Time Value at TSF 0 */
/* FIX: need to calculate this based on the current TSF value */
WPA_PUT_LE16(pos, tm.year); /* Year */
pos += 2;
*pos++ = tm.month; /* Month */
*pos++ = tm.day; /* Day of month */
*pos++ = tm.hour; /* Hours */
*pos++ = tm.min; /* Minutes */
*pos++ = tm.sec; /* Seconds */
WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
pos += 2;
*pos++ = 0; /* Reserved */
/* Time Error */
/* TODO: fill in an estimate on the error */
*pos++ = 0;
*pos++ = 0;
*pos++ = 0;
*pos++ = 0;
*pos++ = 0;
*pos++ = hapd->time_update_counter++;
return 0;
}
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
#ifdef CONFIG_WNM_AP
if (hapd->conf->ap_max_inactivity > 0) {
unsigned int val;
*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
*pos++ = 3;
val = hapd->conf->ap_max_inactivity;
if (val > 68000)
val = 68000;
val *= 1000;
val /= 1024;
if (val == 0)
val = 1;
if (val > 65535)
val = 65535;
WPA_PUT_LE16(pos, val);
pos += 2;
*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
}
#endif /* CONFIG_WNM_AP */
return pos;
}
#ifdef CONFIG_MBO
u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
size_t len, int delta)
{
u8 mbo[4];
mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
mbo[1] = 2;
/* Delta RSSI */
mbo[2] = delta;
/* Retry delay */
mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
return eid + mbo_add_ie(eid, len, mbo, 4);
}
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 mbo[9], *mbo_pos = mbo;
u8 *pos = eid;
if (!hapd->conf->mbo_enabled &&
!OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return eid;
if (hapd->conf->mbo_enabled) {
*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
*mbo_pos++ = 1;
/* Not Cellular aware */
*mbo_pos++ = 0;
}
if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
*mbo_pos++ = 1;
*mbo_pos++ = hapd->mbo_assoc_disallow;
}
if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
u8 ctrl;
ctrl = OCE_RELEASE;
if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
ctrl |= OCE_IS_STA_CFON;
*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
*mbo_pos++ = 1;
*mbo_pos++ = ctrl;
}
pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
return pos;
}
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
{
u8 len;
if (!hapd->conf->mbo_enabled &&
!OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return 0;
/*
* MBO IE header (6) + Capability Indication attribute (3) +
* Association Disallowed attribute (3) = 12
*/
len = 6;
if (hapd->conf->mbo_enabled)
len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
/* OCE capability indication attribute (3) */
if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
len += 3;
return len;
}
#endif /* CONFIG_MBO */
#ifdef CONFIG_OWE
static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
{
return hapd->conf->owe_transition_ssid_len > 0 &&
!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
}
#endif /* CONFIG_OWE */
size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
{
#ifdef CONFIG_OWE
if (!hostapd_eid_owe_trans_enabled(hapd))
return 0;
return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
#else /* CONFIG_OWE */
return 0;
#endif /* CONFIG_OWE */
}
u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
size_t len)
{
#ifdef CONFIG_OWE
u8 *pos = eid;
size_t elen;
if (hapd->conf->owe_transition_ifname[0] &&
!hostapd_eid_owe_trans_enabled(hapd))
hostapd_owe_trans_get_info(hapd);
if (!hostapd_eid_owe_trans_enabled(hapd))
return pos;
elen = hostapd_eid_owe_trans_len(hapd);
if (len < elen) {
wpa_printf(MSG_DEBUG,
"OWE: Not enough room in the buffer for OWE IE");
return pos;
}
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = elen - 2;
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = OWE_OUI_TYPE;
os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
pos += ETH_ALEN;
*pos++ = hapd->conf->owe_transition_ssid_len;
os_memcpy(pos, hapd->conf->owe_transition_ssid,
hapd->conf->owe_transition_ssid_len);
pos += hapd->conf->owe_transition_ssid_len;
return pos;
#else /* CONFIG_OWE */
return eid;
#endif /* CONFIG_OWE */
}
size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
{
#ifdef CONFIG_DPP2
if (hapd->conf->dpp_configurator_connectivity)
return 6;
#endif /* CONFIG_DPP2 */
return 0;
}
u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
#ifdef CONFIG_DPP2
if (!hapd->conf->dpp_configurator_connectivity || len < 6)
return pos;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4;
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = DPP_CC_OUI_TYPE;
#endif /* CONFIG_DPP2 */
return pos;
}
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len)
{
if (!supp_op_classes)
return;
os_free(sta->supp_op_classes);
sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
if (!sta->supp_op_classes)
return;
sta->supp_op_classes[0] = supp_op_classes_len;
os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
supp_op_classes_len);
}
u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
{
u8 *pos = eid;
#ifdef CONFIG_FILS
u8 *len;
u16 fils_info = 0;
size_t realms;
struct fils_realm *realm;
if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
!wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
return pos;
realms = dl_list_len(&hapd->conf->fils_realms);
if (realms > 7)
realms = 7; /* 3 bit count field limits this to max 7 */
*pos++ = WLAN_EID_FILS_INDICATION;
len = pos++;
/* TODO: B0..B2: Number of Public Key Identifiers */
if (hapd->conf->erp_domain) {
/* B3..B5: Number of Realm Identifiers */
fils_info |= realms << 3;
}
/* TODO: B6: FILS IP Address Configuration */
if (hapd->conf->fils_cache_id_set)
fils_info |= BIT(7);
if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
fils_info |= BIT(8); /* HESSID Included */
/* FILS Shared Key Authentication without PFS Supported */
fils_info |= BIT(9);
if (hapd->conf->fils_dh_group) {
/* FILS Shared Key Authentication with PFS Supported */
fils_info |= BIT(10);
}
/* TODO: B11: FILS Public Key Authentication Supported */
/* B12..B15: Reserved */
WPA_PUT_LE16(pos, fils_info);
pos += 2;
if (hapd->conf->fils_cache_id_set) {
os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
pos += FILS_CACHE_ID_LEN;
}
if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
pos += ETH_ALEN;
}
dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
list) {
if (realms == 0)
break;
realms--;
os_memcpy(pos, realm->hash, 2);
pos += 2;
}
*len = pos - len - 1;
#endif /* CONFIG_FILS */
return pos;
}
#ifdef CONFIG_OCV
int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
int ap_seg1_idx, int *bandwidth, int *seg1_idx)
{
int ht_40mhz = 0;
int vht_80p80 = 0;
int requested_bw;
if (sta->ht_capabilities)
ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
if (sta->vht_operation) {
struct ieee80211_vht_operation *oper = sta->vht_operation;
/*
* If a VHT Operation element was present, use it to determine
* the supported channel bandwidth.
*/
if (oper->vht_op_info_chwidth == 0) {
requested_bw = ht_40mhz ? 40 : 20;
} else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
requested_bw = 80;
} else {
int diff;
requested_bw = 160;
diff = abs((int)
oper->vht_op_info_chan_center_freq_seg0_idx -
(int)
oper->vht_op_info_chan_center_freq_seg1_idx);
vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
!= 0 && diff > 16;
}
} else if (sta->vht_capabilities) {
struct ieee80211_vht_capabilities *capab;
int vht_chanwidth;
capab = sta->vht_capabilities;
/*
* If only the VHT Capabilities element is present (e.g., for
* normal clients), use it to determine the supported channel
* bandwidth.
*/
vht_chanwidth = capab->vht_capabilities_info &
VHT_CAP_SUPP_CHAN_WIDTH_MASK;
vht_80p80 = capab->vht_capabilities_info &
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
/* TODO: Also take into account Extended NSS BW Support field */
requested_bw = vht_chanwidth ? 160 : 80;
} else {
requested_bw = ht_40mhz ? 40 : 20;
}
*bandwidth = requested_bw < ap_max_chanwidth ?
requested_bw : ap_max_chanwidth;
*seg1_idx = 0;
if (ap_seg1_idx && vht_80p80)
*seg1_idx = ap_seg1_idx;
return 0;
}
#endif /* CONFIG_OCV */
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
bool sae_pk = false;
u16 capab = 0;
size_t flen;
if (!(hapd->conf->wpa & WPA_PROTO_RSN))
return eid;
#ifdef CONFIG_SAE_PK
sae_pk = hostapd_sae_pk_in_use(hapd->conf);
#endif /* CONFIG_SAE_PK */
if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
(hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk) &&
hapd->conf->sae_pwe != 3) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sae_pk)
capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
#endif /* CONFIG_SAE_PK */
}
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
flen = (capab & 0xff00) ? 2 : 1;
if (len < 2 + flen || !capab)
return eid; /* no supported extended RSN capabilities */
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab)
*pos++ = capab;
return pos;
}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index efa48e7e3d8d..27e72f9a0164 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,411 +1,412 @@
/*
* hostapd / Station table
* 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.
*/
#ifndef STA_INFO_H
#define STA_INFO_H
#include "common/defs.h"
#include "list.h"
#include "vlan.h"
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/sha384.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
#define WLAN_STA_AUTHORIZED BIT(5)
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
#define WLAN_STA_SHORT_PREAMBLE BIT(7)
#define WLAN_STA_PREAUTH BIT(8)
#define WLAN_STA_WMM BIT(9)
#define WLAN_STA_MFP BIT(10)
#define WLAN_STA_HT BIT(11)
#define WLAN_STA_WPS BIT(12)
#define WLAN_STA_MAYBE_WPS BIT(13)
#define WLAN_STA_WDS BIT(14)
#define WLAN_STA_ASSOC_REQ_OK BIT(15)
#define WLAN_STA_WPS2 BIT(16)
#define WLAN_STA_GAS BIT(17)
#define WLAN_STA_VHT BIT(18)
#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_HE BIT(24)
#define WLAN_STA_6GHZ BIT(25)
#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended
* Supported Rates IEs). */
#define WLAN_SUPP_RATES_MAX 32
struct hostapd_data;
struct mbo_non_pref_chan_info {
struct mbo_non_pref_chan_info *next;
u8 op_class;
u8 pref;
u8 reason_code;
u8 num_channels;
u8 channels[];
};
struct pending_eapol_rx {
struct wpabuf *buf;
struct os_reltime rx_time;
};
enum pasn_fils_state {
PASN_FILS_STATE_NONE = 0,
PASN_FILS_STATE_PENDING_AS,
PASN_FILS_STATE_COMPLETE
};
struct pasn_fils_data {
u8 state;
u8 nonce[FILS_NONCE_LEN];
u8 anonce[FILS_NONCE_LEN];
u8 session[FILS_SESSION_LEN];
u8 erp_pmkid[PMKID_LEN];
struct wpabuf *erp_resp;
};
struct pasn_data {
int akmp;
int cipher;
u16 group;
u8 trans_seq;
u8 wrapped_data_format;
+ size_t kdk_len;
u8 hash[SHA384_MAC_LEN];
struct wpa_ptk ptk;
struct crypto_ecdh *ecdh;
struct wpabuf *secret;
#ifdef CONFIG_SAE
struct sae_data sae;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
struct pasn_fils_data fils;
#endif /* CONFIG_FILS */
};
struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
u8 addr[6];
be32 ipaddr;
struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
u16 disconnect_reason_code; /* RADIUS server override */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int supported_rates_len;
u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
#ifdef CONFIG_MESH
enum mesh_plink_state plink_state;
u16 peer_lid;
u16 my_lid;
u16 peer_aid;
u16 mpm_close_reason;
int mpm_retries;
u8 my_nonce[WPA_NONCE_LEN];
u8 peer_nonce[WPA_NONCE_LEN];
u8 aek[32]; /* SHA256 digest length */
u8 mtk[WPA_TK_MAX_LEN];
size_t mtk_len;
u8 mgtk_rsc[6];
u8 mgtk_key_id;
u8 mgtk[WPA_TK_MAX_LEN];
size_t mgtk_len;
u8 igtk_rsc[6];
u8 igtk[WPA_TK_MAX_LEN];
size_t igtk_len;
u16 igtk_key_id;
u8 sae_auth_retry;
#endif /* CONFIG_MESH */
unsigned int nonerp_set:1;
unsigned int no_short_slot_time_set:1;
unsigned int no_short_preamble_set:1;
unsigned int no_ht_gf_set:1;
unsigned int no_ht_set:1;
unsigned int ht40_intolerant_set:1;
unsigned int ht_20mhz_set:1;
unsigned int no_p2p_set:1;
unsigned int qos_map_enabled:1;
unsigned int remediation:1;
unsigned int hs20_deauth_requested:1;
unsigned int session_timeout_set:1;
unsigned int radius_das_match:1;
unsigned int ecsa_supported:1;
unsigned int added_unassoc:1;
unsigned int pending_wds_enable:1;
unsigned int power_capab:1;
unsigned int agreed_to_steer:1;
unsigned int hs20_t_c_filtering:1;
unsigned int ft_over_ds:1;
unsigned int external_dh_updated:1;
unsigned int post_csa_sa_query:1;
u16 auth_alg;
enum {
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
STA_DISASSOC_FROM_CLI
} timeout_next;
u16 deauth_reason;
u16 disassoc_reason;
/* IEEE 802.1X related data */
struct eapol_state_machine *eapol_sm;
struct pending_eapol_rx *pending_eapol_rx;
u64 acct_session_id;
struct os_reltime acct_session_start;
int acct_session_started;
int acct_terminate_cause; /* Acct-Terminate-Cause */
int acct_interim_interval; /* Acct-Interim-Interval */
unsigned int acct_interim_errors;
/* For extending 32-bit driver counters to 64-bit counters */
u32 last_rx_bytes_hi;
u32 last_rx_bytes_lo;
u32 last_tx_bytes_hi;
u32 last_tx_bytes_lo;
u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
struct wpa_state_machine *wpa_sm;
struct rsn_preauth_interface *preauth_iface;
int vlan_id; /* 0: none, >0: VID */
struct vlan_description *vlan_desc;
int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
/* PSKs from RADIUS authentication server */
struct hostapd_sta_wpa_psk_short *psk;
char *identity; /* User-Name from RADIUS */
char *radius_cui; /* Chargeable-User-Identity from RADIUS */
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
struct ieee80211_vht_operation *vht_operation;
u8 vht_opmode;
struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
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;
#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
u8 gas_dialog_next;
#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
struct wpabuf *roaming_consortium;
u8 remediation_method;
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
int hs20_disassoc_timer;
#ifdef CONFIG_FST
struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
#endif /* CONFIG_FST */
struct os_reltime connected_time;
#ifdef CONFIG_SAE
struct sae_data *sae;
unsigned int mesh_sae_pmksa_caching:1;
#endif /* CONFIG_SAE */
/* valid only if session_timeout_set == 1 */
struct os_reltime session_timeout;
/* Last Authentication/(Re)Association Request/Action frame sequence
* control */
u16 last_seq_ctrl;
/* Last Authentication/(Re)Association Request/Action frame subtype */
u8 last_subtype;
#ifdef CONFIG_MBO
u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
* enum mbo_cellular_capa values */
struct mbo_non_pref_chan_info *non_pref_chan;
int auth_rssi; /* Last Authentication frame RSSI */
#endif /* CONFIG_MBO */
u8 *supp_op_classes; /* Supported Operating Classes element, if
* received, starting from the Length field */
u8 rrm_enabled_capa[5];
s8 min_tx_power;
s8 max_tx_power;
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
struct wpabuf *assoc_ie_taxonomy;
#endif /* CONFIG_TAXONOMY */
#ifdef CONFIG_FILS
u8 fils_snonce[FILS_NONCE_LEN];
u8 fils_session[FILS_SESSION_LEN];
u8 fils_erp_pmkid[PMKID_LEN];
u8 *fils_pending_assoc_req;
size_t fils_pending_assoc_req_len;
unsigned int fils_pending_assoc_is_reassoc:1;
unsigned int fils_dhcp_rapid_commit_proxy:1;
unsigned int fils_erp_pmkid_set:1;
unsigned int fils_drv_assoc_finish:1;
struct wpabuf *fils_hlp_resp;
struct wpabuf *hlp_dhcp_discover;
void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
u16 resp, struct wpabuf *data, int pub);
#ifdef CONFIG_FILS_SK_PFS
struct crypto_ecdh *fils_ecdh;
#endif /* CONFIG_FILS_SK_PFS */
struct wpabuf *fils_dh_ss;
struct wpabuf *fils_g_sta;
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
u8 *owe_pmk;
size_t owe_pmk_len;
struct crypto_ecdh *owe_ecdh;
u16 owe_group;
#endif /* CONFIG_OWE */
u8 *ext_capability;
char *ifname_wds; /* WDS ifname, if in use */
#ifdef CONFIG_DPP2
struct dpp_pfs *dpp_pfs;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
enum wpa_alg last_tk_alg;
int last_tk_key_idx;
u8 last_tk[WPA_TK_MAX_LEN];
size_t last_tk_len;
u8 *sae_postponed_commit;
size_t sae_postponed_commit_len;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_AIRTIME_POLICY
unsigned int airtime_weight;
struct os_reltime backlogged_until;
#endif /* CONFIG_AIRTIME_POLICY */
#ifdef CONFIG_PASN
struct pasn_data *pasn;
#endif /* CONFIG_PASN */
};
/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
* passed since last received frame from the station, a nullfunc data frame is
* sent to the station. If this frame is not acknowledged and no other frames
* have been received, the station will be disassociated after
* AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
* after AP_DEAUTH_DELAY seconds has passed after disassociation. */
#define AP_MAX_INACTIVITY (5 * 60)
#define AP_DISASSOC_DELAY (3)
#define AP_DEAUTH_DELAY (1)
/* Number of seconds to keep STA entry with Authenticated flag after it has
* been disassociated. */
#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
/* Number of seconds to keep STA entry after it has been deauthenticated. */
#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx),
void *ctx);
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_free_stas(struct hostapd_data *hapd);
void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
u32 session_timeout);
void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
u32 session_timeout);
void ap_sta_no_session_timeout(struct hostapd_data *hapd,
struct sta_info *sta);
void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
struct sta_info *sta, int warning_time);
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason);
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason);
#ifdef CONFIG_WPS
int ap_sta_wps_cancel(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx);
#endif /* CONFIG_WPS */
int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
struct vlan_description *vlan_desc);
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
void ap_sta_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
static inline int ap_sta_is_authorized(struct sta_info *sta)
{
return sta->flags & WLAN_STA_AUTHORIZED;
}
void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* STA_INFO_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 6c791e26b0b7..59cd46aa4601 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,5661 +1,5659 @@
/*
* 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;
}
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);
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 &&
- sm->rsnxe && sm->rsnxe_len >= 4 &&
- sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ 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 &&
- sm->rsnxe && sm->rsnxe_len >= 4 &&
- sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ 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];
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) {
wpabuf_clear_free(plain);
return NULL;
}
gtk = dummy_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];
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)
goto done;
gtk = dummy_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");
#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];
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)
return;
gtk = dummy_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->IGTK[gsm->GN_bigtk - 6], len);
+ 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);
}
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/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 32b745651ace..e80086b93d8d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,4813 +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 */
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];
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) {
os_free(subelem);
return NULL;
}
igtk = dummy_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];
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) {
os_free(subelem);
return NULL;
}
bigtk = dummy_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 &&
- sm->rsnxe && sm->rsnxe_len >= 4 &&
- sm->rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ 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
* 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/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 50ce1923c120..8aba713f92ba 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -1,827 +1,794 @@
/*
* common module tests
* Copyright (c) 2014-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/module_tests.h"
#include "crypto/crypto.h"
#include "crypto/dh_groups.h"
#include "ieee802_11_common.h"
#include "ieee802_11_defs.h"
#include "gas.h"
#include "wpa_common.h"
#include "sae.h"
struct ieee802_11_parse_test_data {
u8 *data;
size_t len;
ParseRes result;
int count;
};
static const struct ieee802_11_parse_test_data parse_tests[] = {
{ (u8 *) "", 0, ParseOK, 0 },
{ (u8 *) " ", 1, ParseFailed, 0 },
{ (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
{ (u8 *) "\xff\x01", 2, ParseFailed, 0 },
{ (u8 *) "\xdd\x03\x01\x02\x03", 5, ParseUnknown, 1 },
{ (u8 *) "\xdd\x04\x01\x02\x03\x04", 6, ParseUnknown, 1 },
{ (u8 *) "\xdd\x04\x00\x50\xf2\x02", 6, ParseUnknown, 1 },
{ (u8 *) "\xdd\x05\x00\x50\xf2\x02\x02", 7, ParseOK, 1 },
{ (u8 *) "\xdd\x05\x00\x50\xf2\x02\xff", 7, ParseUnknown, 1 },
{ (u8 *) "\xdd\x04\x00\x50\xf2\xff", 6, ParseUnknown, 1 },
{ (u8 *) "\xdd\x04\x50\x6f\x9a\xff", 6, ParseUnknown, 1 },
{ (u8 *) "\xdd\x04\x00\x90\x4c\x33", 6, ParseOK, 1 },
{ (u8 *) "\xdd\x04\x00\x90\x4c\xff\xdd\x04\x00\x90\x4c\x33", 12,
ParseUnknown, 2 },
{ (u8 *) "\x10\x01\x00\x21\x00", 5, ParseOK, 2 },
{ (u8 *) "\x24\x00", 2, ParseOK, 1 },
{ (u8 *) "\x38\x00", 2, ParseOK, 1 },
{ (u8 *) "\x54\x00", 2, ParseOK, 1 },
{ (u8 *) "\x5a\x00", 2, ParseOK, 1 },
{ (u8 *) "\x65\x00", 2, ParseOK, 1 },
{ (u8 *) "\x65\x12\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11",
20, ParseOK, 1 },
{ (u8 *) "\x6e\x00", 2, ParseOK, 1 },
{ (u8 *) "\xc7\x00", 2, ParseOK, 1 },
{ (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 },
{ (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00",
18, ParseOK, 9 },
{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
{ (u8 *) "\xed\x00", 2, ParseOK, 1 },
{ (u8 *) "\xef\x00", 2, ParseOK, 1 },
{ (u8 *) "\xef\x01\x11", 3, ParseOK, 1 },
{ (u8 *) "\xf0\x00", 2, ParseOK, 1 },
{ (u8 *) "\xf1\x00", 2, ParseOK, 1 },
{ (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 },
{ (u8 *) "\xf2\x00", 2, ParseOK, 1 },
{ (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
{ (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 },
{ (u8 *) "\xff\x01\x01", 3, ParseOK, 1 },
{ (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 },
{ (u8 *) "\xff\x01\x02", 3, ParseOK, 1 },
{ (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 },
{ (u8 *) "\xff\x01\x04", 3, ParseOK, 1 },
{ (u8 *) "\xff\x01\x05", 3, ParseOK, 1 },
{ (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55",
15, ParseOK, 1 },
{ (u8 *) "\xff\x01\x06", 3, ParseOK, 1 },
{ (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 },
{ (u8 *) "\xff\x01\x07", 3, ParseOK, 1 },
{ (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11,
ParseOK, 1 },
{ (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 },
{ (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 },
{ (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 },
{ NULL, 0, ParseOK, 0 }
};
static int ieee802_11_parse_tests(void)
{
int i, ret = 0;
struct wpabuf *buf;
wpa_printf(MSG_INFO, "ieee802_11_parse tests");
for (i = 0; parse_tests[i].data; i++) {
const struct ieee802_11_parse_test_data *test;
struct ieee802_11_elems elems;
ParseRes res;
test = &parse_tests[i];
res = ieee802_11_parse_elems(test->data, test->len, &elems, 1);
if (res != test->result ||
ieee802_11_ie_count(test->data, test->len) != test->count) {
wpa_printf(MSG_ERROR, "ieee802_11_parse test %d failed",
i);
ret = -1;
}
}
if (ieee802_11_vendor_ie_concat((const u8 *) "\x00\x01", 2, 0) != NULL)
{
wpa_printf(MSG_ERROR,
"ieee802_11_vendor_ie_concat test failed");
ret = -1;
}
buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01",
16, 0x11223344);
do {
const u8 *pos;
if (!buf) {
wpa_printf(MSG_ERROR,
"ieee802_11_vendor_ie_concat test 2 failed");
ret = -1;
break;
}
if (wpabuf_len(buf) != 2) {
wpa_printf(MSG_ERROR,
"ieee802_11_vendor_ie_concat test 3 failed");
ret = -1;
break;
}
pos = wpabuf_head(buf);
if (pos[0] != 0x01 || pos[1] != 0x02) {
wpa_printf(MSG_ERROR,
"ieee802_11_vendor_ie_concat test 3 failed");
ret = -1;
break;
}
} while (0);
wpabuf_free(buf);
return ret;
}
struct rsn_ie_parse_test_data {
u8 *data;
size_t len;
int result;
};
static const struct rsn_ie_parse_test_data rsn_parse_tests[] = {
{ (u8 *) "", 0, -1 },
{ (u8 *) "\x30\x00", 2, -1 },
{ (u8 *) "\x30\x02\x01\x00", 4, 0 },
{ (u8 *) "\x30\x02\x00\x00", 4, -2 },
{ (u8 *) "\x30\x02\x02\x00", 4, -2 },
{ (u8 *) "\x30\x02\x00\x01", 4, -2 },
{ (u8 *) "\x30\x02\x00\x00\x00", 5, -2 },
{ (u8 *) "\x30\x03\x01\x00\x00", 5, -3 },
{ (u8 *) "\x30\x06\x01\x00\x00\x00\x00\x00", 8, -1 },
{ (u8 *) "\x30\x06\x01\x00\x00\x0f\xac\x04", 8, 0 },
{ (u8 *) "\x30\x07\x01\x00\x00\x0f\xac\x04\x00", 9, -5 },
{ (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x00", 10, -4 },
{ (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x01", 10, -4 },
{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04",
14, 0 },
{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x00\x01\x00\x0f\xac\x04",
14, -4 },
{ (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x06",
14, -1 },
{ (u8 *) "\x30\x10\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x08",
18, 0 },
{ (u8 *) "\x30\x0d\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00",
15, -7 },
{ (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x00",
16, -6 },
{ (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x01",
16, -6 },
{ (u8 *) "\x30\x12\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01",
20, 0 },
{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x01\x00\x0f\xac\x02",
24, 0 },
{ (u8 *) "\x30\x13\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00",
21, 0 },
{ (u8 *) "\x30\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00",
22, 0 },
{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00",
24, 0 },
{ (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x01",
24, -9 },
{ (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x00\x00\x00",
28, -10 },
{ (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06",
28, 0 },
{ (u8 *) "\x30\x1c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06\x01\x02",
30, 0 },
{ NULL, 0, 0 }
};
static int rsn_ie_parse_tests(void)
{
int i, ret = 0;
wpa_printf(MSG_INFO, "rsn_ie_parse tests");
for (i = 0; rsn_parse_tests[i].data; i++) {
const struct rsn_ie_parse_test_data *test;
struct wpa_ie_data data;
test = &rsn_parse_tests[i];
if (wpa_parse_wpa_ie_rsn(test->data, test->len, &data) !=
test->result) {
wpa_printf(MSG_ERROR, "rsn_ie_parse test %d failed", i);
ret = -1;
}
}
return ret;
}
static int gas_tests(void)
{
struct wpabuf *buf;
wpa_printf(MSG_INFO, "gas tests");
gas_anqp_set_len(NULL);
buf = wpabuf_alloc(1);
if (buf == NULL)
return -1;
gas_anqp_set_len(buf);
wpabuf_free(buf);
buf = wpabuf_alloc(20);
if (buf == NULL)
return -1;
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
wpabuf_put_u8(buf, 0);
wpabuf_put_be32(buf, 0);
wpabuf_put_u8(buf, 0);
gas_anqp_set_len(buf);
wpabuf_free(buf);
return 0;
}
static int sae_tests(void)
{
#ifdef CONFIG_SAE
struct sae_data sae;
int ret = -1;
- /* IEEE P802.11-REVmd/D2.1, Annex J.10 */
- const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
- const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
+ /* IEEE Std 802.11-2020, Annex J.10 */
+ const u8 addr1[ETH_ALEN] = { 0x4d, 0x3f, 0x2f, 0xff, 0xe3, 0x87 };
+ const u8 addr2[ETH_ALEN] = { 0xa5, 0xd8, 0xaa, 0x95, 0x8e, 0x3c };
const char *ssid = "byteme";
const char *pw = "mekmitasdigoat";
const char *pwid = "psk4internet";
const u8 local_rand[] = {
- 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e,
- 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd,
- 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5,
- 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf
+ 0x99, 0x24, 0x65, 0xfd, 0x3d, 0xaa, 0x3c, 0x60,
+ 0xaa, 0x65, 0x65, 0xb7, 0xf6, 0x2a, 0x2a, 0x7f,
+ 0x2e, 0x12, 0xdd, 0x12, 0xf1, 0x98, 0xfa, 0xf4,
+ 0xfb, 0xed, 0x89, 0xd7, 0xff, 0x1a, 0xce, 0x94
};
const u8 local_mask[] = {
- 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c,
- 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4,
- 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e,
- 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15
+ 0x95, 0x07, 0xa9, 0x0f, 0x77, 0x7a, 0x04, 0x4d,
+ 0x6a, 0x08, 0x30, 0xb9, 0x1e, 0xa3, 0xd5, 0xdd,
+ 0x70, 0xbe, 0xce, 0x44, 0xe1, 0xac, 0xff, 0xb8,
+ 0x69, 0x83, 0xb5, 0xe1, 0xbf, 0x9f, 0xb3, 0x22
};
const u8 local_commit[] = {
- 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4,
- 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39,
- 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0,
- 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4,
- 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58,
- 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25,
- 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5,
- 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61,
- 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4,
- 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98,
- 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88,
- 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52,
- 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
- 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
- 0x74
+ 0x13, 0x00, 0x2e, 0x2c, 0x0f, 0x0d, 0xb5, 0x24,
+ 0x40, 0xad, 0x14, 0x6d, 0x96, 0x71, 0x14, 0xce,
+ 0x00, 0x5c, 0xe1, 0xea, 0xb0, 0xaa, 0x2c, 0x2e,
+ 0x5c, 0x28, 0x71, 0xb7, 0x74, 0xf6, 0xc2, 0x57,
+ 0x5c, 0x65, 0xd5, 0xad, 0x9e, 0x00, 0x82, 0x97,
+ 0x07, 0xaa, 0x36, 0xba, 0x8b, 0x85, 0x97, 0x38,
+ 0xfc, 0x96, 0x1d, 0x08, 0x24, 0x35, 0x05, 0xf4,
+ 0x7c, 0x03, 0x53, 0x76, 0xd7, 0xac, 0x4b, 0xc8,
+ 0xd7, 0xb9, 0x50, 0x83, 0xbf, 0x43, 0x82, 0x7d,
+ 0x0f, 0xc3, 0x1e, 0xd7, 0x78, 0xdd, 0x36, 0x71,
+ 0xfd, 0x21, 0xa4, 0x6d, 0x10, 0x91, 0xd6, 0x4b,
+ 0x6f, 0x9a, 0x1e, 0x12, 0x72, 0x62, 0x13, 0x25,
+ 0xdb, 0xe1
};
const u8 peer_commit[] = {
- 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea,
- 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70,
- 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55,
- 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2,
- 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9,
- 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d,
- 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38,
- 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f,
- 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc,
- 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf,
- 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf,
- 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62,
- 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
- 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
- 0x74
+ 0x13, 0x00, 0x59, 0x1b, 0x96, 0xf3, 0x39, 0x7f,
+ 0xb9, 0x45, 0x10, 0x08, 0x48, 0xe7, 0xb5, 0x50,
+ 0x54, 0x3b, 0x67, 0x20, 0xd8, 0x83, 0x37, 0xee,
+ 0x93, 0xfc, 0x49, 0xfd, 0x6d, 0xf7, 0xe0, 0x8b,
+ 0x52, 0x23, 0xe7, 0x1b, 0x9b, 0xb0, 0x48, 0xd3,
+ 0x87, 0x3f, 0x20, 0x55, 0x69, 0x53, 0xa9, 0x6c,
+ 0x91, 0x53, 0x6f, 0xd8, 0xee, 0x6c, 0xa9, 0xb4,
+ 0xa6, 0x8a, 0x14, 0x8b, 0x05, 0x6a, 0x90, 0x9b,
+ 0xe0, 0x3e, 0x83, 0xae, 0x20, 0x8f, 0x60, 0xf8,
+ 0xef, 0x55, 0x37, 0x85, 0x80, 0x74, 0xdb, 0x06,
+ 0x68, 0x70, 0x32, 0x39, 0x98, 0x62, 0x99, 0x9b,
+ 0x51, 0x1e, 0x0a, 0x15, 0x52, 0xa5, 0xfe, 0xa3,
+ 0x17, 0xc2
};
const u8 kck[] = {
- 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8,
- 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94,
- 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3,
- 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed
+ 0x1e, 0x73, 0x3f, 0x6d, 0x9b, 0xd5, 0x32, 0x56,
+ 0x28, 0x73, 0x04, 0x33, 0x88, 0x31, 0xb0, 0x9a,
+ 0x39, 0x40, 0x6d, 0x12, 0x10, 0x17, 0x07, 0x3a,
+ 0x5c, 0x30, 0xdb, 0x36, 0xf3, 0x6c, 0xb8, 0x1a
};
const u8 pmk[] = {
- 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21,
- 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85,
- 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16,
- 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5
+ 0x4e, 0x4d, 0xfa, 0xb1, 0xa2, 0xdd, 0x8a, 0xc1,
+ 0xa9, 0x17, 0x90, 0xf9, 0x53, 0xfa, 0xaa, 0x45,
+ 0x2a, 0xe5, 0xc6, 0x87, 0x3a, 0xb7, 0x5b, 0x63,
+ 0x60, 0x5b, 0xa6, 0x63, 0xf8, 0xa7, 0xfe, 0x59
};
const u8 pmkid[] = {
- 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00,
- 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f
- };
- const u8 local_confirm[] = {
- 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50,
- 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a,
- 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca,
- 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9,
- 0xfc, 0x77
- };
- const u8 peer_confirm[] = {
- 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89,
- 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe,
- 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e,
- 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa,
- 0xc2, 0xfd
+ 0x87, 0x47, 0xa6, 0x00, 0xee, 0xa3, 0xf9, 0xf2,
+ 0x24, 0x75, 0xdf, 0x58, 0xca, 0x1e, 0x54, 0x98
};
struct wpabuf *buf = NULL;
struct crypto_bignum *mask = NULL;
const u8 pwe_19_x[32] = {
0xc9, 0x30, 0x49, 0xb9, 0xe6, 0x40, 0x00, 0xf8,
0x48, 0x20, 0x16, 0x49, 0xe9, 0x99, 0xf2, 0xb5,
0xc2, 0x2d, 0xea, 0x69, 0xb5, 0x63, 0x2c, 0x9d,
0xf4, 0xd6, 0x33, 0xb8, 0xaa, 0x1f, 0x6c, 0x1e
};
const u8 pwe_19_y[32] = {
0x73, 0x63, 0x4e, 0x94, 0xb5, 0x3d, 0x82, 0xe7,
0x38, 0x3a, 0x8d, 0x25, 0x81, 0x99, 0xd9, 0xdc,
0x1a, 0x5e, 0xe8, 0x26, 0x9d, 0x06, 0x03, 0x82,
0xcc, 0xbf, 0x33, 0xe6, 0x14, 0xff, 0x59, 0xa0
};
const u8 pwe_15[384] = {
0x69, 0x68, 0x73, 0x65, 0x8f, 0x65, 0x31, 0x42,
0x9f, 0x97, 0x39, 0x6f, 0xb8, 0x5f, 0x89, 0xe1,
0xfc, 0xd2, 0xf6, 0x92, 0x19, 0xa9, 0x0e, 0x82,
0x2f, 0xf7, 0xf4, 0xbc, 0x0b, 0xd8, 0xa7, 0x9f,
0xf0, 0x80, 0x35, 0x31, 0x6f, 0xca, 0xe1, 0xa5,
0x39, 0x77, 0xdc, 0x11, 0x2b, 0x0b, 0xfe, 0x2e,
0x6f, 0x65, 0x6d, 0xc7, 0xd4, 0xa4, 0x5b, 0x08,
0x1f, 0xd9, 0xbb, 0xe2, 0x22, 0x85, 0x31, 0x81,
0x79, 0x70, 0xbe, 0xa1, 0x66, 0x58, 0x4a, 0x09,
0x3c, 0x57, 0x34, 0x3c, 0x9d, 0x57, 0x8f, 0x42,
0x58, 0xd0, 0x39, 0x81, 0xdb, 0x8f, 0x79, 0xa2,
0x1b, 0x01, 0xcd, 0x27, 0xc9, 0xae, 0xcf, 0xcb,
0x9c, 0xdb, 0x1f, 0x84, 0xb8, 0x88, 0x4e, 0x8f,
0x50, 0x66, 0xb4, 0x29, 0x83, 0x1e, 0xb9, 0x89,
0x0c, 0xa5, 0x47, 0x21, 0xba, 0x10, 0xd5, 0xaa,
0x1a, 0x80, 0xce, 0xf1, 0x4c, 0xad, 0x16, 0xda,
0x57, 0xb2, 0x41, 0x8a, 0xbe, 0x4b, 0x8c, 0xb0,
0xb2, 0xeb, 0xf7, 0xa8, 0x0e, 0x3e, 0xcf, 0x22,
0x8f, 0xd8, 0xb6, 0xdb, 0x79, 0x9c, 0x9b, 0x80,
0xaf, 0xd7, 0x14, 0xad, 0x51, 0x82, 0xf4, 0x64,
0xb6, 0x3f, 0x4c, 0x6c, 0xe5, 0x3f, 0xaa, 0x6f,
0xbf, 0x3d, 0xc2, 0x3f, 0x77, 0xfd, 0xcb, 0xe1,
0x9c, 0xe3, 0x1e, 0x8a, 0x0e, 0x97, 0xe2, 0x2b,
0xe2, 0xdd, 0x37, 0x39, 0x88, 0xc2, 0x8e, 0xbe,
0xfa, 0xac, 0x3d, 0x5b, 0x62, 0x2e, 0x1e, 0x74,
0xa0, 0x9a, 0xf8, 0xed, 0xfa, 0xe1, 0xce, 0x9c,
0xab, 0xbb, 0xdc, 0x36, 0xb1, 0x28, 0x46, 0x3c,
0x7e, 0xa8, 0xbd, 0xb9, 0x36, 0x4c, 0x26, 0x75,
0xe0, 0x17, 0x73, 0x1f, 0xe0, 0xfe, 0xf6, 0x49,
0xfa, 0xa0, 0x45, 0xf4, 0x44, 0x05, 0x20, 0x27,
0x25, 0xc2, 0x99, 0xde, 0x27, 0x8b, 0x70, 0xdc,
0x54, 0x60, 0x90, 0x02, 0x1e, 0x29, 0x97, 0x9a,
0xc4, 0xe7, 0xb6, 0xf5, 0x8b, 0xae, 0x7c, 0x34,
0xaa, 0xef, 0x9b, 0xc6, 0x30, 0xf2, 0x80, 0x8d,
0x80, 0x78, 0xc2, 0x55, 0x63, 0xa0, 0xa1, 0x38,
0x70, 0xfb, 0xf4, 0x74, 0x8d, 0xcd, 0x87, 0x90,
0xb4, 0x54, 0xc3, 0x75, 0xdf, 0x10, 0xc5, 0xb6,
0xb2, 0x08, 0x59, 0x61, 0xe6, 0x68, 0xa5, 0x82,
0xf8, 0x8f, 0x47, 0x30, 0x43, 0xb4, 0xdc, 0x31,
0xfc, 0xbc, 0x69, 0xe7, 0xb4, 0x94, 0xb0, 0x6a,
0x60, 0x59, 0x80, 0x2e, 0xd3, 0xa4, 0xe8, 0x97,
0xa2, 0xa3, 0xc9, 0x08, 0x4b, 0x27, 0x6c, 0xc1,
0x37, 0xe8, 0xfc, 0x5c, 0xe2, 0x54, 0x30, 0x3e,
0xf8, 0xfe, 0xa2, 0xfc, 0xbb, 0xbd, 0x88, 0x6c,
0x92, 0xa3, 0x2a, 0x40, 0x7a, 0x2c, 0x22, 0x38,
0x8c, 0x86, 0x86, 0xfe, 0xb9, 0xd4, 0x6b, 0xd6,
0x47, 0x88, 0xa7, 0xf6, 0x8e, 0x0f, 0x14, 0xad,
0x1e, 0xac, 0xcf, 0x33, 0x01, 0x99, 0xc1, 0x62
};
int pt_groups[] = { 19, 20, 21, 25, 26, 28, 29, 30, 15, 0 };
struct sae_pt *pt_info, *pt;
const u8 addr1b[ETH_ALEN] = { 0x00, 0x09, 0x5b, 0x66, 0xec, 0x1e };
const u8 addr2b[ETH_ALEN] = { 0x00, 0x0b, 0x6b, 0xd9, 0x02, 0x46 };
os_memset(&sae, 0, sizeof(sae));
buf = wpabuf_alloc(1000);
if (!buf ||
sae_set_group(&sae, 19) < 0 ||
sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw),
- pwid, &sae) < 0)
+ &sae) < 0)
goto fail;
/* Override local values based on SAE test vector */
crypto_bignum_deinit(sae.tmp->sae_rand, 1);
sae.tmp->sae_rand = crypto_bignum_init_set(local_rand,
sizeof(local_rand));
mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
if (!sae.tmp->sae_rand || !mask)
goto fail;
if (crypto_bignum_add(sae.tmp->sae_rand, mask,
sae.tmp->own_commit_scalar) < 0 ||
crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order,
sae.tmp->own_commit_scalar) < 0 ||
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)
goto fail;
/* Check that output matches the test vector */
- if (sae_write_commit(&sae, buf, NULL, pwid) < 0)
+ if (sae_write_commit(&sae, buf, NULL, NULL) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
if (wpabuf_len(buf) != sizeof(local_commit) ||
os_memcmp(wpabuf_head(buf), local_commit,
sizeof(local_commit)) != 0) {
wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit");
goto fail;
}
if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
NULL, 0) != 0 ||
sae_process_commit(&sae) < 0)
goto fail;
if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) {
wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK");
goto fail;
}
if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) {
wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK");
goto fail;
}
if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) {
wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID");
goto fail;
}
- buf->used = 0;
- sae.send_confirm = 1;
- sae_write_confirm(&sae, buf);
- wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf);
-
- if (wpabuf_len(buf) != sizeof(local_confirm) ||
- os_memcmp(wpabuf_head(buf), local_confirm,
- sizeof(local_confirm)) != 0) {
- wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm");
- goto fail;
- }
-
- if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
- goto fail;
-
pt_info = sae_derive_pt(pt_groups,
(const u8 *) ssid, os_strlen(ssid),
(const u8 *) pw, os_strlen(pw), pwid);
if (!pt_info)
goto fail;
for (pt = pt_info; pt; pt = pt->next) {
if (pt->group == 19) {
struct crypto_ec_point *pwe;
u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
size_t prime_len = sizeof(pwe_19_x);
pwe = sae_derive_pwe_from_pt_ecc(pt, addr1b, addr2b);
if (!pwe) {
sae_deinit_pt(pt);
goto fail;
}
if (crypto_ec_point_to_bin(pt->ec, pwe, bin,
bin + prime_len) < 0 ||
os_memcmp(pwe_19_x, bin, prime_len) != 0 ||
os_memcmp(pwe_19_y, bin + prime_len,
prime_len) != 0) {
wpa_printf(MSG_ERROR,
"SAE: PT/PWE test vector mismatch");
crypto_ec_point_deinit(pwe, 1);
sae_deinit_pt(pt);
goto fail;
}
crypto_ec_point_deinit(pwe, 1);
}
if (pt->group == 15) {
struct crypto_bignum *pwe;
u8 bin[SAE_MAX_PRIME_LEN];
size_t prime_len = sizeof(pwe_15);
pwe = sae_derive_pwe_from_pt_ffc(pt, addr1b, addr2b);
if (!pwe) {
sae_deinit_pt(pt);
goto fail;
}
if (crypto_bignum_to_bin(pwe, bin, sizeof(bin),
prime_len) < 0 ||
os_memcmp(pwe_15, bin, prime_len) != 0) {
wpa_printf(MSG_ERROR,
"SAE: PT/PWE test vector mismatch");
crypto_bignum_deinit(pwe, 1);
sae_deinit_pt(pt);
goto fail;
}
crypto_bignum_deinit(pwe, 1);
}
}
sae_deinit_pt(pt_info);
ret = 0;
fail:
sae_clear_data(&sae);
wpabuf_free(buf);
crypto_bignum_deinit(mask, 1);
return ret;
#else /* CONFIG_SAE */
return 0;
#endif /* CONFIG_SAE */
}
static int sae_pk_tests(void)
{
#ifdef CONFIG_SAE_PK
const char *invalid[] = { "a2bc-de3f-ghim-", "a2bcde3fghim", "", NULL };
struct {
const char *pw;
const u8 *val;
} valid[] = {
{ "a2bc-de3f-ghim", (u8 *) "\x06\x82\x21\x93\x65\x31\xd0\xc0" },
{ "aaaa-aaaa-aaaj", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x90" },
{ "7777-7777-777f", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe\x50" },
{ NULL, NULL }
};
int i;
bool failed;
for (i = 0; invalid[i]; i++) {
if (sae_pk_valid_password(invalid[i])) {
wpa_printf(MSG_ERROR,
"SAE-PK: Invalid password '%s' not recognized",
invalid[i]);
return -1;
}
}
failed = false;
for (i = 0; valid[i].pw; i++) {
u8 *res;
size_t res_len;
char *b32;
const char *pw = valid[i].pw;
const u8 *val = valid[i].val;
size_t pw_len = os_strlen(pw);
size_t bits = (pw_len - pw_len / 5) * 5;
size_t bytes = (bits + 7) / 8;
if (!sae_pk_valid_password(pw)) {
wpa_printf(MSG_ERROR,
"SAE-PK: Valid password '%s' not recognized",
pw);
failed = true;
continue;
}
res = sae_pk_base32_decode(pw, pw_len, &res_len);
if (!res) {
wpa_printf(MSG_ERROR,
"SAE-PK: Failed to decode password '%s'",
valid[i].pw);
failed = true;
continue;
}
if (res_len != bytes || os_memcmp(val, res, res_len) != 0) {
wpa_printf(MSG_ERROR,
"SAE-PK: Mismatch for decoded password '%s'",
valid[i].pw);
wpa_hexdump(MSG_INFO, "SAE-PK: Decoded value",
res, res_len);
wpa_hexdump(MSG_INFO, "SAE-PK: Expected value",
val, bytes);
failed = true;
}
os_free(res);
b32 = sae_pk_base32_encode(val, bits - 5);
if (!b32) {
wpa_printf(MSG_ERROR,
"SAE-PK: Failed to encode password '%s'",
pw);
failed = true;
continue;
}
if (os_strcmp(b32, pw) != 0) {
wpa_printf(MSG_ERROR,
"SAE-PK: Mismatch for password '%s'", pw);
wpa_printf(MSG_INFO, "SAE-PK: Encoded value: '%s'",
b32);
failed = true;
}
os_free(b32);
}
return failed ? -1 : 0;
#else /* CONFIG_SAE_PK */
return 0;
#endif /* CONFIG_SAE_PK */
}
#ifdef CONFIG_PASN
static int pasn_test_pasn_auth(void)
{
/* Test vector taken from IEEE P802.11az/D2.6, J.12 */
const u8 pmk[] = {
0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6,
0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef,
0xf8, 0xbd, 0x88, 0x8f, 0x6c, 0x1d, 0x9c, 0xc9,
0xd1, 0x0f, 0x04, 0xbd, 0x37, 0x8f, 0x3c, 0xad
};
const u8 spa_addr[] = {
0x00, 0x90, 0x4c, 0x01, 0xc1, 0x07
};
const u8 bssid[] = {
0xc0, 0xff, 0xd4, 0xa8, 0xdb, 0xc1
};
const u8 dhss[] = {
0xf8, 0x7b, 0x20, 0x8e, 0x7e, 0xd2, 0xb7, 0x37,
0xaf, 0xdb, 0xc2, 0xe1, 0x3e, 0xae, 0x78, 0xda,
0x30, 0x01, 0x23, 0xd4, 0xd8, 0x4b, 0xa8, 0xb0,
0xea, 0xfe, 0x90, 0xc4, 0x8c, 0xdf, 0x1f, 0x93
};
const u8 kck[] = {
0x7b, 0xb8, 0x21, 0xac, 0x0a, 0xa5, 0x90, 0x9d,
0xd6, 0x54, 0xa5, 0x60, 0x65, 0xad, 0x7c, 0x77,
0xeb, 0x88, 0x9c, 0xbe, 0x29, 0x05, 0xbb, 0xf0,
0x5a, 0xbb, 0x1e, 0xea, 0xc8, 0x8b, 0xa3, 0x06
};
const u8 tk[] = {
0x67, 0x3e, 0xab, 0x46, 0xb8, 0x32, 0xd5, 0xa8,
0x0c, 0xbc, 0x02, 0x43, 0x01, 0x6e, 0x20, 0x7e
};
const u8 kdk[] = {
0x2d, 0x0f, 0x0e, 0x82, 0xc7, 0x0d, 0xd2, 0x6b,
0x79, 0x06, 0x1a, 0x46, 0x81, 0xe8, 0xdb, 0xb2,
0xea, 0x83, 0xbe, 0xa3, 0x99, 0x84, 0x4b, 0xd5,
0x89, 0x4e, 0xb3, 0x20, 0xf6, 0x9d, 0x7d, 0xd6
};
struct wpa_ptk ptk;
int ret;
ret = pasn_pmk_to_ptk(pmk, sizeof(pmk),
spa_addr, bssid,
dhss, sizeof(dhss),
&ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP,
WPA_KDK_MAX_LEN);
if (ret)
return ret;
if (ptk.kck_len != sizeof(kck) ||
os_memcmp(kck, ptk.kck, sizeof(kck)) != 0) {
wpa_printf(MSG_ERROR, "PASN: Mismatched KCK");
return -1;
}
if (ptk.tk_len != sizeof(tk) ||
os_memcmp(tk, ptk.tk, sizeof(tk)) != 0) {
wpa_printf(MSG_ERROR, "PASN: Mismatched TK");
return -1;
}
if (ptk.kdk_len != sizeof(kdk) ||
os_memcmp(kdk, ptk.kdk, sizeof(kdk)) != 0) {
wpa_printf(MSG_ERROR, "PASN: Mismatched KDK");
return -1;
}
return 0;
}
static int pasn_test_no_pasn_auth(void)
{
/* Test vector taken from IEEE P802.11az/D2.6, J.13 */
const u8 pmk[] = {
0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6,
0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef,
0xf8, 0xbd, 0x88, 0x8f, 0x6c, 0x1d, 0x9c, 0xc9,
0xd1, 0x0f, 0x04, 0xbd, 0x37, 0x8f, 0x3c, 0xad
};
const u8 aa[] = {
0xc0, 0xff, 0xd4, 0xa8, 0xdb, 0xc1
};
const u8 spa[] = {
0x00, 0x90, 0x4c, 0x01, 0xc1, 0x07
};
const u8 anonce[] = {
0xbe, 0x7a, 0x1c, 0xa2, 0x84, 0x34, 0x7b, 0x5b,
0xd6, 0x7d, 0xbd, 0x2d, 0xfd, 0xb4, 0xd9, 0x9f,
0x1a, 0xfa, 0xe0, 0xb8, 0x8b, 0xa1, 0x8e, 0x00,
0x87, 0x18, 0x41, 0x7e, 0x4b, 0x27, 0xef, 0x5f
};
const u8 snonce[] = {
0x40, 0x4b, 0x01, 0x2f, 0xfb, 0x43, 0xed, 0x0f,
0xb4, 0x3e, 0xa1, 0xf2, 0x87, 0xc9, 0x1f, 0x25,
0x06, 0xd2, 0x1b, 0x4a, 0x92, 0xd7, 0x4b, 0x5e,
0xa5, 0x0c, 0x94, 0x33, 0x50, 0xce, 0x86, 0x71
};
const u8 kck[] = {
0xcd, 0x7b, 0x9e, 0x75, 0x55, 0x36, 0x2d, 0xf0,
0xb6, 0x35, 0x68, 0x48, 0x4a, 0x81, 0x12, 0xf5
};
const u8 kek[] = {
0x99, 0xca, 0xd3, 0x58, 0x8d, 0xa0, 0xf1, 0xe6,
0x3f, 0xd1, 0x90, 0x19, 0x10, 0x39, 0xbb, 0x4b
};
const u8 tk[] = {
0x9e, 0x2e, 0x93, 0x77, 0xe7, 0x53, 0x2e, 0x73,
0x7a, 0x1b, 0xc2, 0x50, 0xfe, 0x19, 0x4a, 0x03
};
const u8 kdk[] = {
0x6c, 0x7f, 0xb9, 0x7c, 0xeb, 0x55, 0xb0, 0x1a,
0xcf, 0xf0, 0x0f, 0x07, 0x09, 0x42, 0xbd, 0xf5,
0x29, 0x1f, 0xeb, 0x4b, 0xee, 0x38, 0xe0, 0x36,
0x5b, 0x25, 0xa2, 0x50, 0xbb, 0x2a, 0xc9, 0xff
};
struct wpa_ptk ptk;
int ret;
ret = wpa_pmk_to_ptk(pmk, sizeof(pmk),
"Pairwise key expansion",
spa, aa, snonce, anonce,
&ptk, WPA_KEY_MGMT_SAE, WPA_CIPHER_CCMP,
NULL, 0, WPA_KDK_MAX_LEN);
if (ret)
return ret;
if (ptk.kck_len != sizeof(kck) ||
os_memcmp(kck, ptk.kck, sizeof(kck)) != 0) {
wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KCK");
return -1;
}
if (ptk.kek_len != sizeof(kek) ||
os_memcmp(kek, ptk.kek, sizeof(kek)) != 0) {
wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KEK");
return -1;
}
if (ptk.tk_len != sizeof(tk) ||
os_memcmp(tk, ptk.tk, sizeof(tk)) != 0) {
wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched TK");
return -1;
}
if (ptk.kdk_len != sizeof(kdk) ||
os_memcmp(kdk, ptk.kdk, sizeof(kdk)) != 0) {
wpa_printf(MSG_ERROR, "KDK no PASN auth: Mismatched KDK");
return -1;
}
return 0;
}
#endif /* CONFIG_PASN */
static int pasn_tests(void)
{
#ifdef CONFIG_PASN
if (pasn_test_pasn_auth() ||
pasn_test_no_pasn_auth())
return -1;
#endif /* CONFIG_PASN */
return 0;
}
int common_module_tests(void)
{
int ret = 0;
wpa_printf(MSG_INFO, "common module tests");
if (ieee802_11_parse_tests() < 0 ||
gas_tests() < 0 ||
sae_tests() < 0 ||
sae_pk_tests() < 0 ||
pasn_tests() < 0 ||
rsn_ie_parse_tests() < 0)
ret = -1;
return ret;
}
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5b80db6d3b83..96681843e258 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1,2672 +1,2701 @@
/*
* 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) {
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;
}
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, NO_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, 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;
}
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/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 8a16f1666221..fe2b1bca601b 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -1,337 +1,340 @@
/*
* 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);
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 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/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 573e144a63d5..32c93bb84d54 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,10697 +1,10739 @@
/*
* 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.
*
* @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.
*
*/
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,
};
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
* 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.
*/
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,
};
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
*/
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,
/* 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
*/
enum qca_tsf_cmd {
QCA_TSF_CAPTURE,
QCA_TSF_GET,
QCA_TSF_SYNC_GET,
};
/**
* 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_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.
*/
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_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,
/* 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,
/* 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.
*
* @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.
*/
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,
/* 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,
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,
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,
};
/**
* 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,
/* 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.
*/
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,
};
/**
* 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 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_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,
/* 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.
*/
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,
};
/**
* 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_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.
* 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 an optional parameter for response and is
* a required parameter for
* 1. TWT SET Request
* 2. TWT GET Request
* 3. TWT TERMINATE Request
* 4. TWT SUSPEND Request
* In STA mode, this is an optional parameter in request and response for
* the above four TWT operations.
*
* @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
*/
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,
+
/* 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.
*/
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,
};
/**
* 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_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_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.
*/
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,
/* 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.
*/
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,
/* 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.
*/
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,
/* 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_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,
};
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index f0d4c228c5da..74920a78e46a 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -1,2373 +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, const char *identifier)
+ size_t password_len)
{
u8 counter, k;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[3];
- size_t len[3];
- size_t num_elem;
+ const u8 *addr[2];
+ size_t len[2];
u8 *dummy_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);
tmp_password = os_malloc(password_len);
if (!dummy_password || !tmp_password ||
random_get_bytes(dummy_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);
- if (identifier)
- wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
- identifier);
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
- * base = password [|| identifier]
+ * 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;
- num_elem = 1;
- if (identifier) {
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- addr[num_elem] = &counter;
- len[num_elem] = sizeof(counter);
- num_elem++;
+ 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,
password_len, tmp_password);
- if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+ 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);
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, const char *identifier)
+ size_t password_len)
{
u8 counter, k, sel_counter = 0;
u8 addrs[2 * ETH_ALEN];
- const u8 *addr[3];
- size_t len[3];
- size_t num_elem;
+ 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 [|| identifier] || counter)
+ * password || counter)
*/
sae_pwd_seed_key(addr1, addr2, addrs);
addr[0] = password;
len[0] = password_len;
- num_elem = 1;
- if (identifier) {
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- addr[num_elem] = &counter;
- len[num_elem] = sizeof(counter);
- num_elem++;
+ 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), num_elem,
+ 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,
- const char *identifier, struct sae_data *sae)
+ struct sae_data *sae)
{
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
- password_len,
- identifier) < 0) ||
+ password_len) < 0) ||
(sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
- password_len,
- identifier) < 0))
+ 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 */
- sc = wpabuf_put(buf, 0);
- wpabuf_put_le16(buf, sae->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/src/common/sae.h b/src/common/sae.h
index 2243c0f339b0..93fc5fb39712 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -1,172 +1,172 @@
/*
* Simultaneous authentication of equals
* Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef SAE_H
#define SAE_H
#define SAE_KCK_LEN 32
#define SAE_PMK_LEN 32
#define SAE_PMKID_LEN 16
#define SAE_MAX_PRIME_LEN 512
#define SAE_MAX_ECC_PRIME_LEN 66
#define SAE_MAX_HASH_LEN 64
#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255)
#ifdef CONFIG_SAE_PK
#define SAE_CONFIRM_MAX_LEN ((2 + SAE_MAX_HASH_LEN) + 1500)
#else /* CONFIG_SAE_PK */
#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
#endif /* CONFIG_SAE_PK */
#define SAE_PK_M_LEN 16
/* Special value returned by sae_parse_commit() */
#define SAE_SILENTLY_DISCARD 65535
struct sae_pk {
struct wpabuf *m;
struct crypto_ec_key *key;
int group;
struct wpabuf *pubkey; /* DER encoded subjectPublicKey */
#ifdef CONFIG_TESTING_OPTIONS
struct crypto_ec_key *sign_key_override;
#endif /* CONFIG_TESTING_OPTIONS */
};
struct sae_temporary_data {
u8 kck[SAE_MAX_HASH_LEN];
size_t kck_len;
struct crypto_bignum *own_commit_scalar;
struct crypto_bignum *own_commit_element_ffc;
struct crypto_ec_point *own_commit_element_ecc;
struct crypto_bignum *peer_commit_element_ffc;
struct crypto_ec_point *peer_commit_element_ecc;
struct crypto_ec_point *pwe_ecc;
struct crypto_bignum *pwe_ffc;
struct crypto_bignum *sae_rand;
struct crypto_ec *ec;
int prime_len;
int order_len;
const struct dh_group *dh;
const struct crypto_bignum *prime;
const struct crypto_bignum *order;
struct crypto_bignum *prime_buf;
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
char *pw_id;
int vlan_id;
u8 bssid[ETH_ALEN];
struct wpabuf *own_rejected_groups;
struct wpabuf *peer_rejected_groups;
unsigned int own_addr_higher:1;
#ifdef CONFIG_SAE_PK
u8 kek[SAE_MAX_HASH_LEN];
size_t kek_len;
const struct sae_pk *ap_pk;
u8 own_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
u8 fingerprint[SAE_MAX_HASH_LEN];
size_t fingerprint_bytes;
size_t fingerprint_bits;
size_t lambda;
unsigned int sec;
u8 ssid[32];
size_t ssid_len;
#ifdef CONFIG_TESTING_OPTIONS
bool omit_pk_elem;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_SAE_PK */
};
struct sae_pt {
struct sae_pt *next;
int group;
struct crypto_ec *ec;
struct crypto_ec_point *ecc_pt;
const struct dh_group *dh;
struct crypto_bignum *ffc_pt;
#ifdef CONFIG_SAE_PK
u8 ssid[32];
size_t ssid_len;
#endif /* CONFIG_SAE_PK */
};
enum sae_state {
SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED
};
struct sae_data {
enum sae_state state;
u16 send_confirm;
u8 pmk[SAE_PMK_LEN];
u8 pmkid[SAE_PMKID_LEN];
struct crypto_bignum *peer_commit_scalar;
struct crypto_bignum *peer_commit_scalar_accepted;
int group;
unsigned int sync; /* protocol instance variable: Sync */
u16 rc; /* protocol instance variable: Rc (received send-confirm) */
unsigned int h2e:1;
unsigned int pk:1;
struct sae_temporary_data *tmp;
};
int sae_set_group(struct sae_data *sae, int group);
void sae_clear_temp_data(struct sae_data *sae);
void sae_clear_data(struct sae_data *sae);
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
- const char *identifier, struct sae_data *sae);
+ struct sae_data *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);
int sae_process_commit(struct sae_data *sae);
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
const struct wpabuf *token, const char *identifier);
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);
int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
const char * sae_state_txt(enum sae_state state);
size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
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 crypto_ec_point *
sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2);
struct crypto_bignum *
sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2);
void sae_deinit_pt(struct sae_pt *pt);
/* sae_pk.c */
#ifdef CONFIG_SAE_PK
bool sae_pk_valid_password(const char *pw);
#else /* CONFIG_SAE_PK */
static inline bool sae_pk_valid_password(const char *pw)
{
return false;
}
#endif /* CONFIG_SAE_PK */
char * sae_pk_base32_encode(const u8 *src, size_t len_bits);
u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len);
int sae_pk_set_password(struct sae_data *sae, const char *password);
void sae_deinit_pk(struct sae_pk *pk);
struct sae_pk * sae_parse_pk(const char *val);
int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf);
int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len);
int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash);
u32 sae_pk_get_be19(const u8 *buf);
void sae_pk_buf_shift_left_19(u8 *buf, size_t len);
#endif /* SAE_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 493da18d7265..04461516f138 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,3732 +1,3732 @@
/*
* 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_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,
- struct wpabuf *pubkey, bool compressed,
- struct wpabuf *comeback, int after)
+ 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/src/common/wpa_common.h b/src/common/wpa_common.h
index e51e19483e79..a1ff895659cb 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,677 +1,677 @@
/*
* 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 *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,
- struct wpabuf *pubkey, bool compressed,
- struct wpabuf *comeback, int after);
+ 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/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 72f93c19255a..a4b1083bb4c1 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,2346 +1,2348 @@
/*
* 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>
#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;
}
+#endif /* CONFIG_ECC */
#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;
/* 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;
}
#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_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_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_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;
/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
switch (group) {
case 19:
nid = NID_X9_62_prime256v1;
break;
case 20:
nid = NID_secp384r1;
break;
case 21:
nid = NID_secp521r1;
break;
case 25:
nid = NID_X9_62_prime192v1;
break;
case 26:
nid = NID_secp224r1;
break;
#ifdef NID_brainpoolP224r1
case 27:
nid = NID_brainpoolP224r1;
break;
#endif /* NID_brainpoolP224r1 */
#ifdef NID_brainpoolP256r1
case 28:
nid = NID_brainpoolP256r1;
break;
#endif /* NID_brainpoolP256r1 */
#ifdef NID_brainpoolP384r1
case 29:
nid = NID_brainpoolP384r1;
break;
#endif /* NID_brainpoolP384r1 */
#ifdef NID_brainpoolP512r1
case 30:
nid = NID_brainpoolP512r1;
break;
#endif /* NID_brainpoolP512r1 */
default:
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;
}
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);
}
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 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;
key->eckey = d2i_ECPrivateKey(NULL, &der, der_len);
if (!key->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);
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;
goto fail;
}
return key;
fail:
crypto_ec_key_deinit(key);
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;
key->pkey = d2i_PUBKEY(NULL, &der, der_len);
if (!key->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)
goto fail;
return key;
fail:
crypto_ec_key_deinit(key);
return NULL;
}
void crypto_ec_key_deinit(struct crypto_ec_key *key)
{
if (key) {
EVP_PKEY_free(key->pkey);
os_free(key);
}
}
struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
{
unsigned char *der = NULL;
int der_len;
struct wpabuf *buf;
der_len = i2d_PUBKEY(key->pkey, &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;
}
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(key->pkey);
sig_der = wpabuf_alloc(sig_len);
if (!sig_der)
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);
}
EVP_PKEY_CTX_free(pkctx);
return sig_der;
}
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(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;
}
int crypto_ec_key_group(struct crypto_ec_key *key)
{
const EC_GROUP *group;
int nid;
group = EC_KEY_get0_group(key->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;
}
return -1;
}
#endif /* CONFIG_ECC */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 5b2c71ca0fd0..8ef9ea23a986 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6141 +1,6147 @@
/*
* 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;
/*
* 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
* 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
/** 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)
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 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),
};
/* 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/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 35526fc2fe06..ed194be2a8a6 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1,12169 +1,12218 @@
/*
* 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"),
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(struct wpa_driver_nl80211_data *drv, int ifindex,
- int flags, uint8_t cmd)
+nl80211_ifindex_msg_build(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int ifindex, int flags,
+ uint8_t cmd)
{
- struct nl_msg *msg;
-
- msg = nlmsg_alloc();
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->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 MSCS Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 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) {
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 = nlmsg_alloc();
+ 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;
}
}
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
+ /*
+ * 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
* 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;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ 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 },
};
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_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_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))
goto fail;
if (peer_capab) {
/*
* The internal enum tdls_peer_capability definition is
* currently identical with the nl80211 enum
* nl80211_tdls_peer_capability, so no conversion is needed
* here.
*/
if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
peer_capab))
goto fail;
}
if ((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))
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/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index d3d43d48a7cb..cd596e311e59 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/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_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
+ 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_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/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
index 4a9324406509..ab9bd86774b3 100644
--- a/src/eap_common/eap_sim_common.c
+++ b/src/eap_common/eap_sim_common.c
@@ -1,1230 +1,1254 @@
/*
* EAP peer/server: EAP-SIM/AKA/AKA' shared routines
* Copyright (c) 2004-2008, 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 "wpabuf.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "eap_common/eap_defs.h"
#include "eap_common/eap_sim_common.h"
static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
{
return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
}
void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
const u8 *nonce_mt, u16 selected_version,
const u8 *ver_list, size_t ver_list_len,
int num_chal, const u8 *kc, u8 *mk)
{
u8 sel_ver[2];
const unsigned char *addr[5];
size_t len[5];
addr[0] = identity;
len[0] = identity_len;
addr[1] = kc;
len[1] = num_chal * EAP_SIM_KC_LEN;
addr[2] = nonce_mt;
len[2] = EAP_SIM_NONCE_MT_LEN;
addr[3] = ver_list;
len[3] = ver_list_len;
addr[4] = sel_ver;
len[4] = 2;
WPA_PUT_BE16(sel_ver, selected_version);
/* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
sha1_vector(5, addr, len, mk);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
}
void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
const u8 *ik, const u8 *ck, u8 *mk)
{
const u8 *addr[3];
size_t len[3];
addr[0] = identity;
len[0] = identity_len;
addr[1] = ik;
len[1] = EAP_AKA_IK_LEN;
addr[2] = ck;
len[2] = EAP_AKA_CK_LEN;
/* MK = SHA1(Identity|IK|CK) */
sha1_vector(3, addr, len, mk);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
}
int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
{
u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
return -1;
}
pos = buf;
os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
pos += EAP_SIM_K_ENCR_LEN;
os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
pos += EAP_SIM_K_AUT_LEN;
os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
pos += EAP_SIM_KEYING_DATA_LEN;
os_memcpy(emsk, pos, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
k_encr, EAP_SIM_K_ENCR_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
k_aut, EAP_SIM_K_AUT_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
msk, EAP_SIM_KEYING_DATA_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
os_memset(buf, 0, sizeof(buf));
return 0;
}
int eap_sim_derive_keys_reauth(u16 _counter,
const u8 *identity, size_t identity_len,
const u8 *nonce_s, const u8 *mk, u8 *msk,
u8 *emsk)
{
u8 xkey[SHA1_MAC_LEN];
u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
u8 counter[2];
const u8 *addr[4];
size_t len[4];
while (identity_len > 0 && identity[identity_len - 1] == 0) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
"character from the end of identity");
identity_len--;
}
addr[0] = identity;
len[0] = identity_len;
addr[1] = counter;
len[1] = 2;
addr[2] = nonce_s;
len[2] = EAP_SIM_NONCE_S_LEN;
addr[3] = mk;
len[3] = EAP_SIM_MK_LEN;
WPA_PUT_BE16(counter, _counter);
wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
identity, identity_len);
wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
EAP_SIM_NONCE_S_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
/* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
sha1_vector(4, addr, len, xkey);
wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
return -1;
}
if (msk) {
os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
msk, EAP_SIM_KEYING_DATA_LEN);
}
if (emsk) {
os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
}
os_memset(buf, 0, sizeof(buf));
return 0;
}
int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
const u8 *mac, const u8 *extra, size_t extra_len)
{
unsigned char hmac[SHA1_MAC_LEN];
const u8 *addr[2];
size_t len[2];
u8 *tmp;
if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
mac < wpabuf_head_u8(req) ||
mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
return -1;
tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
if (tmp == NULL)
return -1;
addr[0] = tmp;
len[0] = wpabuf_len(req);
addr[1] = extra;
len[1] = extra_len;
/* HMAC-SHA1-128 */
os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
tmp, wpabuf_len(req));
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
extra, extra_len);
wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
k_aut, EAP_SIM_K_AUT_LEN);
hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
hmac, EAP_SIM_MAC_LEN);
os_free(tmp);
return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
}
void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
const u8 *extra, size_t extra_len)
{
unsigned char hmac[SHA1_MAC_LEN];
const u8 *addr[2];
size_t len[2];
addr[0] = msg;
len[0] = msg_len;
addr[1] = extra;
len[1] = extra_len;
/* HMAC-SHA1-128 */
os_memset(mac, 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
extra, extra_len);
wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
k_aut, EAP_SIM_K_AUT_LEN);
hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
mac, EAP_SIM_MAC_LEN);
}
#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
static void prf_prime(const u8 *k, const char *seed1,
const u8 *seed2, size_t seed2_len,
const u8 *seed3, size_t seed3_len,
u8 *res, size_t res_len)
{
const u8 *addr[5];
size_t len[5];
u8 hash[SHA256_MAC_LEN];
u8 iter;
/*
* PRF'(K,S) = T1 | T2 | T3 | T4 | ...
* T1 = HMAC-SHA-256 (K, S | 0x01)
* T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
* T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
* T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
* ...
*/
addr[0] = hash;
len[0] = 0;
addr[1] = (const u8 *) seed1;
len[1] = os_strlen(seed1);
addr[2] = seed2;
len[2] = seed2_len;
addr[3] = seed3;
len[3] = seed3_len;
addr[4] = &iter;
len[4] = 1;
iter = 0;
while (res_len) {
size_t hlen;
iter++;
hmac_sha256_vector(k, 32, 5, addr, len, hash);
len[0] = SHA256_MAC_LEN;
hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
os_memcpy(res, hash, hlen);
res += hlen;
res_len -= hlen;
}
}
void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
const u8 *ik, const u8 *ck, u8 *k_encr,
u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
{
u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
u8 *pos;
/*
* MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
* K_encr = MK[0..127]
* K_aut = MK[128..383]
* K_re = MK[384..639]
* MSK = MK[640..1151]
* EMSK = MK[1152..1663]
*/
os_memcpy(key, ik, EAP_AKA_IK_LEN);
os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
keys, sizeof(keys));
pos = keys;
os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
k_encr, EAP_SIM_K_ENCR_LEN);
pos += EAP_SIM_K_ENCR_LEN;
os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
k_aut, EAP_AKA_PRIME_K_AUT_LEN);
pos += EAP_AKA_PRIME_K_AUT_LEN;
os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
k_re, EAP_AKA_PRIME_K_RE_LEN);
pos += EAP_AKA_PRIME_K_RE_LEN;
os_memcpy(msk, pos, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
pos += EAP_MSK_LEN;
os_memcpy(emsk, pos, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
}
int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
const u8 *identity, size_t identity_len,
const u8 *nonce_s, u8 *msk, u8 *emsk)
{
u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
u8 *pos;
/*
* MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
* MSK = MK[0..511]
* EMSK = MK[512..1023]
*/
WPA_PUT_BE16(seed3, counter);
os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
seed3, sizeof(seed3),
keys, sizeof(keys));
pos = keys;
os_memcpy(msk, pos, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
pos += EAP_MSK_LEN;
os_memcpy(emsk, pos, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
os_memset(keys, 0, sizeof(keys));
return 0;
}
int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
const u8 *mac, const u8 *extra, size_t extra_len)
{
unsigned char hmac[SHA256_MAC_LEN];
const u8 *addr[2];
size_t len[2];
u8 *tmp;
if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
mac < wpabuf_head_u8(req) ||
mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
return -1;
tmp = os_memdup(wpabuf_head(req), wpabuf_len(req));
if (tmp == NULL)
return -1;
addr[0] = tmp;
len[0] = wpabuf_len(req);
addr[1] = extra;
len[1] = extra_len;
/* HMAC-SHA-256-128 */
os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
tmp, wpabuf_len(req));
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
extra, extra_len);
wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
k_aut, EAP_AKA_PRIME_K_AUT_LEN);
hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
hmac, EAP_SIM_MAC_LEN);
os_free(tmp);
return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
}
void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
u8 *mac, const u8 *extra, size_t extra_len)
{
unsigned char hmac[SHA256_MAC_LEN];
const u8 *addr[2];
size_t len[2];
addr[0] = msg;
len[0] = msg_len;
addr[1] = extra;
len[1] = extra_len;
/* HMAC-SHA-256-128 */
os_memset(mac, 0, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
extra, extra_len);
wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
k_aut, EAP_AKA_PRIME_K_AUT_LEN);
hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
mac, EAP_SIM_MAC_LEN);
}
void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
const u8 *network_name,
size_t network_name_len)
{
u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
u8 hash[SHA256_MAC_LEN];
const u8 *addr[5];
size_t len[5];
u8 fc;
u8 l0[2], l1[2];
/* 3GPP TS 33.402 V8.0.0
* (CK', IK') = F(CK, IK, <access network identity>)
*/
/* TODO: CK', IK' generation should really be moved into the actual
* AKA procedure with network name passed in there and option to use
* AMF separation bit = 1 (3GPP TS 33.401). */
/* Change Request 33.402 CR 0033 to version 8.1.1 from
* 3GPP TSG-SA WG3 Meeting #53 in September 2008:
*
* CK' || IK' = HMAC-SHA-256(Key, S)
* S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
* Key = CK || IK
* FC = 0x20
* P0 = access network identity (3GPP TS 24.302)
* L0 = length of acceess network identity (2 octets, big endian)
* P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
* L1 = 0x00 0x06
*/
fc = 0x20;
wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
network_name, network_name_len);
wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
os_memcpy(key, ck, EAP_AKA_CK_LEN);
os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
key, sizeof(key));
addr[0] = &fc;
len[0] = 1;
addr[1] = network_name;
len[1] = network_name_len;
WPA_PUT_BE16(l0, network_name_len);
addr[2] = l0;
len[2] = 2;
addr[3] = sqn_ak;
len[3] = 6;
WPA_PUT_BE16(l1, 6);
addr[4] = l1;
len[4] = 2;
hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
hash, sizeof(hash));
os_memcpy(ck, hash, EAP_AKA_CK_LEN);
os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
}
#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
int eap_sim_parse_attr(const u8 *start, const u8 *end,
struct eap_sim_attrs *attr, int aka, int encr)
{
const u8 *pos = start, *apos;
size_t alen, plen, i, list_len;
os_memset(attr, 0, sizeof(*attr));
attr->id_req = NO_ID_REQ;
attr->notification = -1;
attr->counter = -1;
attr->selected_version = -1;
attr->client_error_code = -1;
while (pos < end) {
if (pos + 2 > end) {
wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
return -1;
}
wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
pos[0], pos[1] * 4);
if (pos + pos[1] * 4 > end) {
wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
"(pos=%p len=%d end=%p)",
pos, pos[1] * 4, end);
return -1;
}
if (pos[1] == 0) {
wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
return -1;
}
apos = pos + 2;
alen = pos[1] * 4 - 2;
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
apos, alen);
switch (pos[0]) {
case EAP_SIM_AT_RAND:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
apos += 2;
alen -= 2;
if ((!aka && (alen % GSM_RAND_LEN)) ||
(aka && alen != EAP_AKA_RAND_LEN)) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
" (len %lu)",
(unsigned long) alen);
return -1;
}
attr->rand = apos;
attr->num_chal = alen / GSM_RAND_LEN;
break;
case EAP_SIM_AT_AUTN:
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
if (!aka) {
wpa_printf(MSG_DEBUG, "EAP-SIM: "
"Unexpected AT_AUTN");
return -1;
}
apos += 2;
alen -= 2;
if (alen != EAP_AKA_AUTN_LEN) {
wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
" (len %lu)",
(unsigned long) alen);
return -1;
}
attr->autn = apos;
break;
case EAP_SIM_AT_PADDING:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_PADDING");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
for (i = 2; i < alen; i++) {
if (apos[i] != 0) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
"AT_PADDING used a non-zero"
" padding byte");
wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
"(encr) padding bytes",
apos + 2, alen - 2);
return -1;
}
}
break;
case EAP_SIM_AT_NONCE_MT:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_NONCE_MT length");
return -1;
}
attr->nonce_mt = apos + 2;
break;
case EAP_SIM_AT_PERMANENT_ID_REQ:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
attr->id_req = PERMANENT_ID;
break;
case EAP_SIM_AT_MAC:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
if (alen != 2 + EAP_SIM_MAC_LEN) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
"length");
return -1;
}
attr->mac = apos + 2;
break;
case EAP_SIM_AT_NOTIFICATION:
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_NOTIFICATION length %lu",
(unsigned long) alen);
return -1;
}
attr->notification = apos[0] * 256 + apos[1];
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
attr->notification);
break;
case EAP_SIM_AT_ANY_ID_REQ:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
attr->id_req = ANY_ID;
break;
case EAP_SIM_AT_IDENTITY:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
plen = WPA_GET_BE16(apos);
apos += 2;
alen -= 2;
if (plen > alen) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_IDENTITY (Actual Length %lu, "
"remaining length %lu)",
(unsigned long) plen,
(unsigned long) alen);
return -1;
}
attr->identity = apos;
attr->identity_len = plen;
break;
case EAP_SIM_AT_VERSION_LIST:
if (aka) {
wpa_printf(MSG_DEBUG, "EAP-AKA: "
"Unexpected AT_VERSION_LIST");
return -1;
}
list_len = apos[0] * 256 + apos[1];
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
if (list_len < 2 || list_len > alen - 2) {
wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
"AT_VERSION_LIST (list_len=%lu "
"attr_len=%lu)",
(unsigned long) list_len,
(unsigned long) alen);
return -1;
}
attr->version_list = apos + 2;
attr->version_list_len = list_len;
break;
case EAP_SIM_AT_SELECTED_VERSION:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_SELECTED_VERSION length %lu",
(unsigned long) alen);
return -1;
}
attr->selected_version = apos[0] * 256 + apos[1];
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
"%d", attr->selected_version);
break;
case EAP_SIM_AT_FULLAUTH_ID_REQ:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
attr->id_req = FULLAUTH_ID;
break;
case EAP_SIM_AT_COUNTER:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_COUNTER");
return -1;
}
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
"AT_COUNTER (alen=%lu)",
(unsigned long) alen);
return -1;
}
attr->counter = apos[0] * 256 + apos[1];
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
attr->counter);
break;
case EAP_SIM_AT_COUNTER_TOO_SMALL:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_COUNTER_TOO_SMALL");
return -1;
}
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
"AT_COUNTER_TOO_SMALL (alen=%lu)",
(unsigned long) alen);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
"AT_COUNTER_TOO_SMALL");
attr->counter_too_small = 1;
break;
case EAP_SIM_AT_NONCE_S:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_NONCE_S");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
"AT_NONCE_S");
if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
"AT_NONCE_S (alen=%lu)",
(unsigned long) alen);
return -1;
}
attr->nonce_s = apos + 2;
break;
case EAP_SIM_AT_CLIENT_ERROR_CODE:
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_CLIENT_ERROR_CODE length %lu",
(unsigned long) alen);
return -1;
}
attr->client_error_code = apos[0] * 256 + apos[1];
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
"%d", attr->client_error_code);
break;
case EAP_SIM_AT_IV:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
if (alen != 2 + EAP_SIM_MAC_LEN) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
"length %lu", (unsigned long) alen);
return -1;
}
attr->iv = apos + 2;
break;
case EAP_SIM_AT_ENCR_DATA:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
attr->encr_data = apos + 2;
attr->encr_data_len = alen - 2;
if (attr->encr_data_len % 16) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_ENCR_DATA length %lu",
(unsigned long)
attr->encr_data_len);
return -1;
}
break;
case EAP_SIM_AT_NEXT_PSEUDONYM:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_NEXT_PSEUDONYM");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
"AT_NEXT_PSEUDONYM");
plen = apos[0] * 256 + apos[1];
if (plen > alen - 2) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
" AT_NEXT_PSEUDONYM (actual"
" len %lu, attr len %lu)",
(unsigned long) plen,
(unsigned long) alen);
return -1;
}
attr->next_pseudonym = pos + 4;
attr->next_pseudonym_len = plen;
break;
case EAP_SIM_AT_NEXT_REAUTH_ID:
if (!encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
"AT_NEXT_REAUTH_ID");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
"AT_NEXT_REAUTH_ID");
plen = apos[0] * 256 + apos[1];
if (plen > alen - 2) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
" AT_NEXT_REAUTH_ID (actual"
" len %lu, attr len %lu)",
(unsigned long) plen,
(unsigned long) alen);
return -1;
}
attr->next_reauth_id = pos + 4;
attr->next_reauth_id_len = plen;
break;
case EAP_SIM_AT_RES:
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
attr->res_len_bits = WPA_GET_BE16(apos);
apos += 2;
alen -= 2;
if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
alen > EAP_AKA_MAX_RES_LEN) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
"(len %lu)",
(unsigned long) alen);
return -1;
}
attr->res = apos;
attr->res_len = alen;
break;
case EAP_SIM_AT_AUTS:
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
if (!aka) {
wpa_printf(MSG_DEBUG, "EAP-SIM: "
"Unexpected AT_AUTS");
return -1;
}
if (alen != EAP_AKA_AUTS_LEN) {
wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
" (len %lu)",
(unsigned long) alen);
return -1;
}
attr->auts = apos;
break;
case EAP_SIM_AT_CHECKCODE:
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
if (!aka) {
wpa_printf(MSG_DEBUG, "EAP-SIM: "
"Unexpected AT_CHECKCODE");
return -1;
}
apos += 2;
alen -= 2;
if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
"AT_CHECKCODE (len %lu)",
(unsigned long) alen);
return -1;
}
attr->checkcode = apos;
attr->checkcode_len = alen;
break;
case EAP_SIM_AT_RESULT_IND:
if (encr) {
wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
"AT_RESULT_IND");
return -1;
}
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
"AT_RESULT_IND (alen=%lu)",
(unsigned long) alen);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
attr->result_ind = 1;
break;
#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
case EAP_SIM_AT_KDF_INPUT:
if (aka != 2) {
wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
"AT_KDF_INPUT");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
plen = WPA_GET_BE16(apos);
apos += 2;
alen -= 2;
if (plen > alen) {
wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
"AT_KDF_INPUT (Actual Length %lu, "
"remaining length %lu)",
(unsigned long) plen,
(unsigned long) alen);
return -1;
}
attr->kdf_input = apos;
attr->kdf_input_len = plen;
break;
case EAP_SIM_AT_KDF:
if (aka != 2) {
wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
"AT_KDF");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
"AT_KDF (len %lu)",
(unsigned long) alen);
return -1;
}
if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
"AT_KDF attributes - ignore this");
break;
}
attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
attr->kdf_count++;
break;
case EAP_SIM_AT_BIDDING:
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
"AT_BIDDING (len %lu)",
(unsigned long) alen);
return -1;
}
attr->bidding = apos;
break;
#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
default:
if (pos[0] < 128) {
wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
"non-skippable attribute %d",
pos[0]);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
" attribute %d ignored", pos[0]);
break;
}
pos += pos[1] * 4;
}
wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
"(aka=%d encr=%d)", aka, encr);
return 0;
}
u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
size_t encr_data_len, const u8 *iv,
struct eap_sim_attrs *attr, int aka)
{
u8 *decrypted;
if (!iv) {
wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
return NULL;
}
decrypted = os_memdup(encr_data, encr_data_len);
if (decrypted == NULL)
return NULL;
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Skip AES-128-CBC decryption for fuzz testing");
#else /* TEST_FUZZ */
if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
os_free(decrypted);
return NULL;
}
#endif /* TEST_FUZZ */
wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
decrypted, encr_data_len);
if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
aka, 1)) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
"decrypted AT_ENCR_DATA");
os_free(decrypted);
return NULL;
}
return decrypted;
}
#define EAP_SIM_INIT_LEN 128
struct eap_sim_msg {
struct wpabuf *buf;
size_t mac, iv, encr; /* index from buf */
};
struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
{
struct eap_sim_msg *msg;
struct eap_hdr *eap;
u8 *pos;
msg = os_zalloc(sizeof(*msg));
if (msg == NULL)
return NULL;
msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
if (msg->buf == NULL) {
os_free(msg);
return NULL;
}
eap = wpabuf_put(msg->buf, sizeof(*eap));
eap->code = code;
eap->identifier = id;
pos = wpabuf_put(msg->buf, 4);
*pos++ = type;
*pos++ = subtype;
*pos++ = 0; /* Reserved */
*pos++ = 0; /* Reserved */
return msg;
}
struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
const u8 *k_aut,
const u8 *extra, size_t extra_len)
{
struct eap_hdr *eap;
struct wpabuf *buf;
if (msg == NULL)
return NULL;
eap = wpabuf_mhead(msg->buf);
eap->length = host_to_be16(wpabuf_len(msg->buf));
#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
if (k_aut && msg->mac && type == EAP_TYPE_AKA_PRIME) {
eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
wpabuf_len(msg->buf),
(u8 *) wpabuf_mhead(msg->buf) +
msg->mac, extra, extra_len);
} else
#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
if (k_aut && msg->mac) {
eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
wpabuf_len(msg->buf),
(u8 *) wpabuf_mhead(msg->buf) + msg->mac,
extra, extra_len);
}
buf = msg->buf;
os_free(msg);
return buf;
}
void eap_sim_msg_free(struct eap_sim_msg *msg)
{
if (msg) {
wpabuf_free(msg->buf);
os_free(msg);
}
}
u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
const u8 *data, size_t len)
{
int attr_len = 2 + len;
int pad_len;
u8 *start;
if (msg == NULL)
return NULL;
pad_len = (4 - attr_len % 4) % 4;
attr_len += pad_len;
if (wpabuf_resize(&msg->buf, attr_len))
return NULL;
start = wpabuf_put(msg->buf, 0);
wpabuf_put_u8(msg->buf, attr);
wpabuf_put_u8(msg->buf, attr_len / 4);
wpabuf_put_data(msg->buf, data, len);
if (pad_len)
os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
return start;
}
u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
const u8 *data, size_t len)
{
int attr_len = 4 + len;
int pad_len;
u8 *start;
if (msg == NULL)
return NULL;
pad_len = (4 - attr_len % 4) % 4;
attr_len += pad_len;
if (wpabuf_resize(&msg->buf, attr_len))
return NULL;
start = wpabuf_put(msg->buf, 0);
wpabuf_put_u8(msg->buf, attr);
wpabuf_put_u8(msg->buf, attr_len / 4);
wpabuf_put_be16(msg->buf, value);
if (data)
wpabuf_put_data(msg->buf, data, len);
else
wpabuf_put(msg->buf, len);
if (pad_len)
os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
return start;
}
u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
{
u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
if (pos)
msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
return pos;
}
int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
u8 attr_encr)
{
u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
if (pos == NULL)
return -1;
msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
EAP_SIM_IV_LEN)) {
msg->iv = 0;
return -1;
}
pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
if (pos == NULL) {
msg->iv = 0;
return -1;
}
msg->encr = pos - wpabuf_head_u8(msg->buf);
return 0;
}
int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
{
size_t encr_len;
if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
return -1;
encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
if (encr_len % 16) {
u8 *pos;
int pad_len = 16 - (encr_len % 16);
if (pad_len < 4) {
wpa_printf(MSG_WARNING, "EAP-SIM: "
"eap_sim_msg_add_encr_end - invalid pad_len"
" %d", pad_len);
return -1;
}
wpa_printf(MSG_DEBUG, " *AT_PADDING");
pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
if (pos == NULL)
return -1;
os_memset(pos + 4, 0, pad_len - 4);
encr_len += pad_len;
}
wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)",
(unsigned long) encr_len);
wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
encr_len);
}
void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
const char *type = aka ? "AKA" : "SIM";
#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (notification) {
case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
wpa_printf(MSG_WARNING, "EAP-%s: General failure "
"notification (after authentication)", type);
break;
case EAP_SIM_TEMPORARILY_DENIED:
wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
"User has been temporarily denied access to the "
"requested service", type);
break;
case EAP_SIM_NOT_SUBSCRIBED:
wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
"User has not subscribed to the requested service",
type);
break;
case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
wpa_printf(MSG_WARNING, "EAP-%s: General failure "
"notification (before authentication)", type);
break;
case EAP_SIM_SUCCESS:
wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
"notification", type);
break;
default:
if (notification >= 32768) {
wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
"non-failure notification %d",
type, notification);
} else {
wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
"failure notification %d",
type, notification);
}
}
}
+static const u8 * get_last_char(const u8 *val, size_t len, char c)
+{
+ while (len > 0) {
+ const u8 *pos = &val[len - 1];
+
+ if (*pos == (u8) c)
+ return pos;
+ len--;
+ }
+
+ return NULL;
+}
+
+
int eap_sim_anonymous_username(const u8 *id, size_t id_len)
{
static const char *anonymous_id_prefix = "anonymous@";
+ const u8 *decorated;
size_t anonymous_id_len = os_strlen(anonymous_id_prefix);
if (id_len > anonymous_id_len &&
os_memcmp(id, anonymous_id_prefix, anonymous_id_len) == 0)
return 1; /* 'anonymous@realm' */
if (id_len > anonymous_id_len + 1 &&
os_memcmp(id + 1, anonymous_id_prefix, anonymous_id_len) == 0)
return 1; /* 'Xanonymous@realm' where X is an EAP method code */
if (id_len > 1 && id[0] == '@')
return 1; /* '@realm' */
+ /* RFC 7542 decorated username, for example:
+ * homerealm.example.org!anonymous@otherrealm.example.net */
+ decorated = get_last_char(id, id_len, '!');
+ if (decorated) {
+ decorated++;
+ return eap_sim_anonymous_username(decorated,
+ id + id_len - decorated);
+ }
+
return 0;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 9a5ba7b877c5..78e2380b15e8 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,5228 +1,5232 @@
/*
* 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 && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 &&
- sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ (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;
}
}
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);
}
}
/**
* 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);
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);
}
}
/**
* 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);
}
#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 && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 &&
- sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ (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 */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index fce6e77e7fb9..2669da9b3fa8 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -1,1358 +1,1358 @@
/*
* WPA Supplicant - IEEE 802.11r - Fast BSS Transition
* Copyright (c) 2006-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/aes_wrap.h"
#include "crypto/sha384.h"
#include "crypto/random.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 "wpa.h"
#include "wpa_i.h"
#include "wpa_ie.h"
#include "pmksa_cache.h"
#ifdef CONFIG_IEEE80211R
#ifdef CONFIG_PASN
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid);
#else /* CONFIG_PASN */
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
{
}
#endif /* CONFIG_PASN */
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
{
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *anonce = key->key_nonce;
int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
const u8 *mpmk;
size_t mpmk_len, kdk_len;
if (sm->xxkey_len > 0) {
mpmk = sm->xxkey;
mpmk_len = sm->xxkey_len;
} else if (sm->cur_pmksa) {
mpmk = sm->cur_pmksa->pmk;
mpmk_len = sm->cur_pmksa->pmk_len;
} else {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
"derivation");
return -1;
}
sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
if (wpa_derive_pmk_r0(mpmk, mpmk_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)
return -1;
sm->pmk_r1_len = sm->pmk_r0_len;
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
return -1;
wpa_ft_pasn_store_r1kh(sm, src_addr);
if (sm->force_kdk_derivation ||
- (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 &&
- sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ (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_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
ptk_name, sm->key_mgmt, sm->pairwise_cipher,
kdk_len);
}
/**
* wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ies: Association Response IEs or %NULL to clear FT parameters
* @ies_len: Length of ies buffer in octets
* Returns: 0 on success, -1 on failure
*/
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
{
struct wpa_ft_ies ft;
int use_sha384;
if (sm == NULL)
return 0;
if (!get_ie(ies, ies_len, WLAN_EID_MOBILITY_DOMAIN)) {
os_free(sm->assoc_resp_ies);
sm->assoc_resp_ies = NULL;
sm->assoc_resp_ies_len = 0;
os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
os_memset(sm->r0kh_id, 0, FT_R0KH_ID_MAX_LEN);
sm->r0kh_id_len = 0;
os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
return 0;
}
use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
return -1;
if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
return -1;
wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
ft.mdie, MOBILITY_DOMAIN_ID_LEN);
os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN);
sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
sm->mdie_ft_capab);
if (ft.r0kh_id) {
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
ft.r0kh_id, ft.r0kh_id_len);
os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
sm->r0kh_id_len = ft.r0kh_id_len;
} else {
/* FIX: When should R0KH-ID be cleared? We need to keep the
* old R0KH-ID in order to be able to use this during FT. */
/*
* os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
* sm->r0kh_id_len = 0;
*/
}
if (ft.r1kh_id) {
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
ft.r1kh_id, FT_R1KH_ID_LEN);
os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
} else
os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
os_free(sm->assoc_resp_ies);
sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
if (sm->assoc_resp_ies) {
u8 *pos = sm->assoc_resp_ies;
os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
pos += ft.mdie_len + 2;
if (ft.ftie) {
os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
pos += ft.ftie_len + 2;
}
sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
"(Re)Association Response",
sm->assoc_resp_ies, sm->assoc_resp_ies_len);
}
return 0;
}
/**
* wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @len: Buffer for returning the length of the IEs
* @anonce: ANonce or %NULL if not yet available
* @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
* @kck: 128-bit KCK for MIC or %NULL if no MIC is used
* @kck_len: KCK length in octets
* @target_ap: Target AP address
* @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
* @ric_ies_len: Length of ric_ies buffer in octets
* @ap_mdie: Mobility Domain IE from the target AP
* @omit_rsnxe: Whether RSNXE is omitted from Reassociation Request frame
* Returns: Pointer to buffer with IEs or %NULL on failure
*
* Caller is responsible for freeing the returned buffer with os_free();
*/
static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
const u8 *anonce, const u8 *pmk_name,
const u8 *kck, size_t kck_len,
const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len,
const u8 *ap_mdie, int omit_rsnxe)
{
size_t buf_len;
u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count;
struct rsn_mdie *mdie;
struct rsn_ie_hdr *rsnie;
int mdie_len;
u8 rsnxe[10];
size_t rsnxe_len;
int rsnxe_used;
int res;
sm->ft_completed = 0;
sm->ft_reassoc_completed = 0;
buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
sizeof(struct rsn_ftie_sha384) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
buf = os_zalloc(buf_len);
if (buf == NULL)
return NULL;
pos = buf;
/* RSNIE[PMKR0Name/PMKR1Name] */
rsnie = (struct rsn_ie_hdr *) pos;
rsnie->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(rsnie->version, RSN_VERSION);
pos = (u8 *) (rsnie + 1);
/* Group Suite Selector */
if (!wpa_cipher_valid_group(sm->group_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
sm->group_cipher);
os_free(buf);
return NULL;
}
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
sm->group_cipher));
pos += RSN_SELECTOR_LEN;
/* Pairwise Suite Count */
WPA_PUT_LE16(pos, 1);
pos += 2;
/* Pairwise Suite List */
if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
sm->pairwise_cipher);
os_free(buf);
return NULL;
}
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
sm->pairwise_cipher));
pos += RSN_SELECTOR_LEN;
/* Authenticated Key Management Suite Count */
WPA_PUT_LE16(pos, 1);
pos += 2;
/* Authenticated Key Management Suite List */
if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
#ifdef CONFIG_SHA384
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
#endif /* CONFIG_SHA384 */
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
#ifdef CONFIG_FILS
else 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);
#endif /* CONFIG_FILS */
else {
wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
sm->key_mgmt);
os_free(buf);
return NULL;
}
pos += RSN_SELECTOR_LEN;
/* RSN Capabilities */
WPA_PUT_LE16(pos, rsn_supp_capab(sm));
pos += 2;
/* PMKID Count */
WPA_PUT_LE16(pos, 1);
pos += 2;
/* PMKID List [PMKR0Name/PMKR1Name] */
os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
pos += WPA_PMK_NAME_LEN;
/* Management Group Cipher Suite */
switch (sm->mgmt_group_cipher) {
case WPA_CIPHER_AES_128_CMAC:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
pos += RSN_SELECTOR_LEN;
break;
case WPA_CIPHER_BIP_GMAC_128:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
pos += RSN_SELECTOR_LEN;
break;
case WPA_CIPHER_BIP_GMAC_256:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
pos += RSN_SELECTOR_LEN;
break;
case WPA_CIPHER_BIP_CMAC_256:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
pos += RSN_SELECTOR_LEN;
break;
}
rsnie->len = (pos - (u8 *) rsnie) - 2;
/* MDIE */
mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie);
if (mdie_len <= 0) {
os_free(buf);
return NULL;
}
mdie = (struct rsn_mdie *) (pos + 2);
pos += mdie_len;
/* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
ftie_pos = pos;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ftie_len = pos++;
rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) && anonce &&
(sm->sae_pwe == 1 || sm->sae_pwe == 2);
#ifdef CONFIG_TESTING_OPTIONS
if (anonce && sm->ft_rsnxe_used) {
rsnxe_used = sm->ft_rsnxe_used == 1;
wpa_printf(MSG_DEBUG, "TESTING: FT: Force RSNXE Used %d",
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
struct rsn_ftie_sha384 *ftie;
ftie = (struct rsn_ftie_sha384 *) pos;
ftie->mic_control[0] = !!rsnxe_used;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
if (anonce)
os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
} else {
struct rsn_ftie *ftie;
ftie = (struct rsn_ftie *) pos;
ftie->mic_control[0] = !!rsnxe_used;
fte_mic = ftie->mic;
elem_count = &ftie->mic_control[1];
pos += sizeof(*ftie);
os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
if (anonce)
os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
}
if (kck) {
/* R1KH-ID sub-element in third FT message */
*pos++ = FTIE_SUBELEM_R1KH_ID;
*pos++ = FT_R1KH_ID_LEN;
os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
pos += FT_R1KH_ID_LEN;
}
/* R0KH-ID sub-element */
*pos++ = FTIE_SUBELEM_R0KH_ID;
*pos++ = sm->r0kh_id_len;
os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
pos += sm->r0kh_id_len;
#ifdef CONFIG_OCV
if (kck && wpa_sm_ocv_enabled(sm)) {
/* OCI sub-element in the third FT message */
struct wpa_channel_info ci;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in FTE");
os_free(buf);
return NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
if (sm->oci_freq_override_ft_assoc) {
wpa_printf(MSG_INFO,
"TEST: Override OCI KDE frequency %d -> %d MHz",
ci.frequency, sm->oci_freq_override_ft_assoc);
ci.frequency = sm->oci_freq_override_ft_assoc;
}
#endif /* CONFIG_TESTING_OPTIONS */
*pos++ = FTIE_SUBELEM_OCI;
*pos++ = OCV_OCI_LEN;
if (ocv_insert_oci(&ci, &pos) < 0) {
os_free(buf);
return NULL;
}
}
#endif /* CONFIG_OCV */
*ftie_len = pos - ftie_len - 1;
if (ric_ies) {
/* RIC Request */
os_memcpy(pos, ric_ies, ric_ies_len);
pos += ric_ies_len;
}
if (omit_rsnxe) {
rsnxe_len = 0;
} else {
res = wpa_gen_rsnxe(sm, rsnxe, sizeof(rsnxe));
if (res < 0) {
os_free(buf);
return NULL;
}
rsnxe_len = res;
}
if (kck) {
/*
* IEEE Std 802.11r-2008, 11A.8.4
* MIC shall be calculated over:
* non-AP STA MAC address
* Target AP MAC address
* Transaction seq number (5 for ReassocReq, 3 otherwise)
* RSN IE
* MDIE
* FTIE (with MIC field set to 0)
* RIC-Request (if present)
* RSNXE (if present)
*/
/* Information element count */
*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
if (rsnxe_len)
*elem_count += 1;
if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
((u8 *) mdie) - 2, 2 + sizeof(*mdie),
ftie_pos, 2 + *ftie_len,
(u8 *) rsnie, 2 + rsnie->len, ric_ies,
ric_ies_len, rsnxe_len ? rsnxe : NULL, rsnxe_len,
fte_mic) < 0) {
wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
os_free(buf);
return NULL;
}
}
*len = pos - buf;
return buf;
}
static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
{
int keylen;
enum wpa_alg alg;
u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_printf(MSG_WARNING, "FT: 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 (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
(u8 *) sm->ptk.tk, keylen,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
return -1;
}
wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
return 0;
}
/**
* wpa_ft_prepare_auth_request - Generate over-the-air auth request
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @mdie: Target AP MDIE
* Returns: 0 on success, -1 on failure
*/
int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
{
u8 *ft_ies;
size_t ft_ies_len;
/* Generate a new SNonce */
if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
NULL, 0, sm->bssid, NULL, 0, mdie, 0);
if (ft_ies) {
wpa_sm_update_ft_ies(sm, sm->mobility_domain,
ft_ies, ft_ies_len);
os_free(ft_ies);
}
return 0;
}
int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len,
const u8 *ap_mdie)
{
u8 *pos = buf;
struct rsn_mdie *mdie;
if (buf_len < 2 + sizeof(*mdie)) {
wpa_printf(MSG_INFO,
"FT: Failed to add MDIE: short buffer, length=%zu",
buf_len);
return 0;
}
*pos++ = WLAN_EID_MOBILITY_DOMAIN;
*pos++ = sizeof(*mdie);
mdie = (struct rsn_mdie *) pos;
os_memcpy(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN);
mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
sm->mdie_ft_capab;
return 2 + sizeof(*mdie);
}
const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm)
{
return sm->mobility_domain;
}
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)
{
u8 *ft_ies;
size_t ft_ies_len;
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
u8 ptk_name[WPA_PMK_NAME_LEN];
int ret;
const u8 *bssid;
const u8 *kck;
size_t kck_len, kdk_len;
int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
const u8 *anonce, *snonce;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
if (ft_action) {
if (!sm->over_the_ds_in_progress) {
wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
"- drop FT Action Response");
return -1;
}
if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
"with this Target AP - drop FT Action "
"Response");
return -1;
}
}
if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
"enabled for this connection");
return -1;
}
if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
return -1;
}
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 -1;
}
anonce = ftie->anonce;
snonce = ftie->snonce;
} 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 -1;
}
anonce = ftie->anonce;
snonce = ftie->snonce;
}
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 -1;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
return -1;
}
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 -1;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
return -1;
}
if (parse.rsn_pmkid == NULL ||
os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN))
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
"RSNIE");
return -1;
}
if (sm->mfp == 2 && !(parse.rsn_capab & WPA_CAPABILITY_MFPC)) {
wpa_printf(MSG_INFO,
"FT: Target AP does not support PMF, but local configuration requires that");
return -1;
}
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
return -1;
sm->pmk_r1_len = sm->pmk_r0_len;
bssid = target_ap;
wpa_ft_pasn_store_r1kh(sm, bssid);
if (sm->force_kdk_derivation ||
- (sm->secure_ltf && sm->ap_rsnxe && sm->ap_rsnxe_len >= 4 &&
- sm->ap_rsnxe[3] & BIT(WLAN_RSNX_CAPAB_SECURE_LTF - 8)))
+ (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 (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
anonce, sm->own_addr, bssid,
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
sm->pairwise_cipher,
kdk_len) < 0)
return -1;
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
kck = sm->ptk.kck2;
kck_len = sm->ptk.kck2_len;
} else {
kck = sm->ptk.kck;
kck_len = sm->ptk.kck_len;
}
ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
sm->pmk_r1_name,
kck, kck_len, bssid,
ric_ies, ric_ies_len,
parse.mdie ? parse.mdie - 2 : NULL,
!sm->ap_rsnxe);
if (ft_ies) {
wpa_sm_update_ft_ies(sm, sm->mobility_domain,
ft_ies, ft_ies_len);
os_free(ft_ies);
}
wpa_sm_mark_authenticated(sm, bssid);
ret = wpa_ft_install_ptk(sm, bssid);
if (ret) {
/*
* Some drivers do not support key configuration when we are
* not associated with the target AP. Work around this by
* trying again after the following reassociation gets
* completed.
*/
wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
"association - try again after reassociation");
sm->set_ptk_after_assoc = 1;
} else
sm->set_ptk_after_assoc = 0;
sm->ft_completed = 1;
if (ft_action) {
/*
* The caller is expected trigger re-association with the
* Target AP.
*/
os_memcpy(sm->bssid, target_ap, ETH_ALEN);
}
return 0;
}
int wpa_ft_is_completed(struct wpa_sm *sm)
{
if (sm == NULL)
return 0;
if (!wpa_key_mgmt_ft(sm->key_mgmt))
return 0;
return sm->ft_completed;
}
void wpa_reset_ft_completed(struct wpa_sm *sm)
{
if (sm != NULL)
sm->ft_completed = 0;
}
static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
size_t gtk_elem_len)
{
u8 gtk[32];
int keyidx;
enum wpa_alg alg;
size_t gtk_len, keylen, rsc_len;
const u8 *kek;
size_t kek_len;
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
kek = sm->ptk.kek2;
kek_len = sm->ptk.kek2_len;
} else {
kek = sm->ptk.kek;
kek_len = sm->ptk.kek_len;
}
if (gtk_elem == NULL) {
wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
return 0;
}
wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
gtk_elem, gtk_elem_len);
if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
gtk_elem_len - 19 > sizeof(gtk)) {
wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
"length %lu", (unsigned long) gtk_elem_len);
return -1;
}
gtk_len = gtk_elem_len - 19;
if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt GTK");
return -1;
}
keylen = wpa_cipher_key_len(sm->group_cipher);
rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
alg = wpa_cipher_to_alg(sm->group_cipher);
if (alg == WPA_ALG_NONE) {
wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
sm->group_cipher);
return -1;
}
if (gtk_len < keylen) {
wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
return -1;
}
/* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
if (gtk_elem[2] != keylen) {
wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
"negotiated %lu",
gtk_elem[2], (unsigned long) keylen);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
if (sm->group_cipher == WPA_CIPHER_TKIP) {
/* Swap Tx/Rx keys for Michael MIC */
u8 tmp[8];
os_memcpy(tmp, gtk + 16, 8);
os_memcpy(gtk + 16, gtk + 24, 8);
os_memcpy(gtk + 24, tmp, 8);
}
if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
gtk_elem + 3, rsc_len, gtk, keylen,
KEY_FLAG_GROUP_RX) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
"driver.");
return -1;
}
return 0;
}
static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
size_t igtk_elem_len)
{
u8 igtk[WPA_IGTK_MAX_LEN];
size_t igtk_len;
u16 keyidx;
const u8 *kek;
size_t kek_len;
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
kek = sm->ptk.kek2;
kek_len = sm->ptk.kek2_len;
} else {
kek = sm->ptk.kek;
kek_len = sm->ptk.kek_len;
}
if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)
return 0;
if (igtk_elem == NULL) {
wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
return 0;
}
wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
igtk_elem, igtk_elem_len);
igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
"length %lu", (unsigned long) igtk_elem_len);
return -1;
}
if (igtk_elem[8] != igtk_len) {
wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
"%d", igtk_elem[8]);
return -1;
}
if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) {
wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
"decrypt IGTK");
return -1;
}
/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
keyidx = WPA_GET_LE16(igtk_elem);
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
igtk_len);
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
igtk_elem + 2, 6, igtk, igtk_len,
KEY_FLAG_GROUP_RX) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
"driver.");
forced_memzero(igtk, sizeof(igtk));
return -1;
}
forced_memzero(igtk, sizeof(igtk));
return 0;
}
static int wpa_ft_process_bigtk_subelem(struct wpa_sm *sm, const u8 *bigtk_elem,
size_t bigtk_elem_len)
{
u8 bigtk[WPA_BIGTK_MAX_LEN];
size_t bigtk_len;
u16 keyidx;
const u8 *kek;
size_t kek_len;
if (!sm->beacon_prot || !bigtk_elem ||
(sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256))
return 0;
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
kek = sm->ptk.kek2;
kek_len = sm->ptk.kek2_len;
} else {
kek = sm->ptk.kek;
kek_len = sm->ptk.kek_len;
}
wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp",
bigtk_elem, bigtk_elem_len);
bigtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) {
wpa_printf(MSG_DEBUG,
"FT: Invalid BIGTK sub-elem length %lu",
(unsigned long) bigtk_elem_len);
return -1;
}
if (bigtk_elem[8] != bigtk_len) {
wpa_printf(MSG_DEBUG,
"FT: Invalid BIGTK sub-elem Key Length %d",
bigtk_elem[8]);
return -1;
}
if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) {
wpa_printf(MSG_WARNING,
"FT: AES unwrap failed - could not decrypt BIGTK");
return -1;
}
/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
keyidx = WPA_GET_LE16(bigtk_elem);
wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk,
bigtk_len);
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr, keyidx, 0,
bigtk_elem + 2, 6, bigtk, bigtk_len,
KEY_FLAG_GROUP_RX) < 0) {
wpa_printf(MSG_WARNING,
"WPA: Failed to set BIGTK to the driver");
forced_memzero(bigtk, sizeof(bigtk));
return -1;
}
forced_memzero(bigtk, sizeof(bigtk));
return 0;
}
int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
size_t ies_len, const u8 *src_addr)
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
unsigned int count;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
const u8 *kck;
size_t kck_len;
int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
const u8 *anonce, *snonce, *fte_mic;
u8 fte_elem_count;
int own_rsnxe_used, rsnxe_used;
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
"enabled for this connection");
return -1;
}
if (sm->ft_reassoc_completed) {
wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission");
return 0;
}
if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
}
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
return -1;
}
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 -1;
}
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 || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
return -1;
}
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 -1;
}
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 -1;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
return -1;
}
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 -1;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
return -1;
}
if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
"ReassocResp");
return -1;
}
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 -1;
}
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 -1;
}
if (wpa_key_mgmt_fils(sm->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->own_addr, src_addr, 6,
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 -1;
}
if (os_memcmp_const(mic, fte_mic, 16) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
return -1;
}
if (rsnxe_used && !sm->ap_rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that AP uses RSNXE, but RSNXE was not included in Beacon/Probe Response frames");
return -1;
}
if (!sm->ap_rsn_ie) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"FT: No RSNE 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,
"FT: Could not find AP from the scan results");
return -1;
}
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
"FT: Found the current AP from updated scan results");
}
if (sm->ap_rsn_ie &&
wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
sm->ap_rsn_ie, sm->ap_rsn_ie_len,
parse.rsn - 2, parse.rsn_len + 2)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FT: RSNE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame");
wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
sm->ap_rsn_ie, sm->ap_rsn_ie_len);
wpa_hexdump(MSG_INFO,
"RSNE in FT protocol Reassociation Response frame",
parse.rsn ? parse.rsn - 2 : NULL,
parse.rsn ? parse.rsn_len + 2 : 0);
return -1;
}
own_rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) &&
(sm->sae_pwe == 1 || sm->sae_pwe == 2);
if ((sm->ap_rsnxe && !parse.rsnxe && own_rsnxe_used) ||
(!sm->ap_rsnxe && parse.rsnxe) ||
(sm->ap_rsnxe && parse.rsnxe &&
(sm->ap_rsnxe_len != 2 + parse.rsnxe_len ||
os_memcmp(sm->ap_rsnxe, parse.rsnxe - 2,
sm->ap_rsnxe_len) != 0))) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FT: RSNXE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame");
wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp",
sm->ap_rsnxe, sm->ap_rsnxe_len);
wpa_hexdump(MSG_INFO,
"RSNXE in FT protocol Reassociation Response frame",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
return -1;
}
#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 (Re)Assoc Response");
return -1;
}
if (ocv_verify_tx_params(parse.oci, parse.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=ft-assoc error=%s",
MAC2STR(src_addr), ocv_errorstr);
return -1;
}
}
#endif /* CONFIG_OCV */
sm->ft_reassoc_completed = 1;
if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 ||
wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 ||
wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0)
return -1;
if (sm->set_ptk_after_assoc) {
wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
"are associated");
if (wpa_ft_install_ptk(sm, src_addr) < 0)
return -1;
sm->set_ptk_after_assoc = 0;
}
if (parse.ric) {
wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
parse.ric, parse.ric_len);
/* TODO: parse response and inform driver about results when
* using wpa_supplicant SME */
}
wpa_printf(MSG_DEBUG, "FT: Completed successfully");
return 0;
}
/**
* wpa_ft_start_over_ds - Generate over-the-DS auth request
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @target_ap: Target AP Address
* @mdie: Mobility Domain IE from the target AP
* Returns: 0 on success, -1 on failure
*/
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
const u8 *mdie)
{
u8 *ft_ies;
size_t ft_ies_len;
wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
MAC2STR(target_ap));
/* Generate a new SNonce */
if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
NULL, 0, target_ap, NULL, 0, mdie, 0);
if (ft_ies) {
sm->over_the_ds_in_progress = 1;
os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
os_free(ft_ies);
}
return 0;
}
#ifdef CONFIG_PASN
static struct pasn_ft_r1kh * wpa_ft_pasn_get_r1kh(struct wpa_sm *sm,
const u8 *bssid)
{
size_t i;
for (i = 0; i < sm->n_pasn_r1kh; i++)
if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0)
return &sm->pasn_r1kh[i];
return NULL;
}
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
{
struct pasn_ft_r1kh *tmp = wpa_ft_pasn_get_r1kh(sm, bssid);
if (tmp)
return;
tmp = os_realloc_array(sm->pasn_r1kh, sm->n_pasn_r1kh + 1,
sizeof(*tmp));
if (!tmp) {
wpa_printf(MSG_DEBUG, "PASN: FT: Failed to store R1KH");
return;
}
sm->pasn_r1kh = tmp;
tmp = &sm->pasn_r1kh[sm->n_pasn_r1kh];
wpa_printf(MSG_DEBUG, "PASN: FT: Store R1KH for " MACSTR,
MAC2STR(bssid));
os_memcpy(tmp->bssid, bssid, ETH_ALEN);
os_memcpy(tmp->r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN);
sm->n_pasn_r1kh++;
}
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *bssid,
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
{
struct pasn_ft_r1kh *r1kh_entry;
if (sm->key_mgmt != (unsigned int) akmp) {
wpa_printf(MSG_DEBUG,
"PASN: FT: Key management mismatch: %u != %u",
sm->key_mgmt, akmp);
return -1;
}
r1kh_entry = wpa_ft_pasn_get_r1kh(sm, bssid);
if (!r1kh_entry) {
wpa_printf(MSG_DEBUG,
"PASN: FT: Cannot find R1KH-ID for " MACSTR,
MAC2STR(bssid));
return -1;
}
/*
* Note: PMK R0 etc. were already derived and are maintained by the
* state machine, and as the same key hierarchy is used, there is no
* need to derive them again, so only derive PMK R1 etc.
*/
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
r1kh_entry->r1kh_id, sm->own_addr, pmk_r1,
pmk_r1_name) < 0)
return -1;
*pmk_r1_len = sm->pmk_r0_len;
wpa_hexdump_key(MSG_DEBUG, "PASN: FT: PMK-R1", pmk_r1, sm->pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "PASN: FT: PMKR1Name", pmk_r1_name,
WPA_PMK_NAME_LEN);
return 0;
}
#endif /* CONFIG_PASN */
#endif /* CONFIG_IEEE80211R */
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 0db936744327..45f7e947e0fd 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1,3805 +1,3793 @@
/*
* 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);
- if (beacon == NULL)
- return -1;
probe = wpabuf_alloc(500 + vendor_len);
- if (probe == NULL) {
- wpabuf_free(beacon);
- return -1;
- }
+ 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)) {
- wpabuf_free(beacon);
- wpabuf_free(probe);
- return -1;
- }
+ 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)) {
- wpabuf_free(beacon);
- wpabuf_free(probe);
- return -1;
- }
+ 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)) {
- wpabuf_free(beacon);
- wpabuf_free(probe);
- return -1;
- }
+ wps_build_application_ext(&reg->wps->dev, probe))
+ goto fail;
beacon = wps_ie_encapsulate(beacon);
probe = wps_ie_encapsulate(probe);
- if (!beacon || !probe) {
- wpabuf_free(beacon);
- wpabuf_free(probe);
- return -1;
- }
+ 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->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)
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);
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/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
index 8b466e8aeca3..d01a1d2edcfe 100644
--- a/tests/hwsim/example-hostapd.config
+++ b/tests/hwsim/example-hostapd.config
@@ -1,115 +1,116 @@
#CC=ccache gcc
CONFIG_DRIVER_NONE=y
CONFIG_DRIVER_NL80211=y
CONFIG_RSN_PREAUTH=y
#CONFIG_TLS=internal
#CONFIG_INTERNAL_LIBTOMMATH=y
#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
CONFIG_TLS=openssl
CONFIG_EAP=y
CONFIG_ERP=y
CONFIG_EAP_MD5=y
CONFIG_EAP_TLS=y
CONFIG_EAP_MSCHAPV2=y
CONFIG_EAP_PEAP=y
CONFIG_EAP_GTC=y
CONFIG_EAP_TTLS=y
CONFIG_EAP_SIM=y
CONFIG_EAP_AKA=y
CONFIG_EAP_AKA_PRIME=y
CONFIG_EAP_GPSK=y
CONFIG_EAP_GPSK_SHA256=y
CONFIG_EAP_SAKE=y
CONFIG_EAP_PAX=y
CONFIG_EAP_PSK=y
CONFIG_EAP_VENDOR_TEST=y
CONFIG_EAP_FAST=y
CONFIG_EAP_TEAP=y
CONFIG_EAP_IKEV2=y
CONFIG_EAP_TNC=y
CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
LIBS += -rdynamic
CONFIG_EAP_UNAUTH_TLS=y
ifeq ($(CONFIG_TLS), openssl)
CONFIG_EAP_PWD=y
endif
CONFIG_EAP_EKE=y
CONFIG_PKCS12=y
CONFIG_RADIUS_SERVER=y
CONFIG_IPV6=y
CONFIG_TLSV11=y
CONFIG_TLSV12=y
CONFIG_FULL_DYNAMIC_VLAN=y
CONFIG_VLAN_NETLINK=y
CONFIG_LIBNL32=y
CONFIG_LIBNL3_ROUTE=y
CONFIG_IEEE80211R=y
CONFIG_IEEE80211AC=y
CONFIG_IEEE80211AX=y
CONFIG_OCV=y
CONFIG_WPS=y
CONFIG_WPS_UPNP=y
CONFIG_WPS_NFC=y
#CONFIG_WPS_STRICT=y
CONFIG_WPA_TRACE=y
CONFIG_WPA_TRACE_BFD=y
CONFIG_P2P_MANAGER=y
CONFIG_DEBUG_FILE=y
CONFIG_DEBUG_LINUX_TRACING=y
CONFIG_WPA_CLI_EDIT=y
CONFIG_ACS=y
CONFIG_NO_RANDOM_POOL=y
CONFIG_WNM=y
CONFIG_INTERWORKING=y
CONFIG_HS20=y
CONFIG_SQLITE=y
CONFIG_SAE=y
CONFIG_SAE_PK=y
CFLAGS += -DALL_DH_GROUPS
CONFIG_FST=y
CONFIG_FST_TEST=y
CONFIG_TESTING_OPTIONS=y
CFLAGS += -DCONFIG_RADIUS_TEST
CONFIG_MODULE_TESTS=y
CONFIG_SUITEB=y
# AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
# This can be used as a more efficient memory error detector than valgrind
# (though, with still some CPU and memory cost, so VM cases will need more
# memory allocated for the guest).
#CFLAGS += -fsanitize=address -O1 -fno-omit-frame-pointer -g
#LIBS += -fsanitize=address -fno-omit-frame-pointer -g
#LIBS_h += -fsanitize=address -fno-omit-frame-pointer -g
#LIBS_n += -fsanitize=address -fno-omit-frame-pointer -g
#LIBS_c += -fsanitize=address -fno-omit-frame-pointer -g
# Undefined Behavior Sanitizer (UBSan) can be enabled by uncommenting the
# following lines.
#CFLAGS += -Wno-format-nonliteral
#CFLAGS += -fsanitize=undefined
##CFLAGS += -fno-sanitize-recover
#LIBS += -fsanitize=undefined
##LIBS += -fno-sanitize-recover
#LIBS_h += -fsanitize=undefined
#LIBS_n += -fsanitize=undefined
#LIBS_c += -fsanitize=undefined
CONFIG_MBO=y
CONFIG_TAXONOMY=y
CONFIG_FILS=y
CONFIG_FILS_SK_PFS=y
CONFIG_OWE=y
CONFIG_DPP=y
CONFIG_DPP2=y
CONFIG_WEP=y
CONFIG_PASN=y
+CONFIG_AIRTIME_POLICY=y
diff --git a/tests/hwsim/hostapd.py b/tests/hwsim/hostapd.py
index d484f67bc47f..5ea68444c1b9 100644
--- a/tests/hwsim/hostapd.py
+++ b/tests/hwsim/hostapd.py
@@ -1,870 +1,882 @@
# Python class for controlling hostapd
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import os
import re
import time
import logging
import binascii
import struct
import tempfile
import wpaspy
import remotehost
import utils
import subprocess
logger = logging.getLogger()
hapd_ctrl = '/var/run/hostapd'
hapd_global = '/var/run/hostapd-global'
def mac2tuple(mac):
return struct.unpack('6B', binascii.unhexlify(mac.replace(':', '')))
class HostapdGlobal:
def __init__(self, apdev=None, global_ctrl_override=None):
try:
hostname = apdev['hostname']
port = apdev['port']
except:
hostname = None
port = 8878
self.host = remotehost.Host(hostname)
self.hostname = hostname
self.port = port
if hostname is None:
global_ctrl = hapd_global
if global_ctrl_override:
global_ctrl = global_ctrl_override
self.ctrl = wpaspy.Ctrl(global_ctrl)
self.mon = wpaspy.Ctrl(global_ctrl)
self.dbg = ""
else:
self.ctrl = wpaspy.Ctrl(hostname, port)
self.mon = wpaspy.Ctrl(hostname, port)
self.dbg = hostname + "/" + str(port)
self.mon.attach()
def cmd_execute(self, cmd_array, shell=False):
if self.hostname is None:
if shell:
cmd = ' '.join(cmd_array)
else:
cmd = cmd_array
proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, shell=shell)
out = proc.communicate()[0]
ret = proc.returncode
return ret, out.decode()
else:
return self.host.execute(cmd_array)
def request(self, cmd, timeout=10):
logger.debug(self.dbg + ": CTRL(global): " + cmd)
return self.ctrl.request(cmd, timeout)
def wait_event(self, events, timeout):
start = os.times()[4]
while True:
while self.mon.pending():
ev = self.mon.recv()
logger.debug(self.dbg + "(global): " + 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 add(self, ifname, driver=None):
cmd = "ADD " + ifname + " " + hapd_ctrl
if driver:
cmd += " " + driver
res = self.request(cmd)
if "OK" not in res:
raise Exception("Could not add hostapd interface " + ifname)
def add_iface(self, ifname, confname):
res = self.request("ADD " + ifname + " config=" + confname)
if "OK" not in res:
raise Exception("Could not add hostapd interface")
def add_bss(self, phy, confname, ignore_error=False):
res = self.request("ADD bss_config=" + phy + ":" + confname)
if "OK" not in res:
if not ignore_error:
raise Exception("Could not add hostapd BSS")
def remove(self, ifname):
self.request("REMOVE " + ifname, timeout=30)
def relog(self):
self.request("RELOG")
def flush(self):
self.request("FLUSH")
def get_ctrl_iface_port(self, ifname):
if self.hostname is None:
return None
res = self.request("INTERFACES ctrl")
lines = res.splitlines()
found = False
for line in lines:
words = line.split()
if words[0] == ifname:
found = True
break
if not found:
raise Exception("Could not find UDP port for " + ifname)
res = line.find("ctrl_iface=udp:")
if res == -1:
raise Exception("Wrong ctrl_interface format")
words = line.split(":")
return int(words[1])
def terminate(self):
self.mon.detach()
self.mon.close()
self.mon = None
self.ctrl.terminate()
self.ctrl = None
def send_file(self, src, dst):
self.host.send_file(src, dst)
class Hostapd:
def __init__(self, ifname, bssidx=0, hostname=None, port=8877):
self.hostname = hostname
self.host = remotehost.Host(hostname, ifname)
self.ifname = ifname
if hostname is None:
self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
self.dbg = ifname
else:
self.ctrl = wpaspy.Ctrl(hostname, port)
self.mon = wpaspy.Ctrl(hostname, port)
self.dbg = hostname + "/" + ifname
self.mon.attach()
self.bssid = None
self.bssidx = bssidx
def cmd_execute(self, cmd_array, shell=False):
if self.hostname is None:
if shell:
cmd = ' '.join(cmd_array)
else:
cmd = cmd_array
proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, shell=shell)
out = proc.communicate()[0]
ret = proc.returncode
return ret, out.decode()
else:
return self.host.execute(cmd_array)
def close_ctrl(self):
if self.mon is not None:
self.mon.detach()
self.mon.close()
self.mon = None
self.ctrl.close()
self.ctrl = None
def own_addr(self):
if self.bssid is None:
self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
return self.bssid
def get_addr(self, group=False):
return self.own_addr()
def request(self, cmd):
logger.debug(self.dbg + ": CTRL: " + cmd)
return self.ctrl.request(cmd)
def ping(self):
return "PONG" in self.request("PING")
def set(self, field, value):
if "OK" not in self.request("SET " + field + " " + value):
if "TKIP" in value and (field == "wpa_pairwise" or \
field == "rsn_pairwise"):
raise utils.HwsimSkip("Cipher TKIP not supported")
raise Exception("Failed to set hostapd parameter " + field)
def set_defaults(self):
self.set("driver", "nl80211")
self.set("hw_mode", "g")
self.set("channel", "1")
self.set("ieee80211n", "1")
self.set("logger_stdout", "-1")
self.set("logger_stdout_level", "0")
def set_open(self, ssid):
self.set_defaults()
self.set("ssid", ssid)
def set_wpa2_psk(self, ssid, passphrase):
self.set_defaults()
self.set("ssid", ssid)
self.set("wpa_passphrase", passphrase)
self.set("wpa", "2")
self.set("wpa_key_mgmt", "WPA-PSK")
self.set("rsn_pairwise", "CCMP")
def set_wpa_psk(self, ssid, passphrase):
self.set_defaults()
self.set("ssid", ssid)
self.set("wpa_passphrase", passphrase)
self.set("wpa", "1")
self.set("wpa_key_mgmt", "WPA-PSK")
self.set("wpa_pairwise", "TKIP")
def set_wpa_psk_mixed(self, ssid, passphrase):
self.set_defaults()
self.set("ssid", ssid)
self.set("wpa_passphrase", passphrase)
self.set("wpa", "3")
self.set("wpa_key_mgmt", "WPA-PSK")
self.set("wpa_pairwise", "TKIP")
self.set("rsn_pairwise", "CCMP")
def set_wep(self, ssid, key):
self.set_defaults()
self.set("ssid", ssid)
self.set("wep_key0", key)
def enable(self):
if "OK" not in self.request("ENABLE"):
raise Exception("Failed to enable hostapd interface " + self.ifname)
def disable(self):
if "OK" not in self.request("DISABLE"):
raise Exception("Failed to disable hostapd interface " + self.ifname)
def dump_monitor(self):
while self.mon.pending():
ev = self.mon.recv()
logger.debug(self.dbg + ": " + ev)
def wait_event(self, events, timeout):
if not isinstance(events, list):
raise Exception("Hostapd.wait_event() called with incorrect events argument type")
start = os.times()[4]
while True:
while self.mon.pending():
ev = self.mon.recv()
logger.debug(self.dbg + ": " + 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 wait_sta(self, addr=None, timeout=2):
ev = self.wait_event(["AP-STA-CONNECT"], timeout=timeout)
if ev is None:
raise Exception("AP did not report STA connection")
if addr and addr not in ev:
raise Exception("Unexpected STA address in connection event: " + ev)
def wait_ptkinitdone(self, addr, timeout=2):
while timeout > 0:
sta = self.get_sta(addr)
if 'hostapdWPAPTKState' not in sta:
raise Exception("GET_STA did not return hostapdWPAPTKState")
state = sta['hostapdWPAPTKState']
if state == "11":
return
time.sleep(0.1)
timeout -= 0.1
raise Exception("Timeout while waiting for PTKINITDONE")
def get_status(self):
res = self.request("STATUS")
lines = res.splitlines()
vals = dict()
for l in lines:
[name, value] = l.split('=', 1)
vals[name] = value
return vals
def get_status_field(self, field):
vals = self.get_status()
if field in vals:
return vals[field]
return None
def get_driver_status(self):
res = self.request("STATUS-DRIVER")
lines = res.splitlines()
vals = dict()
for l in lines:
[name, value] = l.split('=', 1)
vals[name] = value
return vals
def get_driver_status_field(self, field):
vals = self.get_driver_status()
if field in vals:
return vals[field]
return None
def get_config(self):
res = self.request("GET_CONFIG")
lines = res.splitlines()
vals = dict()
for l in lines:
[name, value] = l.split('=', 1)
vals[name] = value
return vals
def mgmt_rx(self, timeout=5):
ev = self.wait_event(["MGMT-RX"], timeout=timeout)
if ev is None:
return None
msg = {}
frame = binascii.unhexlify(ev.split(' ')[1])
msg['frame'] = frame
hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
msg['fc'] = hdr[0]
msg['subtype'] = (hdr[0] >> 4) & 0xf
hdr = hdr[1:]
msg['duration'] = hdr[0]
hdr = hdr[1:]
msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
hdr = hdr[6:]
msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
hdr = hdr[6:]
msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
hdr = hdr[6:]
msg['seq_ctrl'] = hdr[0]
msg['payload'] = frame[24:]
return msg
def mgmt_tx(self, msg):
t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
hdr = struct.pack('<HH6B6B6BH', *t)
res = self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']).decode())
if "OK" not in res:
raise Exception("MGMT_TX command to hostapd failed")
def get_sta(self, addr, info=None, next=False):
cmd = "STA-NEXT " if next else "STA "
if addr is None:
res = self.request("STA-FIRST")
elif info:
res = self.request(cmd + addr + " " + info)
else:
res = self.request(cmd + addr)
lines = res.splitlines()
vals = dict()
first = True
for l in lines:
if first and '=' not in l:
vals['addr'] = l
first = False
else:
[name, value] = l.split('=', 1)
vals[name] = value
return vals
def get_mib(self, param=None):
if param:
res = self.request("MIB " + param)
else:
res = self.request("MIB")
lines = res.splitlines()
vals = dict()
for l in lines:
name_val = l.split('=', 1)
if len(name_val) > 1:
vals[name_val[0]] = name_val[1]
return vals
def get_pmksa(self, addr):
res = self.request("PMKSA")
lines = res.splitlines()
for l in lines:
if addr not in l:
continue
vals = dict()
[index, aa, pmkid, expiration, opportunistic] = l.split(' ')
vals['index'] = index
vals['pmkid'] = pmkid
vals['expiration'] = expiration
vals['opportunistic'] = opportunistic
return vals
return None
def dpp_qr_code(self, uri):
res = self.request("DPP_QR_CODE " + uri)
if "FAIL" in res:
raise Exception("Failed to parse QR Code URI")
return int(res)
+ def dpp_nfc_uri(self, uri):
+ res = self.request("DPP_NFC_URI " + uri)
+ if "FAIL" in res:
+ raise Exception("Failed to parse NFC URI")
+ return int(res)
+
def dpp_bootstrap_gen(self, type="qrcode", chan=None, mac=None, info=None,
curve=None, key=None):
cmd = "DPP_BOOTSTRAP_GEN type=" + type
if chan:
cmd += " chan=" + chan
if mac:
if mac is True:
mac = self.own_addr()
cmd += " mac=" + mac.replace(':', '')
if info:
cmd += " info=" + info
if curve:
cmd += " curve=" + curve
if key:
cmd += " key=" + key
res = self.request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate bootstrapping info")
return int(res)
def dpp_bootstrap_set(self, id, conf=None, configurator=None, ssid=None,
extra=None):
cmd = "DPP_BOOTSTRAP_SET %d" % id
if ssid:
cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
if extra:
cmd += " " + extra
if conf:
cmd += " conf=" + conf
if configurator is not None:
cmd += " configurator=%d" % configurator
if "OK" not in self.request(cmd):
raise Exception("Failed to set bootstrapping parameters")
def dpp_listen(self, freq, netrole=None, qr=None, role=None):
cmd = "DPP_LISTEN " + str(freq)
if netrole:
cmd += " netrole=" + netrole
if qr:
cmd += " qr=" + qr
if role:
cmd += " role=" + role
if "OK" not in self.request(cmd):
raise Exception("Failed to start listen operation")
def dpp_auth_init(self, peer=None, uri=None, conf=None, configurator=None,
extra=None, own=None, role=None, neg_freq=None,
- ssid=None, passphrase=None, expect_fail=False):
+ ssid=None, passphrase=None, expect_fail=False,
+ conn_status=False, nfc_uri=None):
cmd = "DPP_AUTH_INIT"
if peer is None:
- peer = self.dpp_qr_code(uri)
+ if nfc_uri:
+ peer = self.dpp_nfc_uri(nfc_uri)
+ else:
+ peer = self.dpp_qr_code(uri)
cmd += " peer=%d" % peer
if own is not None:
cmd += " own=%d" % own
if role:
cmd += " role=" + role
if extra:
cmd += " " + extra
if conf:
cmd += " conf=" + conf
if configurator is not None:
cmd += " configurator=%d" % configurator
if neg_freq:
cmd += " neg_freq=%d" % neg_freq
if ssid:
cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
if passphrase:
cmd += " pass=" + binascii.hexlify(passphrase.encode()).decode()
+ if conn_status:
+ cmd += " conn_status=1"
res = self.request(cmd)
if expect_fail:
if "FAIL" not in res:
raise Exception("DPP authentication started unexpectedly")
return
if "OK" not in res:
raise Exception("Failed to initiate DPP Authentication")
def dpp_pkex_init(self, identifier, code, role=None, key=None, curve=None,
extra=None, use_id=None):
if use_id is None:
id1 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
else:
id1 = use_id
cmd = "own=%d " % id1
if identifier:
cmd += "identifier=%s " % identifier
cmd += "init=1 "
if role:
cmd += "role=%s " % role
if extra:
cmd += extra + " "
cmd += "code=%s" % code
res = self.request("DPP_PKEX_ADD " + cmd)
if "FAIL" in res:
raise Exception("Failed to set PKEX data (initiator)")
return id1
def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None,
listen_role=None):
id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
cmd = "own=%d " % id0
if identifier:
cmd += "identifier=%s " % identifier
cmd += "code=%s" % code
res = self.request("DPP_PKEX_ADD " + cmd)
if "FAIL" in res:
raise Exception("Failed to set PKEX data (responder)")
self.dpp_listen(freq, role=listen_role)
def dpp_configurator_add(self, curve=None, key=None):
cmd = "DPP_CONFIGURATOR_ADD"
if curve:
cmd += " curve=" + curve
if key:
cmd += " key=" + key
res = self.request(cmd)
if "FAIL" in res:
raise Exception("Failed to add configurator")
return int(res)
def dpp_configurator_remove(self, conf_id):
res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id)
if "OK" not in res:
raise Exception("DPP_CONFIGURATOR_REMOVE failed")
def note(self, txt):
self.request("NOTE " + txt)
def send_file(self, src, dst):
self.host.send_file(src, dst)
def get_ptksa(self, bssid, cipher):
res = self.request("PTKSA_CACHE_LIST")
lines = res.splitlines()
for l in lines:
if bssid not in l or cipher not in l:
continue
vals = dict()
[index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5)
vals['index'] = index
vals['addr'] = addr
vals['cipher'] = cipher
vals['expiration'] = expiration
vals['tk'] = tk
vals['kdk'] = kdk
return vals
return None
def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30,
global_ctrl_override=None, driver=False):
if isinstance(apdev, dict):
ifname = apdev['ifname']
try:
hostname = apdev['hostname']
port = apdev['port']
logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
except:
logger.info("Starting AP " + ifname)
hostname = None
port = 8878
else:
ifname = apdev
logger.info("Starting AP " + ifname + " (old add_ap argument type)")
hostname = None
port = 8878
hapd_global = HostapdGlobal(apdev,
global_ctrl_override=global_ctrl_override)
hapd_global.remove(ifname)
hapd_global.add(ifname, driver=driver)
port = hapd_global.get_ctrl_iface_port(ifname)
hapd = Hostapd(ifname, hostname=hostname, port=port)
if not hapd.ping():
raise Exception("Could not ping hostapd")
hapd.set_defaults()
fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
"wpa", "wpa_deny_ptk0_rekey",
"wpa_pairwise", "rsn_pairwise", "auth_server_addr",
"acct_server_addr", "osu_server_uri"]
for field in fields:
if field in params:
hapd.set(field, params[field])
for f, v in list(params.items()):
if f in fields:
continue
if isinstance(v, list):
for val in v:
hapd.set(f, val)
else:
hapd.set(f, v)
if no_enable:
return hapd
hapd.enable()
if wait_enabled:
ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
if ev is None:
raise Exception("AP startup timed out")
if "AP-ENABLED" not in ev:
raise Exception("AP startup failed")
return hapd
def add_bss(apdev, ifname, confname, ignore_error=False):
phy = utils.get_phy(apdev)
try:
hostname = apdev['hostname']
port = apdev['port']
logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
except:
logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
hostname = None
port = 8878
hapd_global = HostapdGlobal(apdev)
confname = cfg_file(apdev, confname, ifname)
hapd_global.send_file(confname, confname)
hapd_global.add_bss(phy, confname, ignore_error)
port = hapd_global.get_ctrl_iface_port(ifname)
hapd = Hostapd(ifname, hostname=hostname, port=port)
if not hapd.ping():
raise Exception("Could not ping hostapd")
return hapd
def add_iface(apdev, confname):
ifname = apdev['ifname']
try:
hostname = apdev['hostname']
port = apdev['port']
logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
except:
logger.info("Starting interface " + ifname)
hostname = None
port = 8878
hapd_global = HostapdGlobal(apdev)
confname = cfg_file(apdev, confname, ifname)
hapd_global.send_file(confname, confname)
hapd_global.add_iface(ifname, confname)
port = hapd_global.get_ctrl_iface_port(ifname)
hapd = Hostapd(ifname, hostname=hostname, port=port)
if not hapd.ping():
raise Exception("Could not ping hostapd")
return hapd
def remove_bss(apdev, ifname=None):
if ifname == None:
ifname = apdev['ifname']
try:
hostname = apdev['hostname']
port = apdev['port']
logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
except:
logger.info("Removing BSS " + ifname)
hapd_global = HostapdGlobal(apdev)
hapd_global.remove(ifname)
def terminate(apdev):
try:
hostname = apdev['hostname']
port = apdev['port']
logger.info("Terminating hostapd " + hostname + "/" + port)
except:
logger.info("Terminating hostapd")
hapd_global = HostapdGlobal(apdev)
hapd_global.terminate()
def wpa2_params(ssid=None, passphrase=None, wpa_key_mgmt="WPA-PSK",
ieee80211w=None):
params = {"wpa": "2",
"wpa_key_mgmt": wpa_key_mgmt,
"rsn_pairwise": "CCMP"}
if ssid:
params["ssid"] = ssid
if passphrase:
params["wpa_passphrase"] = passphrase
if ieee80211w is not None:
params["ieee80211w"] = ieee80211w
return params
def wpa_params(ssid=None, passphrase=None):
params = {"wpa": "1",
"wpa_key_mgmt": "WPA-PSK",
"wpa_pairwise": "TKIP"}
if ssid:
params["ssid"] = ssid
if passphrase:
params["wpa_passphrase"] = passphrase
return params
def wpa_mixed_params(ssid=None, passphrase=None):
params = {"wpa": "3",
"wpa_key_mgmt": "WPA-PSK",
"wpa_pairwise": "TKIP",
"rsn_pairwise": "CCMP"}
if ssid:
params["ssid"] = ssid
if passphrase:
params["wpa_passphrase"] = passphrase
return params
def radius_params():
params = {"auth_server_addr": "127.0.0.1",
"auth_server_port": "1812",
"auth_server_shared_secret": "radius",
"nas_identifier": "nas.w1.fi"}
return params
def wpa_eap_params(ssid=None):
params = radius_params()
params["wpa"] = "1"
params["wpa_key_mgmt"] = "WPA-EAP"
params["wpa_pairwise"] = "TKIP"
params["ieee8021x"] = "1"
if ssid:
params["ssid"] = ssid
return params
def wpa2_eap_params(ssid=None):
params = radius_params()
params["wpa"] = "2"
params["wpa_key_mgmt"] = "WPA-EAP"
params["rsn_pairwise"] = "CCMP"
params["ieee8021x"] = "1"
if ssid:
params["ssid"] = ssid
return params
def b_only_params(channel="1", ssid=None, country=None):
params = {"hw_mode": "b",
"channel": channel}
if ssid:
params["ssid"] = ssid
if country:
params["country_code"] = country
return params
def g_only_params(channel="1", ssid=None, country=None):
params = {"hw_mode": "g",
"channel": channel}
if ssid:
params["ssid"] = ssid
if country:
params["country_code"] = country
return params
def a_only_params(channel="36", ssid=None, country=None):
params = {"hw_mode": "a",
"channel": channel}
if ssid:
params["ssid"] = ssid
if country:
params["country_code"] = country
return params
def ht20_params(channel="1", ssid=None, country=None):
params = {"ieee80211n": "1",
"channel": channel,
"hw_mode": "g"}
if int(channel) > 14:
params["hw_mode"] = "a"
if ssid:
params["ssid"] = ssid
if country:
params["country_code"] = country
return params
def ht40_plus_params(channel="1", ssid=None, country=None):
params = ht20_params(channel, ssid, country)
params['ht_capab'] = "[HT40+]"
return params
def ht40_minus_params(channel="1", ssid=None, country=None):
params = ht20_params(channel, ssid, country)
params['ht_capab'] = "[HT40-]"
return params
def cmd_execute(apdev, cmd, shell=False):
hapd_global = HostapdGlobal(apdev)
return hapd_global.cmd_execute(cmd, shell=shell)
def send_file(apdev, src, dst):
hapd_global = HostapdGlobal(apdev)
return hapd_global.send_file(src, dst)
def acl_file(dev, apdev, conf):
fd, filename = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
f = os.fdopen(fd, 'w')
if conf == 'hostapd.macaddr':
mac0 = dev[0].get_status_field("address")
f.write(mac0 + '\n')
f.write("02:00:00:00:00:12\n")
f.write("02:00:00:00:00:34\n")
f.write("-02:00:00:00:00:12\n")
f.write("-02:00:00:00:00:34\n")
f.write("01:01:01:01:01:01\n")
f.write("03:01:01:01:01:03\n")
elif conf == 'hostapd.accept':
mac0 = dev[0].get_status_field("address")
mac1 = dev[1].get_status_field("address")
f.write(mac0 + " 1\n")
f.write(mac1 + " 2\n")
elif conf == 'hostapd.accept2':
mac0 = dev[0].get_status_field("address")
mac1 = dev[1].get_status_field("address")
mac2 = dev[2].get_status_field("address")
f.write(mac0 + " 1\n")
f.write(mac1 + " 2\n")
f.write(mac2 + " 3\n")
else:
f.close()
os.unlink(filename)
return conf
return filename
def bssid_inc(apdev, inc=1):
parts = apdev['bssid'].split(':')
parts[5] = '%02x' % (int(parts[5], 16) + int(inc))
bssid = '%s:%s:%s:%s:%s:%s' % (parts[0], parts[1], parts[2],
parts[3], parts[4], parts[5])
return bssid
def cfg_file(apdev, conf, ifname=None):
match = re.search(r'^bss-.+', conf)
if match:
# put cfg file in /tmp directory
fd, fname = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
f = os.fdopen(fd, 'w')
idx = ''.join(filter(str.isdigit, conf.split('-')[-1]))
if ifname is None:
ifname = apdev['ifname']
if idx != '1':
ifname = ifname + '-' + idx
f.write("driver=nl80211\n")
f.write("ctrl_interface=/var/run/hostapd\n")
f.write("hw_mode=g\n")
f.write("channel=1\n")
f.write("ieee80211n=1\n")
if conf.startswith('bss-ht40-'):
f.write("ht_capab=[HT40+]\n")
f.write("interface=%s\n" % ifname)
f.write("ssid=bss-%s\n" % idx)
if conf == 'bss-2-dup.conf':
bssid = apdev['bssid']
else:
bssid = bssid_inc(apdev, int(idx) - 1)
f.write("bssid=%s\n" % bssid)
return fname
return conf
diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py
index 9efa2b47d888..d5e1d995b81e 100644
--- a/tests/hwsim/test_ap_eap.py
+++ b/tests/hwsim/test_ap_eap.py
@@ -1,7491 +1,7492 @@
# -*- coding: utf-8 -*-
# WPA2-Enterprise tests
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import base64
import binascii
import time
import subprocess
import logging
logger = logging.getLogger()
import os
import signal
import socket
try:
import SocketServer
except ImportError:
import socketserver as SocketServer
import struct
import tempfile
import hwsim_utils
from hwsim import HWSimRadio
import hostapd
from utils import *
from wpasupplicant import WpaSupplicant
from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations, set_test_assoc_ie
try:
import OpenSSL
openssl_imported = True
except ImportError:
openssl_imported = False
def check_hlr_auc_gw_support():
if not os.path.exists("/tmp/hlr_auc_gw.sock"):
raise HwsimSkip("No hlr_auc_gw available")
def check_eap_capa(dev, method):
res = dev.get_capability("eap")
if method not in res:
raise HwsimSkip("EAP method %s not supported in the build" % method)
def check_subject_match_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
def check_check_cert_subject_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
def check_altsubject_match_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
def check_domain_match(dev):
tls = dev.request("GET tls_library")
if tls.startswith("internal"):
raise HwsimSkip("domain_match not supported with this TLS library: " + tls)
def check_domain_suffix_match(dev):
tls = dev.request("GET tls_library")
if tls.startswith("internal"):
raise HwsimSkip("domain_suffix_match not supported with this TLS library: " + tls)
def check_domain_match_full(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
def check_cert_probe_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
def check_ext_cert_check_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
def check_ocsp_support(dev):
tls = dev.request("GET tls_library")
#if tls.startswith("internal"):
# raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
#if "BoringSSL" in tls:
# raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
if tls.startswith("wolfSSL"):
raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
def check_pkcs5_v15_support(dev):
tls = dev.request("GET tls_library")
if "BoringSSL" in tls or "GnuTLS" in tls:
raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
def check_ocsp_multi_support(dev):
tls = dev.request("GET tls_library")
if not tls.startswith("internal"):
raise HwsimSkip("OCSP-multi not supported with this TLS library: " + tls)
as_hapd = hostapd.Hostapd("as")
res = as_hapd.request("GET tls_library")
del as_hapd
if not res.startswith("internal"):
raise HwsimSkip("Authentication server does not support ocsp_multi")
def check_pkcs12_support(dev):
tls = dev.request("GET tls_library")
#if tls.startswith("internal"):
# raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
if tls.startswith("wolfSSL"):
raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
def check_dh_dsa_support(dev):
tls = dev.request("GET tls_library")
if tls.startswith("internal"):
raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
def check_ec_support(dev):
tls = dev.request("GET tls_library")
if tls.startswith("internal"):
raise HwsimSkip("EC not supported with this TLS library: " + tls)
def read_pem(fname):
with open(fname, "r") as f:
lines = f.readlines()
copy = False
cert = ""
for l in lines:
if "-----END" in l:
break
if copy:
cert = cert + l
if "-----BEGIN" in l:
copy = True
return base64.b64decode(cert)
def eap_connect(dev, hapd, method, identity,
sha256=False, expect_failure=False, local_error_report=False,
maybe_local_error=False, report_failure=False,
expect_cert_error=None, **kwargs):
id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap=method, identity=identity,
wait_connect=False, scan_freq="2412", ieee80211w="1",
**kwargs)
eap_check_auth(dev, method, True, sha256=sha256,
expect_failure=expect_failure,
local_error_report=local_error_report,
maybe_local_error=maybe_local_error,
report_failure=report_failure,
expect_cert_error=expect_cert_error)
if expect_failure:
return id
if hapd:
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
return id
def eap_check_auth(dev, method, initial, rsn=True, sha256=False,
expect_failure=False, local_error_report=False,
maybe_local_error=False, report_failure=False,
expect_cert_error=None):
ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD",
"CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "CTRL-EVENT-EAP-FAILURE" in ev:
if maybe_local_error:
return
raise Exception("Could not select EAP method")
if method not in ev:
raise Exception("Unexpected EAP method")
if expect_cert_error is not None:
ev = dev.wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None or "reason=%d " % expect_cert_error not in ev:
raise Exception("Expected certificate error not reported")
if expect_failure:
ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP failure timed out")
if "CTRL-EVENT-EAP-SUCCESS" in ev:
raise Exception("Unexpected EAP success")
ev = dev.wait_disconnected(timeout=10)
if maybe_local_error and "locally_generated=1" in ev:
return
if not local_error_report:
if "reason=23" not in ev:
raise Exception("Proper reason code for disconnection not reported")
return
if report_failure:
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
if "CTRL-EVENT-EAP-SUCCESS" not in ev:
raise Exception("EAP failed")
else:
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
if initial:
ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
else:
ev = dev.wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Association with the AP timed out")
status = dev.get_status()
if status["wpa_state"] != "COMPLETED":
raise Exception("Connection not completed")
if status["suppPortStatus"] != "Authorized":
raise Exception("Port not authorized")
if "selectedMethod" not in status:
logger.info("Status: " + str(status))
raise Exception("No selectedMethod in status")
if method not in status["selectedMethod"]:
raise Exception("Incorrect EAP method status")
if sha256:
e = "WPA2-EAP-SHA256"
elif rsn:
e = "WPA2/IEEE 802.1X/EAP"
else:
e = "WPA/IEEE 802.1X/EAP"
if status["key_mgmt"] != e:
raise Exception("Unexpected key_mgmt status: " + status["key_mgmt"])
return status
def eap_reauth(dev, method, rsn=True, sha256=False, expect_failure=False):
dev.request("REAUTHENTICATE")
return eap_check_auth(dev, method, False, rsn=rsn, sha256=sha256,
expect_failure=expect_failure)
def test_ap_wpa2_eap_sim(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "SIM")
eap_connect(dev[1], hapd, "SIM", "1232010000000001",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
eap_connect(dev[2], hapd, "SIM", "1232010000000002",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
expect_failure=True)
logger.info("Negative test with incorrect key")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
expect_failure=True)
logger.info("Invalid GSM-Milenage key")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a",
expect_failure=True)
logger.info("Invalid GSM-Milenage key(2)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581",
expect_failure=True)
logger.info("Invalid GSM-Milenage key(3)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q",
expect_failure=True)
logger.info("Invalid GSM-Milenage key(4)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581",
expect_failure=True)
logger.info("Missing key configuration")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
expect_failure=True)
def test_ap_wpa2_eap_sim_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-SIM (SQL)"""
check_hlr_auc_gw_support()
try:
import sqlite3
except ImportError:
raise HwsimSkip("No sqlite3 module available")
con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['auth_server_port'] = "1814"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
logger.info("SIM fast re-authentication")
eap_reauth(dev[0], "SIM")
logger.info("SIM full auth with pseudonym")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
eap_reauth(dev[0], "SIM")
logger.info("SIM full auth with permanent identity")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
cur.execute("DELETE FROM pseudonyms WHERE permanent='1232010000000000'")
eap_reauth(dev[0], "SIM")
logger.info("SIM reauth with mismatching MK")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='1232010000000000'")
eap_reauth(dev[0], "SIM", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
eap_reauth(dev[0], "SIM")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
logger.info("SIM reauth with mismatching counter")
eap_reauth(dev[0], "SIM")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='1232010000000000'")
logger.info("SIM reauth with max reauth count reached")
eap_reauth(dev[0], "SIM")
def test_ap_wpa2_eap_sim_config(dev, apdev):
"""EAP-SIM configuration options"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="sim_min_num_chal=1",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
if ev is None:
raise Exception("No EAP error message seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="sim_min_num_chal=4",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
if ev is None:
raise Exception("No EAP error message seen (2)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="sim_min_num_chal=2")
eap_connect(dev[1], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
anonymous_identity="345678")
def test_ap_wpa2_eap_sim_id_0(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM (no pseudonym or reauth)"""
run_ap_wpa2_eap_sim_id(dev, apdev, 0)
def test_ap_wpa2_eap_sim_id_1(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM (pseudonym, no reauth)"""
run_ap_wpa2_eap_sim_id(dev, apdev, 1)
def test_ap_wpa2_eap_sim_id_2(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM (no pseudonym, reauth)"""
run_ap_wpa2_eap_sim_id(dev, apdev, 2)
def test_ap_wpa2_eap_sim_id_3(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM (pseudonym and reauth)"""
run_ap_wpa2_eap_sim_id(dev, apdev, 3)
def run_ap_wpa2_eap_sim_id(dev, apdev, eap_sim_id):
check_hlr_auc_gw_support()
params = int_eap_server_params()
params['eap_sim_id'] = str(eap_sim_id)
params['eap_sim_db'] = 'unix:/tmp/hlr_auc_gw.sock'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
eap_reauth(dev[0], "SIM")
def test_ap_wpa2_eap_sim_ext(dev, apdev):
"""WPA2-Enterprise connection using EAP-SIM and external GSM auth"""
try:
_test_ap_wpa2_eap_sim_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_ext(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("Network connected timed out")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# IK:CK:RES
resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
# This will fail during processing, but the ctrl_iface command succeeds
dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTH:" + resp)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:q"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:34"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:q"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during GSM auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233:q"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
def test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev):
"""EAP-SIM with external GSM auth and replacing SIM without clearing pseudonym id"""
try:
_test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=15)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Replace SIM, but forget to drop the previous pseudonym identity
dev[0].set_network_quoted(id, "identity", "1232010000000009")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000009 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP-Failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev):
"""EAP-SIM with external GSM auth and replacing SIM and clearing pseudonym identity"""
try:
_test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=15)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Replace SIM and drop the previous pseudonym identity
dev[0].set_network_quoted(id, "identity", "1232010000000009")
dev[0].set_network(id, "anonymous_identity", "NULL")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000009 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev):
"""EAP-SIM with external GSM auth, replacing SIM, and no identity in config"""
try:
_test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
if ev is None:
raise Exception("Request for identity timed out")
rid = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-IDENTITY-" + rid + ":1232010000000000")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=15)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Replace SIM and drop the previous permanent and pseudonym identities
dev[0].set_network(id, "identity", "NULL")
dev[0].set_network(id, "anonymous_identity", "NULL")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
if ev is None:
raise Exception("Request for identity timed out")
rid = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-IDENTITY-" + rid + ":1232010000000009")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000009 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev):
"""EAP-SIM with external GSM auth and auth failing"""
try:
_test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
rid = p[0].split('-')[3]
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-FAIL")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_change_bssid(dev, apdev):
"""EAP-SIM and external GSM auth to check fast reauth with bssid change"""
try:
_test_ap_wpa2_eap_sim_change_bssid(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_change_bssid(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=15)
hapd.wait_sta()
# Verify that EAP-SIM Reauthentication can be used after a profile change
# that does not affect EAP parameters.
dev[0].set_network(id, "bssid", "any")
eap_reauth(dev[0], "SIM")
def test_ap_wpa2_eap_sim_no_change_set(dev, apdev):
"""EAP-SIM and external GSM auth to check fast reauth with no-change SET_NETWORK"""
try:
_test_ap_wpa2_eap_sim_no_change_set(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_sim_no_change_set(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=15)
hapd.wait_sta()
# Verify that EAP-SIM Reauthentication can be used after network profile
# SET_NETWORK commands that do not actually change previously set
# parameter values.
dev[0].set_network(id, "key_mgmt", "WPA-EAP")
dev[0].set_network(id, "eap", "SIM")
dev[0].set_network_quoted(id, "identity", "1232010000000000")
dev[0].set_network_quoted(id, "ssid", "test-wpa2-eap")
eap_reauth(dev[0], "SIM")
def test_ap_wpa2_eap_sim_ext_anonymous(dev, apdev):
"""EAP-SIM with external GSM auth and anonymous identity"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
try:
run_ap_wpa2_eap_sim_ext_anonymous(dev, "anonymous@example.org")
run_ap_wpa2_eap_sim_ext_anonymous(dev, "@example.org")
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "example.org!anonymous@otherexample.org")
finally:
dev[0].request("SET external_sim 0")
def test_ap_wpa2_eap_sim_ext_anonymous_no_pseudonym(dev, apdev):
"""EAP-SIM with external GSM auth and anonymous identity without pseudonym update"""
check_hlr_auc_gw_support()
params = int_eap_server_params()
params['eap_sim_id'] = '0'
params['eap_sim_db'] = 'unix:/tmp/hlr_auc_gw.sock'
hostapd.add_ap(apdev[0], params)
try:
run_ap_wpa2_eap_sim_ext_anonymous(dev, "anonymous@example.org",
anon_id_change=False)
run_ap_wpa2_eap_sim_ext_anonymous(dev, "@example.org",
anon_id_change=False)
finally:
dev[0].request("SET external_sim 0")
def run_ap_wpa2_eap_sim_ext_anonymous(dev, anon, anon_id_change=True):
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
identity="1232010000000000",
anonymous_identity=anon,
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev[0].wait_connected(timeout=5)
anon_id = dev[0].get_network(id, "anonymous_identity").strip('"')
if anon_id_change and anon == anon_id:
raise Exception("anonymous_identity did not change")
if not anon_id_change and anon != anon_id:
raise Exception("anonymous_identity changed")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def test_ap_wpa2_eap_sim_oom(dev, apdev):
"""EAP-SIM and OOM"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = [(1, "milenage_f2345"),
(2, "milenage_f2345"),
(3, "milenage_f2345"),
(4, "milenage_f2345"),
(5, "milenage_f2345"),
(6, "milenage_f2345"),
(7, "milenage_f2345"),
(8, "milenage_f2345"),
(9, "milenage_f2345"),
(10, "milenage_f2345"),
(11, "milenage_f2345"),
(12, "milenage_f2345")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not selected")
dev[0].wait_disconnected()
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_aka(dev, apdev):
"""WPA2-Enterprise connection using EAP-AKA"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "AKA")
logger.info("Negative test with incorrect key")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
expect_failure=True)
logger.info("Invalid Milenage key")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a",
expect_failure=True)
logger.info("Invalid Milenage key(2)")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581:000000000123",
expect_failure=True)
logger.info("Invalid Milenage key(3)")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q:000000000123",
expect_failure=True)
logger.info("Invalid Milenage key(4)")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:00000000012q",
expect_failure=True)
logger.info("Invalid Milenage key(5)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581q000000000123",
expect_failure=True)
logger.info("Invalid Milenage key(6)")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581q000000000123",
expect_failure=True)
logger.info("Missing key configuration")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
expect_failure=True)
def test_ap_wpa2_eap_aka_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA (SQL)"""
check_hlr_auc_gw_support()
try:
import sqlite3
except ImportError:
raise HwsimSkip("No sqlite3 module available")
con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['auth_server_port'] = "1814"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
logger.info("AKA fast re-authentication")
eap_reauth(dev[0], "AKA")
logger.info("AKA full auth with pseudonym")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
eap_reauth(dev[0], "AKA")
logger.info("AKA full auth with permanent identity")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
cur.execute("DELETE FROM pseudonyms WHERE permanent='0232010000000000'")
eap_reauth(dev[0], "AKA")
logger.info("AKA reauth with mismatching MK")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='0232010000000000'")
eap_reauth(dev[0], "AKA", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
eap_reauth(dev[0], "AKA")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
logger.info("AKA reauth with mismatching counter")
eap_reauth(dev[0], "AKA")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='0232010000000000'")
logger.info("AKA reauth with max reauth count reached")
eap_reauth(dev[0], "AKA")
def test_ap_wpa2_eap_aka_config(dev, apdev):
"""EAP-AKA configuration options"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
anonymous_identity="2345678")
def test_ap_wpa2_eap_aka_ext(dev, apdev):
"""WPA2-Enterprise connection using EAP-AKA and external UMTS auth"""
try:
_test_ap_wpa2_eap_aka_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_aka_ext(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="AKA", key_mgmt="WPA-EAP",
identity="0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("Network connected timed out")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "UMTS-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# IK:CK:RES
resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
# This will fail during processing, but the ctrl_iface command succeeds
dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].dump_monitor()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "UMTS-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during UMTS auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:112233445566778899aabbccddee"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "UMTS-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during UMTS auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:12"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].dump_monitor()
tests = [":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344",
":UMTS-AUTH:34",
":UMTS-AUTH:00112233445566778899aabbccddeeff.00112233445566778899aabbccddeeff:0011223344",
":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddee:0011223344",
":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff.0011223344",
":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff0011223344",
":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:001122334q"]
for t in tests:
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "UMTS-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during UMTS auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + t):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(0.1)
dev[0].dump_monitor()
def test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev):
"""EAP-AKA with external UMTS auth and auth failing"""
try:
_test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="AKA", key_mgmt="WPA-EAP",
identity="0232010000000000",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
rid = p[0].split('-')[3]
dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-FAIL")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_aka_prime(dev, apdev):
"""WPA2-Enterprise connection using EAP-AKA'"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "AKA'")
logger.info("EAP-AKA' bidding protection when EAP-AKA enabled as well")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="AKA' AKA",
identity="6555444333222111@both",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
wait_connect=False, scan_freq="2412")
dev[1].wait_connected(timeout=15)
logger.info("Negative test with incorrect key")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="ff22250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
expect_failure=True)
def test_ap_wpa2_eap_aka_prime_sql(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-AKA' (SQL)"""
check_hlr_auc_gw_support()
try:
import sqlite3
except ImportError:
raise HwsimSkip("No sqlite3 module available")
con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['auth_server_port'] = "1814"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
logger.info("AKA' fast re-authentication")
eap_reauth(dev[0], "AKA'")
logger.info("AKA' full auth with pseudonym")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
eap_reauth(dev[0], "AKA'")
logger.info("AKA' full auth with permanent identity")
with con:
cur = con.cursor()
cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
cur.execute("DELETE FROM pseudonyms WHERE permanent='6555444333222111'")
eap_reauth(dev[0], "AKA'")
logger.info("AKA' reauth with mismatching k_aut")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET k_aut='0000000000000000000000000000000000000000000000000000000000000000' WHERE permanent='6555444333222111'")
eap_reauth(dev[0], "AKA'", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
eap_reauth(dev[0], "AKA'")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
logger.info("AKA' reauth with mismatching counter")
eap_reauth(dev[0], "AKA'")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
with con:
cur = con.cursor()
cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='6555444333222111'")
logger.info("AKA' reauth with max reauth count reached")
eap_reauth(dev[0], "AKA'")
def test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev):
"""EAP-AKA' with external UMTS auth and auth failing"""
try:
_test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="AKA'", key_mgmt="WPA-EAP",
identity="6555444333222111",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
rid = p[0].split('-')[3]
dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-FAIL")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_aka_prime_ext(dev, apdev):
"""EAP-AKA' with external UMTS auth to hit Synchronization-Failure"""
try:
_test_ap_wpa2_eap_aka_prime_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def _test_ap_wpa2_eap_aka_prime_ext(dev, apdev):
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
id = dev[0].connect("test-wpa2-eap", eap="AKA'", key_mgmt="WPA-EAP",
identity="6555444333222111",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("Network connected timed out")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "UMTS-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
# This will fail during UMTS auth validation
if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:112233445566778899aabbccddee"):
raise Exception("CTRL-RSP-SIM failed")
ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
def test_ap_wpa2_eap_ttls_pap(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "WPA-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-1"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-1")])
def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP and (alt)subject_match"""
check_subject_match_support(dev[0])
check_altsubject_match_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_pap_check_cert_subject(dev, apdev):
"""EAP-TTLS/PAP and check_cert_subject"""
check_check_cert_subject_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tests = ["C=FI/O=w1.fi/CN=server.w1.fi",
"C=FI/O=w1.fi",
"C=FI/CN=server.w1.fi",
"O=w1.fi/CN=server.w1.fi",
"C=FI",
"O=w1.fi",
"O=w1.*",
"CN=server.w1.fi",
"*"]
for test in tests:
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
check_cert_subject=test)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def test_ap_wpa2_eap_ttls_pap_check_cert_subject_neg(dev, apdev):
"""EAP-TTLS/PAP and check_cert_subject (negative)"""
check_check_cert_subject_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tests = ["C=US",
"C",
"C=FI1*",
"O=w1.f",
"O=w1.fi1",
"O=w1.fi/O=foo",
"O=foo/O=w1.fi",
"O=w1.fi/O=w1.fi"]
for test in tests:
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
expect_failure=True, expect_cert_error=12,
check_cert_subject=test)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_ap_wpa2_eap_ttls_pap_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP - incorrect password"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
expect_failure=True)
eap_connect(dev[1], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
expect_failure=True)
def test_ap_wpa2_eap_ttls_chap(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/CHAP"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "chap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_chap_altsubject_match(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/CHAP"""
skip_with_fips(dev[0])
check_altsubject_match_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "chap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=CHAP",
altsubject_match="EMAIL:noone@example.com;URI:http://example.com/;DNS:server.w1.fi")
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_chap_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/CHAP - incorrect password"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "chap user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
expect_failure=True)
eap_connect(dev[1], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
expect_failure=True)
def test_ap_wpa2_eap_ttls_mschap(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAP"""
skip_with_fips(dev[0])
check_domain_suffix_match(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "mschap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
domain_suffix_match="server.w1.fi")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "TTLS", "mschap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
fragment_size="200")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
eap_connect(dev[0], hapd, "TTLS", "mschap user",
anonymous_identity="ttls",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP")
def test_ap_wpa2_eap_ttls_mschap_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAP - incorrect password"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "mschap user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
expect_failure=True)
eap_connect(dev[1], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
expect_failure=True)
eap_connect(dev[2], hapd, "TTLS", "no such user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
expect_failure=True)
def test_ap_wpa2_eap_ttls_mschapv2(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
check_domain_suffix_match(dev[0])
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
domain_suffix_match="server.w1.fi")
hwsim_utils.test_connectivity(dev[0], hapd)
sta1 = hapd.get_sta(dev[0].p2p_interface_addr())
eapol1 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
eap_reauth(dev[0], "TTLS")
sta2 = hapd.get_sta(dev[0].p2p_interface_addr())
eapol2 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
if int(sta2['dot1xAuthEapolFramesRx']) <= int(sta1['dot1xAuthEapolFramesRx']):
raise Exception("dot1xAuthEapolFramesRx did not increase")
if int(eapol2['authAuthEapStartsWhileAuthenticated']) < 1:
raise Exception("authAuthEapStartsWhileAuthenticated did not increase")
if int(eapol2['backendAuthSuccesses']) <= int(eapol1['backendAuthSuccesses']):
raise Exception("backendAuthSuccesses did not increase")
logger.info("Password as hash value")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
def test_ap_wpa2_eap_ttls_invalid_phase2(dev, apdev):
"""EAP-TTLS with invalid phase2 parameter values"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = ["auth=MSCHAPv2", "auth=MSCHAPV2 autheap=MD5",
"autheap=MD5 auth=MSCHAPV2", "auth=PAP auth=CHAP",
"autheap=MD5 autheap=FOO autheap=MSCHAPV2"]
for t in tests:
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2=t,
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
if ev is None or "method=21" not in ev:
raise Exception("EAP-TTLS not started")
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method",
"CTRL-EVENT-CONNECTED"], timeout=5)
if ev is None or "CTRL-EVENT-CONNECTED" in ev:
raise Exception("No EAP-TTLS failure reported for phase2=" + t)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def test_ap_wpa2_eap_ttls_mschapv2_suffix_match(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
check_domain_match_full(dev[0])
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
domain_suffix_match="w1.fi")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_mschapv2_domain_match(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 (domain_match)"""
check_domain_match(dev[0])
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
domain_match="Server.w1.fi")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_mschapv2_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 - incorrect password"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password1",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
expect_failure=True)
eap_connect(dev[1], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
expect_failure=True)
def test_ap_wpa2_eap_ttls_mschapv2_utf8(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 and UTF-8 password"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "utf8-user-hash",
anonymous_identity="ttls", password="secret-åäö-€-password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
eap_connect(dev[1], hapd, "TTLS", "utf8-user",
anonymous_identity="ttls",
password_hex="hash:bd5844fad2489992da7fe8c5a01559cf",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
for p in ["80", "41c041e04141e041", 257*"41"]:
dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="TTLS", identity="utf8-user-hash",
anonymous_identity="ttls", password_hex=p,
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False, scan_freq="2412")
ev = dev[2].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=1)
if ev is None:
raise Exception("No failure reported")
dev[2].request("REMOVE_NETWORK all")
dev[2].wait_disconnected()
def test_ap_wpa2_eap_ttls_eap_gtc(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-GTC"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_eap_gtc_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - incorrect password"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_gtc_no_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - no password"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_gtc_server_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - server OOM"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "eap_gtc_init"):
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(hapd, 1, "eap_gtc_buildReq"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having reached
# the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
def test_ap_wpa2_eap_ttls_eap_gtc_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-GTC (OOM)"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tests = ["eap_gtc_init",
"eap_msg_alloc;eap_gtc_process"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_ttls_eap_md5(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MD5"""
check_eap_capa(dev[0], "MD5")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MD5")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_ttls_eap_md5_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - incorrect password"""
check_eap_capa(dev[0], "MD5")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_md5_no_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - no password"""
check_eap_capa(dev[0], "MD5")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_md5_server_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - server OOM"""
check_eap_capa(dev[0], "MD5")
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "eap_md5_init"):
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(hapd, 1, "eap_md5_buildReq"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having reached
# the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
def test_ap_wpa2_eap_ttls_eap_mschapv2(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "TTLS")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password1",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_mschapv2_no_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - no password"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
expect_failure=True)
def test_ap_wpa2_eap_ttls_eap_mschapv2_server_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - server OOM"""
check_eap_capa(dev[0], "MSCHAPV2")
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "eap_mschapv2_init"):
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(hapd, 1, "eap_mschapv2_build_challenge"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having reached
# the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(hapd, 1, "eap_mschapv2_build_success_req"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having reached
# the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(hapd, 1, "eap_mschapv2_build_failure_req"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="user",
anonymous_identity="ttls", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having reached
# the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_ttls_eap_sim(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-SIM"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "1232010000000000",
anonymous_identity="1232010000000000@ttls",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
ca_cert="auth_serv/ca.pem", phase2="autheap=SIM")
eap_reauth(dev[0], "TTLS")
def run_ext_sim_auth(hapd, dev):
ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15)
if ev is None:
raise Exception("Wait for external SIM processing request timed out")
p = ev.split(':', 2)
if p[1] != "GSM-AUTH":
raise Exception("Unexpected CTRL-REQ-SIM type")
rid = p[0].split('-')[3]
rand = p[2].split(' ')[0]
res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
"-m",
"auth_serv/hlr_auc_gw.milenage_db",
"GSM-AUTH-REQ 232010000000000 " + rand]).decode()
if "GSM-AUTH-RESP" not in res:
raise Exception("Unexpected hlr_auc_gw response")
resp = res.split(' ')[2].rstrip()
dev.request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
dev.wait_connected(timeout=15)
hapd.wait_sta()
dev.dump_monitor()
dev.request("REAUTHENTICATE")
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP reauthentication did not succeed")
ev = dev.wait_event(["WPA: Key negotiation completed"], timeout=5)
if ev is None:
raise Exception("Key negotiation did not complete")
dev.dump_monitor()
def test_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-SIM and external GSM auth"""
check_hlr_auc_gw_support()
try:
run_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def run_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
dev[0].connect("test-wpa2-eap", eap="TTLS", key_mgmt="WPA-EAP",
identity="1232010000000000",
anonymous_identity="1232010000000000@ttls",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
ca_cert="auth_serv/ca.pem", phase2="autheap=SIM",
wait_connect=False, scan_freq="2412")
run_ext_sim_auth(hapd, dev[0])
def test_ap_wpa2_eap_ttls_eap_vendor(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-vendor"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "vendor-test-2",
anonymous_identity="ttls",
ca_cert="auth_serv/ca.pem", phase2="autheap=VENDOR-TEST")
def test_ap_wpa2_eap_peap_eap_sim(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-SIM"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "1232010000000000",
anonymous_identity="1232010000000000@peap",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
ca_cert="auth_serv/ca.pem", phase2="auth=SIM")
eap_reauth(dev[0], "PEAP")
def test_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-SIM and external GSM auth"""
check_hlr_auc_gw_support()
try:
run_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def run_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
dev[0].connect("test-wpa2-eap", eap="PEAP", key_mgmt="WPA-EAP",
identity="1232010000000000",
anonymous_identity="1232010000000000@peap",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
ca_cert="auth_serv/ca.pem", phase2="auth=SIM",
wait_connect=False, scan_freq="2412")
run_ext_sim_auth(hapd, dev[0])
def test_ap_wpa2_eap_fast_eap_sim(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/EAP-SIM"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "1232010000000000",
anonymous_identity="1232010000000000@fast",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth_sim",
ca_cert="auth_serv/ca.pem", phase2="auth=SIM")
eap_reauth(dev[0], "FAST")
def test_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/EAP-SIM and external GSM auth"""
check_hlr_auc_gw_support()
try:
run_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev)
finally:
dev[0].request("SET external_sim 0")
def run_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET external_sim 1")
dev[0].connect("test-wpa2-eap", eap="PEAP", key_mgmt="WPA-EAP",
identity="1232010000000000",
anonymous_identity="1232010000000000@peap",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth_sim",
ca_cert="auth_serv/ca.pem", phase2="auth=SIM",
wait_connect=False, scan_freq="2412")
run_ext_sim_auth(hapd, dev[0])
def test_ap_wpa2_eap_ttls_eap_aka(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/EAP-AKA"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "0232010000000000",
anonymous_identity="0232010000000000@ttls",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
ca_cert="auth_serv/ca.pem", phase2="autheap=AKA")
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_peap_eap_aka(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-AKA"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "0232010000000000",
anonymous_identity="0232010000000000@peap",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
eap_reauth(dev[0], "PEAP")
def test_ap_wpa2_eap_fast_eap_aka(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/EAP-AKA"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "0232010000000000",
anonymous_identity="0232010000000000@fast",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth_aka",
ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
eap_reauth(dev[0], "FAST")
def test_ap_wpa2_eap_peap_eap_mschapv2(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "PEAP")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
fragment_size="200")
logger.info("Password as hash value")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password1",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
expect_failure=True)
def test_ap_wpa2_eap_peap_eap_mschapv2_domain(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 with domain"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", r"DOMAIN\user3",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "PEAP")
def test_ap_wpa2_eap_peap_eap_mschapv2_incorrect_password(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 - incorrect password"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="wrong",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
expect_failure=True)
def test_ap_wpa2_eap_peap_crypto_binding(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peapver=0 crypto_binding=2",
phase2="auth=MSCHAPV2")
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "PEAP")
eap_connect(dev[1], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peapver=0 crypto_binding=1",
phase2="auth=MSCHAPV2")
eap_connect(dev[2], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peapver=0 crypto_binding=0",
phase2="auth=MSCHAPV2")
def test_ap_wpa2_eap_peap_crypto_binding_server_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding with server OOM"""
check_eap_capa(dev[0], "MSCHAPV2")
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "eap_mschapv2_getKey"):
eap_connect(dev[0], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peapver=0 crypto_binding=2",
phase2="auth=MSCHAPV2",
expect_failure=True, local_error_report=True)
def test_ap_wpa2_eap_peap_params(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and various parameters"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="peapver=0 peaplabel=1",
expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="peap_outer_success=0",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("No EAP success seen")
# This won't succeed to connect with peap_outer_success=0, so stop here.
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
eap_connect(dev[1], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peap_outer_success=1",
phase2="auth=MSCHAPV2")
eap_connect(dev[2], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="peap_outer_success=2",
phase2="auth=MSCHAPV2")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="peapver=1 peaplabel=1",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("No EAP success seen")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=1)
if ev and "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
dev[0].request("REMOVE_NETWORK all")
dev[0].disconnect_and_stop_scan()
tests = [("peap-ver0", ""),
("peap-ver1", ""),
("peap-ver0", "peapver=0"),
("peap-ver1", "peapver=1")]
for anon, phase1 in tests:
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user", anonymous_identity=anon,
password="password", phase1=phase1,
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [("peap-ver0", "peapver=1"),
("peap-ver1", "peapver=0")]
for anon, phase1 in tests:
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user", anonymous_identity=anon,
password="password", phase1=phase1,
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
eap_connect(dev[0], hapd, "PEAP", "user", password="password",
ca_cert="auth_serv/ca.pem",
phase1="tls_allow_md5=1 tls_disable_session_ticket=1 tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=0 tls_ext_cert_check=0",
phase2="auth=MSCHAPV2")
def test_ap_wpa2_eap_peap_eap_gtc(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-GTC"""
p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], p)
eap_connect(dev[0], hapd, "PEAP", "user", phase1="peapver=1",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC")
def test_ap_wpa2_eap_peap_eap_tls(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-TLS"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "cert user",
ca_cert="auth_serv/ca.pem", phase2="auth=TLS",
ca_cert2="auth_serv/ca.pem",
client_cert2="auth_serv/user.pem",
private_key2="auth_serv/user.key")
eap_reauth(dev[0], "PEAP")
def test_ap_wpa2_eap_peap_eap_vendor(dev, apdev):
"""WPA2-Enterprise connection using EAP-PEAP/EAP-vendor"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "vendor-test-2",
ca_cert="auth_serv/ca.pem", phase2="auth=VENDOR-TEST")
def test_ap_wpa2_eap_tls(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
eap_reauth(dev[0], "TLS")
def test_eap_tls_pkcs8_pkcs5_v2_des3(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and PKCS #8, PKCS #5 v2 DES3 key"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key.pkcs8",
private_key_passwd="whatever")
def test_eap_tls_pkcs8_pkcs5_v15(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and PKCS #8, PKCS #5 v1.5 key"""
check_pkcs5_v15_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key.pkcs8.pkcs5v15",
private_key_passwd="whatever")
def test_ap_wpa2_eap_tls_blob(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and config blobs"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
cert = read_pem("auth_serv/ca.pem")
if "OK" not in dev[0].request("SET blob cacert " + binascii.hexlify(cert).decode()):
raise Exception("Could not set cacert blob")
cert = read_pem("auth_serv/user.pem")
if "OK" not in dev[0].request("SET blob usercert " + binascii.hexlify(cert).decode()):
raise Exception("Could not set usercert blob")
key = read_pem("auth_serv/user.rsa-key")
if "OK" not in dev[0].request("SET blob userkey " + binascii.hexlify(key).decode()):
raise Exception("Could not set cacert blob")
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="blob://cacert",
client_cert="blob://usercert",
private_key="blob://userkey")
def test_ap_wpa2_eap_tls_blob_missing(dev, apdev):
"""EAP-TLS and config blob missing"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert="blob://testing-blob-does-not-exist",
client_cert="blob://testing-blob-does-not-exist",
private_key="blob://testing-blob-does-not-exist",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"], timeout=10)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_tls_with_tls_len(dev, apdev):
"""EAP-TLS and TLS Message Length in unfragmented packets"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
phase1="include_tls_length=1",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
def test_ap_wpa2_eap_tls_pkcs12(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and PKCS#12"""
check_pkcs12_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"])
if ev is None:
raise Exception("Request for private key passphrase timed out")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-PASSPHRASE-" + id + ":whatever")
dev[0].wait_connected(timeout=10)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
# Run this twice to verify certificate chain handling with OpenSSL. Use two
# different files to cover both cases of the extra certificate being the
# one that signed the client certificate and it being unrelated to the
# client certificate.
for pkcs12 in "auth_serv/user2.pkcs12", "auth_serv/user3.pkcs12":
for i in range(2):
eap_connect(dev[0], hapd, "TLS", "tls user",
ca_cert="auth_serv/ca.pem",
private_key=pkcs12,
private_key_passwd="whatever")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and PKCS#12 from configuration blob"""
cert = read_pem("auth_serv/ca.pem")
cacert = binascii.hexlify(cert).decode()
run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert)
def test_ap_wpa2_eap_tls_pkcs12_blob_pem(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and PKCS#12 from configuration blob and PEM ca_cert blob"""
with open("auth_serv/ca.pem", "r") as f:
lines = f.readlines()
copy = False
cert = ""
for l in lines:
if "-----BEGIN" in l:
copy = True
if copy:
cert += l
if "-----END" in l:
copy = False
break
cacert = binascii.hexlify(cert.encode()).decode()
run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert)
def run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert):
check_pkcs12_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
if "OK" not in dev[0].request("SET blob cacert " + cacert):
raise Exception("Could not set cacert blob")
with open("auth_serv/user.pkcs12", "rb") as f:
if "OK" not in dev[0].request("SET blob pkcs12 " + binascii.hexlify(f.read()).decode()):
raise Exception("Could not set pkcs12 blob")
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="blob://cacert",
private_key="blob://pkcs12",
private_key_passwd="whatever")
def test_ap_wpa2_eap_tls_neg_incorrect_trust_root(dev, apdev):
"""WPA2-Enterprise negative test - incorrect trust root"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
cert = read_pem("auth_serv/ca-incorrect.pem")
if "OK" not in dev[0].request("SET blob cacert " + binascii.hexlify(cert).decode()):
raise Exception("Could not set cacert blob")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="blob://cacert",
wait_connect=False, scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca-incorrect.pem",
wait_connect=False, scan_freq="2412")
for dev in (dev[0], dev[1]):
ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "TTLS" not in ev:
raise Exception("Unexpected EAP method")
ev = dev.wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
raise Exception("TLS certificate error not reported")
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(2) timed out")
if "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP failure not reported")
ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(3) timed out")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Disconnection not reported")
ev = dev.wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
if ev is None:
raise Exception("Network block disabling not reported")
def test_ap_wpa2_eap_tls_diff_ca_trust(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
ca_cert="auth_serv/ca.pem",
wait_connect=True, scan_freq="2412")
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
ca_cert="auth_serv/ca-incorrect.pem",
only_add_network=True, scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
if ev is None:
raise Exception("EAP-TTLS not re-started")
ev = dev[0].wait_disconnected(timeout=15)
if "reason=23" not in ev:
raise Exception("Proper reason code for disconnection not reported")
def test_ap_wpa2_eap_tls_diff_ca_trust2(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
wait_connect=True, scan_freq="2412")
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
ca_cert="auth_serv/ca-incorrect.pem",
only_add_network=True, scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
if ev is None:
raise Exception("EAP-TTLS not re-started")
ev = dev[0].wait_disconnected(timeout=15)
if "reason=23" not in ev:
raise Exception("Proper reason code for disconnection not reported")
def test_ap_wpa2_eap_tls_diff_ca_trust3(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
ca_cert="auth_serv/ca.pem",
wait_connect=True, scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].set_network_quoted(id, "ca_cert", "auth_serv/ca-incorrect.pem")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
if ev is None:
raise Exception("EAP-TTLS not re-started")
ev = dev[0].wait_disconnected(timeout=15)
if "reason=23" not in ev:
raise Exception("Proper reason code for disconnection not reported")
def test_ap_wpa2_eap_tls_neg_suffix_match(dev, apdev):
"""WPA2-Enterprise negative test - domain suffix mismatch"""
check_domain_suffix_match(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca.pem",
domain_suffix_match="incorrect.example.com",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "TTLS" not in ev:
raise Exception("Unexpected EAP method")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
raise Exception("TLS certificate error not reported")
if "Domain suffix mismatch" not in ev:
raise Exception("Domain suffix mismatch not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(2) timed out")
if "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP failure not reported")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(3) timed out")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Disconnection not reported")
ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
if ev is None:
raise Exception("Network block disabling not reported")
def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
"""WPA2-Enterprise negative test - domain mismatch"""
check_domain_match(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca.pem",
domain_match="w1.fi",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "TTLS" not in ev:
raise Exception("Unexpected EAP method")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
raise Exception("TLS certificate error not reported")
if "Domain mismatch" not in ev:
raise Exception("Domain mismatch not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(2) timed out")
if "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP failure not reported")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(3) timed out")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Disconnection not reported")
ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
if ev is None:
raise Exception("Network block disabling not reported")
def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
"""WPA2-Enterprise negative test - subject mismatch"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca.pem",
subject_match="/C=FI/O=w1.fi/CN=example.com",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
"EAP: Failed to initialize EAP method"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "EAP: Failed to initialize EAP method" in ev:
tls = dev[0].request("GET tls_library")
if tls.startswith("OpenSSL"):
raise Exception("Failed to select EAP method")
logger.info("subject_match not supported - connection failed, so test succeeded")
return
if "TTLS" not in ev:
raise Exception("Unexpected EAP method")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
raise Exception("TLS certificate error not reported")
if "Subject mismatch" not in ev:
raise Exception("Subject mismatch not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(2) timed out")
if "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP failure not reported")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(3) timed out")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Disconnection not reported")
ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
if ev is None:
raise Exception("Network block disabling not reported")
def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
"""WPA2-Enterprise negative test - altsubject mismatch"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = ["incorrect.example.com",
"DNS:incorrect.example.com",
"DNS:w1.fi",
"DNS:erver.w1.fi"]
for match in tests:
_test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match)
def _test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca.pem",
altsubject_match=match,
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
"EAP: Failed to initialize EAP method"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
if "EAP: Failed to initialize EAP method" in ev:
tls = dev[0].request("GET tls_library")
if tls.startswith("OpenSSL"):
raise Exception("Failed to select EAP method")
logger.info("altsubject_match not supported - connection failed, so test succeeded")
return
if "TTLS" not in ev:
raise Exception("Unexpected EAP method")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
raise Exception("TLS certificate error not reported")
if "AltSubject mismatch" not in ev:
raise Exception("altsubject mismatch not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(2) timed out")
if "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP failure not reported")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("EAP result(3) timed out")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Disconnection not reported")
ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
if ev is None:
raise Exception("Network block disabling not reported")
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_unauth_tls(dev, apdev):
"""WPA2-Enterprise connection using UNAUTH-TLS"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "UNAUTH-TLS", "unauth-tls",
ca_cert="auth_serv/ca.pem")
eap_reauth(dev[0], "UNAUTH-TLS")
def test_ap_wpa2_eap_ttls_server_cert_hash(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS and server certificate hash"""
check_cert_probe_support(dev[0])
skip_with_fips(dev[0])
srv_cert_hash = "f75a953c1aa9967926525d4d860d1ff7e872f7088782f060768d12aecbd5f25e"
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="probe", ca_cert="probe://",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT depth=0"], timeout=10)
if ev is None:
raise Exception("No peer server certificate event seen")
if "hash=" + srv_cert_hash not in ev:
raise Exception("Expected server certificate hash not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "Server certificate chain probe" not in ev:
raise Exception("Server certificate probe not reported")
dev[0].wait_disconnected(timeout=10)
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
if ev is None:
raise Exception("EAP result timed out")
if "Server certificate mismatch" not in ev:
raise Exception("Server certificate mismatch not reported")
dev[0].wait_disconnected(timeout=10)
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="hash://server/sha256/" + srv_cert_hash,
phase2="auth=MSCHAPV2")
def test_ap_wpa2_eap_ttls_server_cert_hash_invalid(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS and server certificate hash (invalid config)"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="hash://server/md5/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
wait_connect=False, scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca",
wait_connect=False, scan_freq="2412")
dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
password="password", phase2="auth=MSCHAPV2",
ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6Q",
wait_connect=False, scan_freq="2412")
for i in range(0, 3):
ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[i].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 21 (TTLS)"], timeout=5)
if ev is None:
raise Exception("Did not report EAP method initialization failure")
def test_ap_wpa2_eap_pwd(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd user", password="secret password")
eap_reauth(dev[0], "PWD")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[1], hapd, "PWD",
"pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
password="secret password",
fragment_size="90")
logger.info("Negative test with incorrect password")
eap_connect(dev[2], hapd, "PWD", "pwd user", password="secret-password",
expect_failure=True, local_error_report=True)
eap_connect(dev[0], hapd, "PWD",
"pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
password="secret password",
fragment_size="31")
def test_ap_wpa2_eap_pwd_nthash(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd and NTHash"""
check_eap_capa(dev[0], "PWD")
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd-hash", password="secret password")
eap_connect(dev[1], hapd, "PWD", "pwd-hash",
password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a")
eap_connect(dev[2], hapd, "PWD", "pwd user",
password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
expect_failure=True, local_error_report=True)
def test_ap_wpa2_eap_pwd_salt_sha1(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd and salted password SHA-1"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha1",
password="secret password")
def test_ap_wpa2_eap_pwd_salt_sha256(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd and salted password SHA256"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha256",
password="secret password")
def test_ap_wpa2_eap_pwd_salt_sha512(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd and salted password SHA512"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha512",
password="secret password")
def test_ap_wpa2_eap_pwd_groups(dev, apdev):
"""WPA2-Enterprise connection using various EAP-pwd groups"""
check_eap_capa(dev[0], "PWD")
tls = dev[0].request("GET tls_library")
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
groups = [19, 20, 21]
for i in groups:
logger.info("Group %d" % i)
params['pwd_group'] = str(i)
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd user",
password="secret password",
phase1="eap_pwd_groups=0-65535")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
def test_ap_wpa2_eap_pwd_invalid_group(dev, apdev):
"""WPA2-Enterprise connection using invalid EAP-pwd group"""
check_eap_capa(dev[0], "PWD")
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
for i in [0, 25, 26, 27]:
logger.info("Group %d" % i)
params['pwd_group'] = str(i)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
identity="pwd user", password="secret password",
phase1="eap_pwd_groups=0-65535",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report (group %d)" % i)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
def test_ap_wpa2_eap_pwd_disabled_group(dev, apdev):
"""WPA2-Enterprise connection using disabled EAP-pwd group"""
check_eap_capa(dev[0], "PWD")
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
for i in [19, 21]:
logger.info("Group %d" % i)
params['pwd_group'] = str(i)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
identity="pwd user", password="secret password",
phase1="eap_pwd_groups=20",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report (group %d)" % i)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
params['pwd_group'] = "20"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
identity="pwd user", password="secret password",
phase1="eap_pwd_groups=20",
scan_freq="2412")
def test_ap_wpa2_eap_pwd_as_frag(dev, apdev):
"""WPA2-Enterprise connection using EAP-pwd with server fragmentation"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
"pwd_group": "19", "fragment_size": "40"}
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PWD", "pwd user", password="secret password")
def test_ap_wpa2_eap_gpsk(dev, apdev):
"""WPA2-Enterprise connection using EAP-GPSK"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
id = eap_connect(dev[0], hapd, "GPSK", "gpsk user",
password="abcdefghijklmnop0123456789abcdef")
eap_reauth(dev[0], "GPSK")
logger.info("Test forced algorithm selection")
for phase1 in ["cipher=1", "cipher=2"]:
dev[0].set_network_quoted(id, "phase1", phase1)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10)
logger.info("Test failed algorithm negotiation")
dev[0].set_network_quoted(id, "phase1", "cipher=9")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP failure timed out")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "GPSK", "gpsk user",
password="ffcdefghijklmnop0123456789abcdef",
expect_failure=True)
def test_ap_wpa2_eap_sake(dev, apdev):
"""WPA2-Enterprise connection using EAP-SAKE"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "SAKE", "sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
eap_reauth(dev[0], "SAKE")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "SAKE", "sake user",
password_hex="ff23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expect_failure=True)
def test_ap_wpa2_eap_eke(dev, apdev):
"""WPA2-Enterprise connection using EAP-EKE"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
id = eap_connect(dev[0], hapd, "EKE", "eke user", password="hello")
eap_reauth(dev[0], "EKE")
logger.info("Test forced algorithm selection")
for phase1 in ["dhgroup=5 encr=1 prf=2 mac=2",
"dhgroup=4 encr=1 prf=2 mac=2",
"dhgroup=3 encr=1 prf=2 mac=2",
"dhgroup=3 encr=1 prf=1 mac=1"]:
dev[0].set_network_quoted(id, "phase1", phase1)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10)
dev[0].dump_monitor()
logger.info("Test failed algorithm negotiation")
dev[0].set_network_quoted(id, "phase1", "dhgroup=9 encr=9 prf=9 mac=9")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP failure timed out")
dev[0].dump_monitor()
logger.info("Test unsupported algorithm proposals")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
phase1="dhgroup=2 encr=1 prf=1 mac=1", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
phase1="dhgroup=1 encr=1 prf=1 mac=1", expect_failure=True)
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "EKE", "eke user", password="hello1",
expect_failure=True)
@long_duration_test
def test_ap_wpa2_eap_eke_many(dev, apdev):
"""WPA2-Enterprise connection using EAP-EKE (many connections)"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
success = 0
fail = 0
for i in range(100):
for j in range(3):
dev[j].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="EKE",
identity="eke user", password="hello",
phase1="dhgroup=3 encr=1 prf=1 mac=1",
scan_freq="2412", wait_connect=False)
for j in range(3):
ev = dev[j].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=15)
if ev is None:
raise Exception("No connected/disconnected event")
if "CTRL-EVENT-DISCONNECTED" in ev:
fail += 1
# The RADIUS server limits on active sessions can be hit when
# going through this test case, so try to give some more time
# for the server to remove sessions.
logger.info("Failed to connect i=%d j=%d" % (i, j))
dev[j].request("REMOVE_NETWORK all")
time.sleep(1)
else:
success += 1
dev[j].request("REMOVE_NETWORK all")
dev[j].wait_disconnected()
dev[j].dump_monitor()
logger.info("Total success=%d failure=%d" % (success, fail))
def test_ap_wpa2_eap_eke_serverid_nai(dev, apdev):
"""WPA2-Enterprise connection using EAP-EKE with serverid NAI"""
params = int_eap_server_params()
params['server_id'] = 'example.server@w1.fi'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "EKE", "eke user", password="hello")
def test_ap_wpa2_eap_eke_server_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-EKE with server OOM"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
for count, func in [(1, "eap_eke_build_commit"),
(2, "eap_eke_build_commit"),
(3, "eap_eke_build_commit"),
(1, "eap_eke_build_confirm"),
(2, "eap_eke_build_confirm"),
(1, "eap_eke_process_commit"),
(2, "eap_eke_process_commit"),
(1, "eap_eke_process_confirm"),
(1, "eap_eke_process_identity"),
(2, "eap_eke_process_identity"),
(3, "eap_eke_process_identity"),
(4, "eap_eke_process_identity")]:
with alloc_fail(hapd, count, func):
eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
for count, func, pw in [(1, "eap_eke_init", "hello"),
(1, "eap_eke_get_session_id", "hello"),
(1, "eap_eke_getKey", "hello"),
(1, "eap_eke_build_msg", "hello"),
(1, "eap_eke_build_failure", "wrong"),
(1, "eap_eke_build_identity", "hello"),
(2, "eap_eke_build_identity", "hello")]:
with alloc_fail(hapd, count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="EKE", identity="eke user", password=pw,
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having
# reached the allocation failure.
for i in range(20):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
dev[0].request("REMOVE_NETWORK all")
for count in range(1, 1000):
try:
with alloc_fail(hapd, count, "eap_server_sm_step"):
dev[0].connect("test-wpa2-eap",
key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="EKE", identity="eke user", password=pw,
wait_connect=False, scan_freq="2412")
# This would eventually time out, but we can stop after having
# reached the allocation failure.
for i in range(10):
time.sleep(0.1)
if hapd.request("GET_ALLOC_FAIL").startswith('0'):
break
dev[0].request("REMOVE_NETWORK all")
except Exception as e:
if str(e) == "Allocation failure did not trigger":
if count < 30:
raise Exception("Too few allocation failures")
logger.info("%d allocation failures tested" % (count - 1))
break
raise e
def test_ap_wpa2_eap_ikev2(dev, apdev):
"""WPA2-Enterprise connection using EAP-IKEv2"""
check_eap_capa(dev[0], "IKEV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
password="ike password")
eap_reauth(dev[0], "IKEV2")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
password="ike password", fragment_size="50")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
password="ike-password", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
password="ike password", fragment_size="0")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_ikev2_as_frag(dev, apdev):
"""WPA2-Enterprise connection using EAP-IKEv2 with server fragmentation"""
check_eap_capa(dev[0], "IKEV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
"fragment_size": "50"}
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
password="ike password")
eap_reauth(dev[0], "IKEV2")
def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-IKEv2 and OOM"""
check_eap_capa(dev[0], "IKEV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = [(1, "dh_init"),
(2, "dh_init"),
(1, "dh_derive_shared")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
identity="ikev2 user", password="ike password",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not selected")
for i in range(10):
if "0:" in dev[0].request("GET_ALLOC_FAIL"):
break
time.sleep(0.02)
dev[0].request("REMOVE_NETWORK all")
tls = dev[0].request("GET tls_library")
if not tls.startswith("wolfSSL"):
tests = [(1, "os_get_random;dh_init")]
else:
tests = [(1, "crypto_dh_init;dh_init")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
identity="ikev2 user", password="ike password",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not selected")
for i in range(10):
if "0:" in dev[0].request("GET_FAIL"):
break
time.sleep(0.02)
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_pax(dev, apdev):
"""WPA2-Enterprise connection using EAP-PAX"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
eap_reauth(dev[0], "PAX")
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="ff23456789abcdef0123456789abcdef",
expect_failure=True)
def test_ap_wpa2_eap_psk(dev, apdev):
"""WPA2-Enterprise connection using EAP-PSK"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params["wpa_key_mgmt"] = "WPA-EAP-SHA256"
params["ieee80211w"] = "2"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef", sha256=True)
eap_reauth(dev[0], "PSK", sha256=True)
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-5"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-5")])
bss = dev[0].get_bss(apdev[0]['bssid'])
if 'flags' not in bss:
raise Exception("Could not get BSS flags from BSS table")
if "[WPA2-EAP-SHA256-CCMP]" not in bss['flags']:
raise Exception("Unexpected BSS flags: " + bss['flags'])
logger.info("Negative test with incorrect password")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
password_hex="ff23456789abcdef0123456789abcdef", sha256=True,
expect_failure=True)
def test_ap_wpa2_eap_psk_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-PSK and OOM"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = [(1, "=aes_128_eax_encrypt"),
(1, "=aes_128_eax_decrypt")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not selected")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
note="Failure not triggered: %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "aes_ctr_encrypt;aes_128_eax_encrypt"),
(1, "omac1_aes_128;aes_128_eax_encrypt"),
(2, "omac1_aes_128;aes_128_eax_encrypt"),
(3, "omac1_aes_128;aes_128_eax_encrypt"),
(1, "omac1_aes_vector"),
(1, "omac1_aes_128;aes_128_eax_decrypt"),
(2, "omac1_aes_128;aes_128_eax_decrypt"),
(3, "omac1_aes_128;aes_128_eax_decrypt"),
(1, "aes_ctr_encrypt;aes_128_eax_decrypt")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not selected")
wait_fail_trigger(dev[0], "GET_FAIL",
note="Failure not triggered: %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "aes_128_encrypt_block"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP method failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa_eap_peap_eap_mschapv2(dev, apdev):
"""WPA-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
skip_without_tkip(dev[0])
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa_eap_params(ssid="test-wpa-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user", password="password", phase2="auth=MSCHAPV2",
ca_cert="auth_serv/ca.pem", wait_connect=False,
scan_freq="2412")
eap_check_auth(dev[0], "PEAP", True, rsn=False)
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
eap_reauth(dev[0], "PEAP", rsn=False)
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-1"),
("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-1")])
status = dev[0].get_status(extra="VERBOSE")
if 'portControl' not in status:
raise Exception("portControl missing from STATUS-VERBOSE")
if status['portControl'] != 'Auto':
raise Exception("Unexpected portControl value: " + status['portControl'])
if 'eap_session_id' not in status:
raise Exception("eap_session_id missing from STATUS-VERBOSE")
if not status['eap_session_id'].startswith("19"):
raise Exception("Unexpected eap_session_id value: " + status['eap_session_id'])
def test_ap_wpa2_eap_interactive(dev, apdev):
"""WPA2-Enterprise connection using interactive identity/password entry"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tests = [("Connection with dynamic TTLS/MSCHAPv2 password entry",
"TTLS", "ttls", "DOMAIN\mschapv2 user", "auth=MSCHAPV2",
None, "password"),
("Connection with dynamic TTLS/MSCHAPv2 identity and password entry",
"TTLS", "ttls", None, "auth=MSCHAPV2",
"DOMAIN\mschapv2 user", "password"),
("Connection with dynamic TTLS/EAP-MSCHAPv2 password entry",
"TTLS", "ttls", "user", "autheap=MSCHAPV2", None, "password"),
("Connection with dynamic TTLS/EAP-MD5 password entry",
"TTLS", "ttls", "user", "autheap=MD5", None, "password"),
("Connection with dynamic PEAP/EAP-MSCHAPv2 password entry",
"PEAP", None, "user", "auth=MSCHAPV2", None, "password"),
("Connection with dynamic PEAP/EAP-GTC password entry",
"PEAP", None, "user", "auth=GTC", None, "password")]
for [desc, eap, anon, identity, phase2, req_id, req_pw] in tests:
logger.info(desc)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap=eap,
anonymous_identity=anon, identity=identity,
ca_cert="auth_serv/ca.pem", phase2=phase2,
wait_connect=False, scan_freq="2412")
if req_id:
ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
if ev is None:
raise Exception("Request for identity timed out")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
ev = dev[0].wait_event(["CTRL-REQ-PASSWORD", "CTRL-REQ-OTP"])
if ev is None:
raise Exception("Request for password timed out")
id = ev.split(':')[0].split('-')[-1]
type = "OTP" if "CTRL-REQ-OTP" in ev else "PASSWORD"
dev[0].request("CTRL-RSP-" + type + "-" + id + ":" + req_pw)
dev[0].wait_connected(timeout=10)
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_ext_enable_network_while_connected(dev, apdev):
"""WPA2-Enterprise interactive identity entry and ENABLE_NETWORK"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
id_other = dev[0].connect("other", key_mgmt="NONE", scan_freq="2412",
only_add_network=True)
req_id = "DOMAIN\mschapv2 user"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
anonymous_identity="ttls", identity=None,
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
if ev is None:
raise Exception("Request for identity timed out")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
dev[0].wait_connected(timeout=10)
if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id_other)):
raise Exception("Failed to enable network")
ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=1)
if ev is not None:
raise Exception("Unexpected reconnection attempt on ENABLE_NETWORK")
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_vendor_test(dev, apdev):
"""WPA2-Enterprise connection using EAP vendor test"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "VENDOR-TEST", "vendor-test")
eap_reauth(dev[0], "VENDOR-TEST")
eap_connect(dev[1], hapd, "VENDOR-TEST", "vendor-test",
password="pending")
def test_ap_wpa2_eap_vendor_test_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP vendor test (OOM)"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = ["eap_vendor_test_init",
"eap_msg_alloc;eap_vendor_test_process",
"eap_vendor_test_getKey"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="VENDOR-TEST", identity="vendor-test",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_fast_mschapv2_unauth_prov(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and unauthenticated provisioning"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1", pac_file="blob://fast_pac")
hwsim_utils.test_connectivity(dev[0], hapd)
res = eap_reauth(dev[0], "FAST")
if res['tls_session_reused'] != '1':
raise Exception("EAP-FAST could not use PAC session ticket")
def test_ap_wpa2_eap_fast_pac_file(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and PAC file"""
check_eap_capa(dev[0], "FAST")
pac_file = os.path.join(params['logdir'], "fast.pac")
pac_file2 = os.path.join(params['logdir'], "fast-bin.pac")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
try:
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1", pac_file=pac_file)
with open(pac_file, "r") as f:
data = f.read()
if "wpa_supplicant EAP-FAST PAC file - version 1" not in data:
raise Exception("PAC file header missing")
if "PAC-Key=" not in data:
raise Exception("PAC-Key missing from PAC file")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
pac_file=pac_file)
eap_connect(dev[1], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file=pac_file2)
dev[1].request("REMOVE_NETWORK all")
eap_connect(dev[1], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_pac_format=binary",
pac_file=pac_file2)
finally:
try:
os.remove(pac_file)
except:
pass
try:
os.remove(pac_file2)
except:
pass
def test_ap_wpa2_eap_fast_binary_pac(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST and binary PAC format"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_max_pac_list_len=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin")
res = eap_reauth(dev[0], "FAST")
if res['tls_session_reused'] != '1':
raise Exception("EAP-FAST could not use PAC session ticket")
# Verify fast_max_pac_list_len=0 special case
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_max_pac_list_len=0 fast_pac_format=binary",
pac_file="blob://fast_pac_bin")
def test_ap_wpa2_eap_fast_missing_pac_config(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST and missing PAC config"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
pac_file="blob://fast_pac_not_in_use",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_fast_binary_pac_errors(dev, apdev):
"""EAP-FAST and binary PAC errors"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tests = [(1, "=eap_fast_save_pac_bin"),
(1, "eap_fast_write_pac"),
(2, "eap_fast_write_pac"),]
for count, func in tests:
if "OK" not in dev[0].request("SET blob fast_pac_bin_errors "):
raise Exception("Could not set blob")
with alloc_fail(dev[0], count, func):
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin_errors")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = ["00", "000000000000", "6ae4920c0001",
"6ae4920c000000",
"6ae4920c0000" + "0000" + 32*"00" + "ffff" + "0000",
"6ae4920c0000" + "0000" + 32*"00" + "0001" + "0000",
"6ae4920c0000" + "0000" + 32*"00" + "0000" + "0001",
"6ae4920c0000" + "0000" + 32*"00" + "0000" + "0008" + "00040000" + "0007000100"]
for t in tests:
if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + t):
raise Exception("Could not set blob")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin_errors",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0000"
tests = [(1, "eap_fast_load_pac_bin"),
(2, "eap_fast_load_pac_bin"),
(3, "eap_fast_load_pac_bin")]
for count, func in tests:
if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
raise Exception("Could not set blob")
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin_errors",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0005" + "0011223344"
if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
raise Exception("Could not set blob")
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin_errors")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0009" + "00040000" + "0007000100"
tests = [(1, "eap_fast_pac_get_a_id"),
(2, "eap_fast_pac_get_a_id")]
for count, func in tests:
if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
raise Exception("Could not set blob")
with alloc_fail(dev[0], count, func):
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_pac_format=binary",
pac_file="blob://fast_pac_bin_errors")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_fast_text_pac_errors(dev, apdev):
"""EAP-FAST and text PAC errors"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = [(1, "eap_fast_parse_hex;eap_fast_parse_pac_key"),
(1, "eap_fast_parse_hex;eap_fast_parse_pac_opaque"),
(1, "eap_fast_parse_hex;eap_fast_parse_a_id"),
(1, "eap_fast_parse_start"),
(1, "eap_fast_save_pac")]
for count, func in tests:
dev[0].request("FLUSH")
if "OK" not in dev[0].request("SET blob fast_pac_text_errors "):
raise Exception("Could not set blob")
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_text_errors",
scan_freq="2412", wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
pac = "wpa_supplicant EAP-FAST PAC file - version 1\n"
pac += "START\n"
pac += "PAC-Type\n"
pac += "END\n"
if "OK" not in dev[0].request("SET blob fast_pac_text_errors " + binascii.hexlify(pac.encode()).decode()):
raise Exception("Could not set blob")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_text_errors",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"], timeout=5)
if ev is None:
raise Exception("Failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].request("FLUSH")
if "OK" not in dev[0].request("SET blob fast_pac_text_errors "):
raise Exception("Could not set blob")
with alloc_fail(dev[0], 1, "eap_fast_add_pac_data"):
for i in range(3):
params = int_eap_server_params()
params['ssid'] = "test-wpa2-eap-2"
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].connect("test-wpa2-eap-2", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_text_errors",
scan_freq="2412", wait_connect=False)
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
hapd2.disable()
def test_ap_wpa2_eap_fast_pac_truncate(dev, apdev):
"""EAP-FAST and PAC list truncation"""
check_eap_capa(dev[0], "FAST")
if "OK" not in dev[0].request("SET blob fast_pac_truncate "):
raise Exception("Could not set blob")
for i in range(5):
params = int_eap_server_params()
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1 fast_max_pac_list_len=2",
pac_file="blob://fast_pac_truncate",
scan_freq="2412", wait_connect=False)
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
hapd.disable()
def test_ap_wpa2_eap_fast_pac_refresh(dev, apdev):
"""EAP-FAST and PAC refresh"""
check_eap_capa(dev[0], "FAST")
if "OK" not in dev[0].request("SET blob fast_pac_refresh "):
raise Exception("Could not set blob")
for i in range(2):
params = int_eap_server_params()
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
params['pac_key_refresh_time'] = "1"
params['pac_key_lifetime'] = "10"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_refresh",
scan_freq="2412", wait_connect=False)
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
hapd.disable()
for i in range(2):
params = int_eap_server_params()
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
params['pac_key_refresh_time'] = "10"
params['pac_key_lifetime'] = "10"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_refresh",
scan_freq="2412", wait_connect=False)
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
hapd.disable()
def test_ap_wpa2_eap_fast_pac_lifetime(dev, apdev):
"""EAP-FAST and PAC lifetime"""
check_eap_capa(dev[0], "FAST")
if "OK" not in dev[0].request("SET blob fast_pac_refresh "):
raise Exception("Could not set blob")
i = 0
params = int_eap_server_params()
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
params['pac_key_refresh_time'] = "0"
params['pac_key_lifetime'] = "2"
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_refresh",
scan_freq="2412", wait_connect=False)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
time.sleep(3)
dev[0].request("PMKSA_FLUSH")
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP-Failure seen after expired PAC")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].select_network(id)
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_fast_gtc_auth_prov(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/GTC and authenticated provisioning"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2", pac_file="blob://fast_pac_auth")
hwsim_utils.test_connectivity(dev[0], hapd)
res = eap_reauth(dev[0], "FAST")
if res['tls_session_reused'] != '1':
raise Exception("EAP-FAST could not use PAC session ticket")
def test_ap_wpa2_eap_fast_gtc_identity_change(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/GTC and identity changing"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
id = eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth")
dev[0].set_network_quoted(id, "identity", "user2")
dev[0].wait_disconnected()
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("EAP-FAST not started")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_fast_prf_oom(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST and OOM in PRF"""
check_eap_capa(dev[0], "FAST")
tls = dev[0].request("GET tls_library")
if tls.startswith("OpenSSL"):
func = "tls_connection_get_eap_fast_key"
count = 2
elif tls.startswith("internal"):
func = "tls_connection_prf"
count = 1
else:
raise HwsimSkip("Unsupported TLS library")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password", ca_cert="auth_serv/ca.pem",
phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("DISCONNECT")
def test_ap_wpa2_eap_fast_server_oom(dev, apdev):
"""EAP-FAST/MSCHAPv2 and server OOM"""
check_eap_capa(dev[0], "FAST")
params = int_eap_server_params()
params['dh_file'] = 'auth_serv/dh.conf'
params['pac_opaque_encr_key'] = '000102030405060708090a0b0c0d0e0f'
params['eap_fast_a_id'] = '1011'
params['eap_fast_a_id_info'] = 'another test server'
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "tls_session_ticket_ext_cb"):
id = eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac",
expect_failure=True)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
dev[0].select_network(id, freq="2412")
def test_ap_wpa2_eap_fast_cipher_suites(dev, apdev):
"""EAP-FAST and different TLS cipher suites"""
check_eap_capa(dev[0], "FAST")
tls = dev[0].request("GET tls_library")
if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
raise HwsimSkip("TLS library is not OpenSSL or wolfSSL: " + tls)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET blob fast_pac_ciphers ")
eap_connect(dev[0], hapd, "FAST", "user",
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_ciphers")
res = dev[0].get_status_field('EAP TLS cipher')
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if res != "DHE-RSA-AES256-SHA":
raise Exception("Unexpected cipher suite for provisioning: " + res)
tests = ["DHE-RSA-AES128-SHA",
"RC4-SHA",
"AES128-SHA",
"AES256-SHA",
"DHE-RSA-AES256-SHA"]
for cipher in tests:
dev[0].dump_monitor()
logger.info("Testing " + cipher)
try:
eap_connect(dev[0], hapd, "FAST", "user",
openssl_ciphers=cipher,
anonymous_identity="FAST", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
pac_file="blob://fast_pac_ciphers",
report_failure=True)
except Exception as e:
if cipher == "RC4-SHA" and \
("Could not select EAP method" in str(e) or \
"EAP failed" in str(e)):
if "run=OpenSSL 1.1" in tls:
logger.info("Allow failure due to missing TLS library support")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
continue
raise
res = dev[0].get_status_field('EAP TLS cipher')
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if res != cipher:
raise Exception("Unexpected TLS cipher info (configured %s): %s" % (cipher, res))
def test_ap_wpa2_eap_fast_prov(dev, apdev):
"""EAP-FAST and provisioning options"""
check_eap_capa(dev[0], "FAST")
if "OK" not in dev[0].request("SET blob fast_pac_prov "):
raise Exception("Could not set blob")
i = 100
params = int_eap_server_params()
params['disable_pmksa_caching'] = '1'
params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
params['eap_fast_a_id_info'] = "test server %d" % i
params['eap_fast_prov'] = "0"
hapd = hostapd.add_ap(apdev[0], params)
logger.info("Provisioning attempt while server has provisioning disabled")
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_prov",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='failure'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
hapd.disable()
logger.info("Authenticated provisioning")
hapd.set("eap_fast_prov", "2")
hapd.enable()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='success'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
logger.info("Provisioning disabled - using previously provisioned PAC")
hapd.set("eap_fast_prov", "0")
hapd.enable()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='success'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
logger.info("Drop PAC and verify connection failure")
if "OK" not in dev[0].request("SET blob fast_pac_prov "):
raise Exception("Could not set blob")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='failure'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
hapd.disable()
logger.info("Anonymous provisioning")
hapd.set("eap_fast_prov", "1")
hapd.enable()
dev[0].set_network_quoted(id, "phase1", "fast_provisioning=1")
dev[0].select_network(id, freq="2412")
# Anonymous provisioning results in EAP-Failure first
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='failure'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_disconnected()
# And then the actual data connection
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='success'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
logger.info("Provisioning disabled - using previously provisioned PAC")
hapd.set("eap_fast_prov", "0")
hapd.enable()
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
timeout=15)
if ev is None:
raise Exception("EAP result not reported")
if "parameter='success'" not in ev:
raise Exception("Unexpected EAP result: " + ev)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def test_ap_wpa2_eap_fast_eap_vendor(dev, apdev):
"""WPA2-Enterprise connection using EAP-FAST/EAP-vendor"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "FAST", "vendor-test-2",
anonymous_identity="FAST",
phase1="fast_provisioning=2", pac_file="blob://fast_pac",
ca_cert="auth_serv/ca.pem", phase2="auth=VENDOR-TEST")
def test_ap_wpa2_eap_tls_ocsp(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and verifying OCSP"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2)
def test_ap_wpa2_eap_tls_ocsp_multi(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and verifying OCSP-multi"""
check_ocsp_multi_support(dev[0])
check_pkcs12_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2)
def int_eap_server_params():
params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ca.pem",
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key",
"dh_file": "auth_serv/dh.conf"}
return params
def run_openssl(arg):
logger.info(' '.join(arg))
cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res = cmd.stdout.read().decode() + "\n" + cmd.stderr.read().decode()
cmd.stdout.close()
cmd.stderr.close()
cmd.wait()
if cmd.returncode != 0:
raise Exception("bad return code from openssl\n\n" + res)
logger.info("openssl result:\n" + res)
def ocsp_cache_key_id(outfile):
if os.path.exists(outfile):
return
arg = ["openssl", "ocsp", "-index", "auth_serv/index.txt",
'-rsigner', 'auth_serv/ocsp-responder.pem',
'-rkey', 'auth_serv/ocsp-responder.key',
'-resp_key_id',
'-CA', 'auth_serv/ca.pem',
'-issuer', 'auth_serv/ca.pem',
'-verify_other', 'auth_serv/ca.pem',
'-trust_other',
'-ndays', '7',
'-reqin', 'auth_serv/ocsp-req.der',
'-respout', outfile]
run_openssl(arg)
def test_ap_wpa2_eap_tls_ocsp_key_id(dev, apdev, params):
"""EAP-TLS and OCSP certificate signed OCSP response using key ID"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
ocsp = os.path.join(params['logdir'], "ocsp-server-cache-key-id.der")
ocsp_cache_key_id(ocsp)
if not os.path.exists(ocsp):
raise HwsimSkip("No OCSP response available")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
scan_freq="2412")
def ocsp_req(outfile):
if os.path.exists(outfile):
return
arg = ["openssl", "ocsp",
"-reqout", outfile,
'-issuer', 'auth_serv/ca.pem',
'-sha256',
'-serial', '0xD8D3E3A6CBE3CD5F',
'-no_nonce']
run_openssl(arg)
if not os.path.exists(outfile):
raise HwsimSkip("Failed to generate OCSP request")
def ocsp_resp_ca_signed(reqfile, outfile, status):
ocsp_req(reqfile)
if os.path.exists(outfile):
return
arg = ["openssl", "ocsp",
"-index", "auth_serv/index%s.txt" % status,
"-rsigner", "auth_serv/ca.pem",
"-rkey", "auth_serv/ca-key.pem",
"-CA", "auth_serv/ca.pem",
"-ndays", "7",
"-reqin", reqfile,
"-resp_no_certs",
"-respout", outfile]
run_openssl(arg)
if not os.path.exists(outfile):
raise HwsimSkip("No OCSP response available")
def ocsp_resp_server_signed(reqfile, outfile):
ocsp_req(reqfile)
if os.path.exists(outfile):
return
arg = ["openssl", "ocsp",
"-index", "auth_serv/index.txt",
"-rsigner", "auth_serv/server.pem",
"-rkey", "auth_serv/server.key",
"-CA", "auth_serv/ca.pem",
"-ndays", "7",
"-reqin", reqfile,
"-respout", outfile]
run_openssl(arg)
if not os.path.exists(outfile):
raise HwsimSkip("No OCSP response available")
def test_ap_wpa2_eap_tls_ocsp_ca_signed_good(dev, apdev, params):
"""EAP-TLS and CA signed OCSP response (good)"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
req = os.path.join(params['logdir'], "ocsp-req.der")
ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed.der")
ocsp_resp_ca_signed(req, ocsp, "")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
scan_freq="2412")
def test_ap_wpa2_eap_tls_ocsp_ca_signed_revoked(dev, apdev, params):
"""EAP-TLS and CA signed OCSP response (revoked)"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
req = os.path.join(params['logdir'], "ocsp-req.der")
ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed-revoked.der")
ocsp_resp_ca_signed(req, ocsp, "-revoked")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
if 'certificate revoked' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_tls_ocsp_ca_signed_unknown(dev, apdev, params):
"""EAP-TLS and CA signed OCSP response (unknown)"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
req = os.path.join(params['logdir'], "ocsp-req.der")
ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed-unknown.der")
ocsp_resp_ca_signed(req, ocsp, "-unknown")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_tls_ocsp_server_signed(dev, apdev, params):
"""EAP-TLS and server signed OCSP response"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
req = os.path.join(params['logdir'], "ocsp-req.der")
ocsp = os.path.join(params['logdir'], "ocsp-resp-server-signed.der")
ocsp_resp_server_signed(req, ocsp)
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_tls_ocsp_invalid_data(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and invalid OCSP data"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["ocsp_stapling_response"] = "auth_serv/ocsp-req.der"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_tls_ocsp_invalid(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and invalid OCSP response"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-invalid"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_tls_ocsp_unknown_sign(dev, apdev):
"""WPA2-Enterprise connection using EAP-TLS and unknown OCSP signer"""
check_ocsp_support(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-unknown-sign"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def ocsp_resp_status(outfile, status):
if os.path.exists(outfile):
return
arg = ["openssl", "ocsp", "-index", "auth_serv/index-%s.txt" % status,
'-rsigner', 'auth_serv/ocsp-responder.pem',
'-rkey', 'auth_serv/ocsp-responder.key',
'-CA', 'auth_serv/ca.pem',
'-issuer', 'auth_serv/ca.pem',
'-verify_other', 'auth_serv/ca.pem',
'-trust_other',
'-ndays', '7',
'-reqin', 'auth_serv/ocsp-req.der',
'-respout', outfile]
run_openssl(arg)
def test_ap_wpa2_eap_ttls_ocsp_revoked(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-TTLS and OCSP status revoked"""
check_ocsp_support(dev[0])
ocsp = os.path.join(params['logdir'], "ocsp-server-cache-revoked.der")
ocsp_resp_status(ocsp, "revoked")
if not os.path.exists(ocsp):
raise HwsimSkip("No OCSP response available")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", ca_cert="auth_serv/ca.pem",
anonymous_identity="ttls", password="password",
phase2="auth=PAP", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
if 'certificate revoked' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_ttls_ocsp_unknown(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-TTLS and OCSP status unknown"""
check_ocsp_support(dev[0])
ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
ocsp_resp_status(ocsp, "unknown")
if not os.path.exists(ocsp):
raise HwsimSkip("No OCSP response available")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", ca_cert="auth_serv/ca.pem",
anonymous_identity="ttls", password="password",
phase2="auth=PAP", ocsp=2,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
if ev is None:
raise Exception("Timeout on EAP status")
if 'bad certificate status response' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_ttls_optional_ocsp_unknown(dev, apdev, params):
"""WPA2-Enterprise connection using EAP-TTLS and OCSP status unknown"""
check_ocsp_support(dev[0])
ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
ocsp_resp_status(ocsp, "unknown")
if not os.path.exists(ocsp):
raise HwsimSkip("No OCSP response available")
params = int_eap_server_params()
params["ocsp_stapling_response"] = ocsp
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", ca_cert="auth_serv/ca.pem",
anonymous_identity="ttls", password="password",
phase2="auth=PAP", ocsp=1, scan_freq="2412")
def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA"""
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
params["server_cert"] = "auth_serv/iCA-server/server.pem"
params["private_key"] = "auth_serv/iCA-server/server.key"
hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "GnuTLS" in tls or "wolfSSL" in tls:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user_and_ica.pem"
else:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user.pem"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert=ca_cert,
client_cert=client_cert,
private_key="auth_serv/iCA-user/user.key",
scan_freq="2412")
def root_ocsp(cert):
ca = "auth_serv/ca.pem"
fd2, fn2 = tempfile.mkstemp()
os.close(fd2)
arg = ["openssl", "ocsp", "-reqout", fn2, "-issuer", ca, "-sha256",
"-cert", cert, "-no_nonce", "-text"]
run_openssl(arg)
fd, fn = tempfile.mkstemp()
os.close(fd)
arg = ["openssl", "ocsp", "-index", "auth_serv/rootCA/index.txt",
"-rsigner", ca, "-rkey", "auth_serv/ca-key.pem",
"-CA", ca, "-issuer", ca, "-verify_other", ca, "-trust_other",
"-ndays", "7", "-reqin", fn2, "-resp_no_certs", "-respout", fn,
"-text"]
run_openssl(arg)
os.unlink(fn2)
return fn
def ica_ocsp(cert, md="-sha256"):
prefix = "auth_serv/iCA-server/"
ca = prefix + "cacert.pem"
cert = prefix + cert
fd2, fn2 = tempfile.mkstemp()
os.close(fd2)
arg = ["openssl", "ocsp", "-reqout", fn2, "-issuer", ca, md,
"-cert", cert, "-no_nonce", "-text"]
run_openssl(arg)
fd, fn = tempfile.mkstemp()
os.close(fd)
arg = ["openssl", "ocsp", "-index", prefix + "index.txt",
"-rsigner", ca, "-rkey", prefix + "private/cakey.pem",
"-CA", ca, "-issuer", ca, "-verify_other", ca, "-trust_other",
"-ndays", "7", "-reqin", fn2, "-resp_no_certs", "-respout", fn,
"-text"]
run_openssl(arg)
os.unlink(fn2)
return fn
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP on server certificate"""
run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha256")
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP on server certificate )SHA1)"""
run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
params["server_cert"] = "auth_serv/iCA-server/server.pem"
params["private_key"] = "auth_serv/iCA-server/server.key"
fn = ica_ocsp("server.pem", md)
params["ocsp_stapling_response"] = fn
try:
hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "GnuTLS" in tls or "wolfSSL" in tls:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user_and_ica.pem"
else:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user.pem"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert=ca_cert,
client_cert=client_cert,
private_key="auth_serv/iCA-user/user.key",
scan_freq="2412", ocsp=2)
finally:
os.unlink(fn)
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP on revoked server certificate"""
run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params,
"-sha256")
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked_sha1(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP on revoked server certificate (SHA1)"""
run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params,
"-sha1")
def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
check_ocsp_support(dev[0])
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
params["server_cert"] = "auth_serv/iCA-server/server-revoked.pem"
params["private_key"] = "auth_serv/iCA-server/server-revoked.key"
fn = ica_ocsp("server-revoked.pem", md)
params["ocsp_stapling_response"] = fn
try:
hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "GnuTLS" in tls or "wolfSSL" in tls:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user_and_ica.pem"
else:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user.pem"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert=ca_cert,
client_cert=client_cert,
private_key="auth_serv/iCA-user/user.key",
scan_freq="2412", ocsp=1, wait_connect=False)
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
"CTRL-EVENT-EAP-SUCCESS"])
if ev is None:
raise Exception("Timeout on EAP status")
if "CTRL-EVENT-EAP-SUCCESS" in ev:
raise Exception("Unexpected EAP-Success")
if 'bad certificate status response' in ev:
break
if 'certificate revoked' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
os.unlink(fn)
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP multi missing response"""
check_ocsp_support(dev[0])
check_ocsp_multi_support(dev[0])
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
params["server_cert"] = "auth_serv/iCA-server/server.pem"
params["private_key"] = "auth_serv/iCA-server/server.key"
fn = ica_ocsp("server.pem")
params["ocsp_stapling_response"] = fn
try:
hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "GnuTLS" in tls or "wolfSSL" in tls:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user_and_ica.pem"
else:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user.pem"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert=ca_cert,
client_cert=client_cert,
private_key="auth_serv/iCA-user/user.key",
scan_freq="2412", ocsp=3, wait_connect=False)
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
"CTRL-EVENT-EAP-SUCCESS"])
if ev is None:
raise Exception("Timeout on EAP status")
if "CTRL-EVENT-EAP-SUCCESS" in ev:
raise Exception("Unexpected EAP-Success")
if 'bad certificate status response' in ev:
break
if 'certificate revoked' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
os.unlink(fn)
def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
"""EAP-TLS with intermediate server/user CA and OCSP multi OK"""
check_ocsp_support(dev[0])
check_ocsp_multi_support(dev[0])
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
params["server_cert"] = "auth_serv/iCA-server/server.pem"
params["private_key"] = "auth_serv/iCA-server/server.key"
fn = ica_ocsp("server.pem")
fn2 = root_ocsp("auth_serv/iCA-server/cacert.pem")
params["ocsp_stapling_response"] = fn
with open(fn, "rb") as f:
resp_server = f.read()
with open(fn2, "rb") as f:
resp_ica = f.read()
fd3, fn3 = tempfile.mkstemp()
try:
f = os.fdopen(fd3, 'wb')
f.write(struct.pack(">L", len(resp_server))[1:4])
f.write(resp_server)
f.write(struct.pack(">L", len(resp_ica))[1:4])
f.write(resp_ica)
f.close()
params["ocsp_stapling_response_multi"] = fn3
hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "GnuTLS" in tls or "wolfSSL" in tls:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user_and_ica.pem"
else:
ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
client_cert = "auth_serv/iCA-user/user.pem"
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert=ca_cert,
client_cert=client_cert,
private_key="auth_serv/iCA-user/user.key",
scan_freq="2412", ocsp=3)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
os.unlink(fn)
os.unlink(fn2)
os.unlink(fn3)
def test_ap_wpa2_eap_tls_ocsp_multi_revoked(dev, apdev, params):
"""EAP-TLS and CA signed OCSP multi response (revoked)"""
check_ocsp_support(dev[0])
check_ocsp_multi_support(dev[0])
check_pkcs12_support(dev[0])
req = os.path.join(params['logdir'], "ocsp-req.der")
ocsp_revoked = os.path.join(params['logdir'],
"ocsp-resp-ca-signed-revoked.der")
ocsp_unknown = os.path.join(params['logdir'],
"ocsp-resp-ca-signed-unknown.der")
ocsp_resp_ca_signed(req, ocsp_revoked, "-revoked")
ocsp_resp_ca_signed(req, ocsp_unknown, "-unknown")
with open(ocsp_revoked, "rb") as f:
resp_revoked = f.read()
with open(ocsp_unknown, "rb") as f:
resp_unknown = f.read()
fd, fn = tempfile.mkstemp()
try:
# This is not really a valid order of the OCSPResponse items in the
# list, but this works for now to verify parsing and processing of
# multiple responses.
f = os.fdopen(fd, 'wb')
f.write(struct.pack(">L", len(resp_unknown))[1:4])
f.write(resp_unknown)
f.write(struct.pack(">L", len(resp_revoked))[1:4])
f.write(resp_revoked)
f.write(struct.pack(">L", 0)[1:4])
f.write(struct.pack(">L", len(resp_unknown))[1:4])
f.write(resp_unknown)
f.close()
params = int_eap_server_params()
params["ocsp_stapling_response_multi"] = fn
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever", ocsp=1,
wait_connect=False, scan_freq="2412")
count = 0
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
"CTRL-EVENT-EAP-SUCCESS"])
if ev is None:
raise Exception("Timeout on EAP status")
if "CTRL-EVENT-EAP-SUCCESS" in ev:
raise Exception("Unexpected EAP-Success")
if 'bad certificate status response' in ev:
break
if 'certificate revoked' in ev:
break
count = count + 1
if count > 10:
raise Exception("Unexpected number of EAP status messages")
finally:
os.unlink(fn)
def test_ap_wpa2_eap_tls_domain_suffix_match_cn_full(dev, apdev):
"""WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
check_domain_match_full(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-no-dnsname.pem"
params["private_key"] = "auth_serv/server-no-dnsname.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_suffix_match="server3.w1.fi",
scan_freq="2412")
def test_ap_wpa2_eap_tls_domain_match_cn(dev, apdev):
"""WPA2-Enterprise using EAP-TLS and domainmatch (CN)"""
check_domain_match(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-no-dnsname.pem"
params["private_key"] = "auth_serv/server-no-dnsname.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_match="server3.w1.fi",
scan_freq="2412")
def test_ap_wpa2_eap_tls_domain_suffix_match_cn(dev, apdev):
"""WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
check_domain_match_full(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-no-dnsname.pem"
params["private_key"] = "auth_serv/server-no-dnsname.key"
hostapd.add_ap(apdev[0], params)
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_suffix_match="w1.fi",
scan_freq="2412")
def test_ap_wpa2_eap_tls_domain_suffix_mismatch_cn(dev, apdev):
"""WPA2-Enterprise using EAP-TLS and domain suffix mismatch (CN)"""
check_domain_suffix_match(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-no-dnsname.pem"
params["private_key"] = "auth_serv/server-no-dnsname.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_suffix_match="example.com",
wait_connect=False,
scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_suffix_match="erver3.w1.fi",
wait_connect=False,
scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report (2)")
def test_ap_wpa2_eap_tls_domain_mismatch_cn(dev, apdev):
"""WPA2-Enterprise using EAP-TLS and domain mismatch (CN)"""
check_domain_match(dev[0])
check_pkcs12_support(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-no-dnsname.pem"
params["private_key"] = "auth_serv/server-no-dnsname.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_match="example.com",
wait_connect=False,
scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
private_key="auth_serv/user.pkcs12",
private_key_passwd="whatever",
domain_match="w1.fi",
wait_connect=False,
scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report (2)")
def test_ap_wpa2_eap_ttls_expired_cert(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and expired certificate"""
skip_with_fips(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-expired.pem"
params["private_key"] = "auth_serv/server-expired.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
wait_connect=False,
scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"])
if ev is None:
raise Exception("Timeout on EAP certificate error report")
if "reason=4" not in ev or "certificate has expired" not in ev:
raise Exception("Unexpected failure reason: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_ttls_ignore_expired_cert(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and ignore certificate expiration"""
skip_with_fips(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-expired.pem"
params["private_key"] = "auth_serv/server-expired.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
phase1="tls_disable_time_checks=1",
scan_freq="2412")
def test_ap_wpa2_eap_ttls_long_duration(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and long certificate duration"""
skip_with_fips(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-long-duration.pem"
params["private_key"] = "auth_serv/server-long-duration.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
scan_freq="2412")
def test_ap_wpa2_eap_ttls_server_cert_eku_client(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and server cert with client EKU"""
skip_with_fips(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-eku-client.pem"
params["private_key"] = "auth_serv/server-eku-client.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
wait_connect=False,
scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("Timeout on EAP failure report")
def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and server cert with client and server EKU"""
skip_with_fips(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-eku-client-server.pem"
params["private_key"] = "auth_serv/server-eku-client-server.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
scan_freq="2412")
def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
skip_with_fips(dev[0])
params = int_eap_server_params()
del params["server_cert"]
params["private_key"] = "auth_serv/server.pkcs12"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
scan_freq="2412")
def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
"""EAP-TTLS and server PKCS#12 file with extra certs"""
skip_with_fips(dev[0])
params = int_eap_server_params()
del params["server_cert"]
params["private_key"] = "auth_serv/server-extra.pkcs12"
params["private_key_passwd"] = "whatever"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
scan_freq="2412")
def test_ap_wpa2_eap_ttls_dh_params(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=PAP",
dh_file="auth_serv/dh.conf")
def test_ap_wpa2_eap_ttls_dh_params_dsa(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS and setting DH params (DSA)"""
check_dh_dsa_support(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=PAP",
dh_file="auth_serv/dsaparam.pem")
def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
"""EAP-TTLS and DH params file not found"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
dh_file="auth_serv/dh-no-such-file.conf",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("EAP failure timed out")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
"""EAP-TTLS and invalid DH params file"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
dh_file="auth_serv/ca.pem",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("EAP failure timed out")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_ttls_dh_params_blob(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params from blob"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dh = read_pem("auth_serv/dh2.conf")
if "OK" not in dev[0].request("SET blob dhparams " + binascii.hexlify(dh).decode()):
raise Exception("Could not set dhparams blob")
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=PAP",
dh_file="blob://dhparams")
def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and alternative server dhparams"""
params = int_eap_server_params()
params["dh_file"] = "auth_serv/dh2.conf"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=PAP")
def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
"""WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
params = int_eap_server_params()
params["dh_file"] = "auth_serv/dsaparam.pem"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=PAP")
def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
"""EAP-TLS server and dhparams file not found"""
params = int_eap_server_params()
params["dh_file"] = "auth_serv/dh-no-such-file.conf"
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted")
def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
"""EAP-TLS server and invalid dhparams file"""
params = int_eap_server_params()
params["dh_file"] = "auth_serv/ca.pem"
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted")
def test_ap_wpa2_eap_reauth(dev, apdev):
"""WPA2-Enterprise and Authenticator forcing reauthentication"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['eap_reauth_period'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
logger.info("Wait for reauthentication")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
if ev is None:
raise Exception("Timeout on reauthentication")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("Timeout on reauthentication")
for i in range(0, 20):
state = dev[0].get_status_field("wpa_state")
if state == "COMPLETED":
break
time.sleep(0.1)
if state != "COMPLETED":
raise Exception("Reauthentication did not complete")
def test_ap_wpa2_eap_reauth_ptk_rekey_blocked_ap(dev, apdev):
"""WPA2-Enterprise and Authenticator forcing reauthentication with PTK rekey blocked on AP"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['eap_reauth_period'] = '2'
params['wpa_deny_ptk0_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
logger.info("Wait for disconnect due to reauth")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("Timeout on reauthentication")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Reauthentication without disconnect")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
if ev is None:
raise Exception("Timeout on reconnect")
def test_ap_wpa2_eap_reauth_ptk_rekey_blocked_sta(dev, apdev):
"""WPA2-Enterprise and Authenticator forcing reauthentication with PTK rekey blocked on station"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['eap_reauth_period'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wpa_deny_ptk0_rekey="2")
logger.info("Wait for disconnect due to reauth")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("Timeout on reauthentication")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Reauthentication without disconnect")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
if ev is None:
raise Exception("Timeout on reconnect")
def test_ap_wpa2_eap_request_identity_message(dev, apdev):
"""Optional displayable message in EAP Request-Identity"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['eap_message'] = 'hello\\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
def test_ap_wpa2_eap_sim_aka_result_ind(dev, apdev):
"""WPA2-Enterprise using EAP-SIM/AKA and protected result indication"""
check_hlr_auc_gw_support()
params = int_eap_server_params()
params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
params['eap_sim_aka_result_ind'] = "1"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
phase1="result_ind=1")
eap_reauth(dev[0], "SIM")
eap_connect(dev[1], hapd, "SIM", "1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
phase1="result_ind=1")
eap_reauth(dev[0], "AKA")
eap_connect(dev[1], hapd, "AKA", "0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
phase1="result_ind=1")
eap_reauth(dev[0], "AKA'")
eap_connect(dev[1], hapd, "AKA'", "6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
def test_ap_wpa2_eap_sim_zero_db_timeout(dev, apdev):
"""WPA2-Enterprise using EAP-SIM with zero database timeout"""
check_hlr_auc_gw_support()
params = int_eap_server_params()
params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
params['eap_sim_db_timeout'] = "0"
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
# Run multiple iterations to make it more likely to hit the case where the
# DB request times out and response is lost.
for i in range(20):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False, scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"],
timeout=15)
if ev is None:
raise Exception("No connection result")
dev[0].request("REMOVE_NETWORK all")
if "CTRL-EVENT-DISCONNECTED" in ev:
break
dev[0].wait_disconnected()
hapd.ping()
def test_ap_wpa2_eap_too_many_roundtrips(dev, apdev):
"""WPA2-Enterprise connection resulting in too many EAP roundtrips"""
skip_with_fips(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="mschap user",
wait_connect=False, scan_freq="2412", ieee80211w="1",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
fragment_size="4")
ev = dev[0].wait_event(["EAP: more than",
"CTRL-EVENT-EAP-SUCCESS"], timeout=20)
if ev is None or "EAP: more than" not in ev:
raise Exception("EAP roundtrip limit not reached")
def test_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev):
"""WPA2-Enterprise connection resulting in too many EAP roundtrips (server)"""
run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, 10, 10)
def test_ap_wpa2_eap_too_many_roundtrips_server2(dev, apdev):
"""WPA2-Enterprise connection resulting in too many EAP roundtrips (server)"""
run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, 10, 1)
def run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, max_rounds,
max_rounds_short):
skip_with_fips(dev[0])
params = int_eap_server_params()
params["max_auth_rounds"] = str(max_rounds)
params["max_auth_rounds_short"] = str(max_rounds_short)
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="TTLS", identity="mschap user",
wait_connect=False, scan_freq="2412", ieee80211w="1",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
fragment_size="4")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-EAP-SUCCESS"], timeout=10)
dev[0].request("DISCONNECT")
if ev is None or "SUCCESS" in ev:
raise Exception("EAP roundtrip limit not reported")
def test_ap_wpa2_eap_expanded_nak(dev, apdev):
"""WPA2-Enterprise connection with EAP resulting in expanded NAK"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="PSK", identity="vendor-test",
password_hex="ff23456789abcdef0123456789abcdef",
wait_connect=False)
found = False
for i in range(0, 5):
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
if "refuse proposed method" in ev:
found = True
break
if not found:
raise Exception("Unexpected EAP status: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
if ev is None:
raise Exception("EAP failure timed out")
def test_ap_wpa2_eap_sql(dev, apdev, params):
"""WPA2-Enterprise connection using SQLite for user DB"""
skip_with_fips(dev[0])
try:
import sqlite3
except ImportError:
raise HwsimSkip("No sqlite3 module available")
dbfile = os.path.join(params['logdir'], "eap-user.db")
try:
os.remove(dbfile)
except:
pass
con = sqlite3.connect(dbfile)
with con:
cur = con.cursor()
cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER)")
cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-pap','TTLS-PAP','password',1)")
cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-chap','TTLS-CHAP','password',1)")
cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschap','TTLS-MSCHAP','password',1)")
cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1)")
cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
try:
params = int_eap_server_params()
params["eap_user_file"] = "sqlite:" + dbfile
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "user-mschapv2",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
dev[0].request("REMOVE_NETWORK all")
eap_connect(dev[1], hapd, "TTLS", "user-mschap",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP")
dev[1].request("REMOVE_NETWORK all")
eap_connect(dev[0], hapd, "TTLS", "user-chap",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=CHAP")
eap_connect(dev[1], hapd, "TTLS", "user-pap",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
hapd.disable()
hapd.enable()
eap_connect(dev[0], hapd, "TTLS", "user-mschapv2",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
finally:
os.remove(dbfile)
def test_ap_wpa2_eap_non_ascii_identity(dev, apdev):
"""WPA2-Enterprise connection attempt using non-ASCII identity"""
params = int_eap_server_params()
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="\x80", password="password", wait_connect=False)
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="a\x80", password="password", wait_connect=False)
for i in range(0, 2):
ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
"""WPA2-Enterprise connection attempt using non-ASCII identity"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="\x80", password="password", wait_connect=False)
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="a\x80", password="password", wait_connect=False)
for i in range(0, 2):
ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
if ev is None:
raise Exception("Association and EAP start timed out")
ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
if ev is None:
raise Exception("EAP method selection timed out")
def test_openssl_cipher_suite_config_wpas(dev, apdev):
"""OpenSSL cipher suite configuration on wpa_supplicant"""
tls = dev[0].request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("TLS library is not OpenSSL: " + tls)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
openssl_ciphers="AES128",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
eap_connect(dev[1], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
openssl_ciphers="EXPORT",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
expect_failure=True, maybe_local_error=True)
dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password",
openssl_ciphers="FOO",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
wait_connect=False)
ev = dev[2].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP failure after invalid openssl_ciphers not reported")
dev[2].request("DISCONNECT")
def test_openssl_cipher_suite_config_hapd(dev, apdev):
"""OpenSSL cipher suite configuration on hostapd"""
tls = dev[0].request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
params = int_eap_server_params()
params['openssl_ciphers'] = "AES256"
hapd = hostapd.add_ap(apdev[0], params)
tls = hapd.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
eap_connect(dev[1], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
openssl_ciphers="AES128",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
expect_failure=True)
eap_connect(dev[2], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
openssl_ciphers="HIGH:!ADH",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
params['openssl_ciphers'] = "FOO"
hapd2 = hostapd.add_ap(apdev[1], params, no_enable=True)
if "FAIL" not in hapd2.request("ENABLE"):
if "run=OpenSSL 1.1.1" in tls:
logger.info("Ignore acceptance of an invalid openssl_ciphers value with OpenSSL 1.1.1")
else:
raise Exception("Invalid openssl_ciphers value accepted")
def test_wpa2_eap_ttls_pap_key_lifetime_in_memory(dev, apdev, params):
"""Key lifetime in memory with WPA2-Enterprise using EAP-TTLS/PAP"""
p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], p)
password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
id = eap_connect(dev[0], hapd, "TTLS", "pap-secret",
anonymous_identity="ttls", password=password,
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
run_eap_key_lifetime_in_memory(dev, params, id, password)
def test_wpa2_eap_peap_gtc_key_lifetime_in_memory(dev, apdev, params):
"""Key lifetime in memory with WPA2-Enterprise using PEAP/GTC"""
p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], p)
password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
id = eap_connect(dev[0], hapd, "PEAP", "user-secret",
anonymous_identity="peap", password=password,
ca_cert="auth_serv/ca.pem", phase2="auth=GTC")
run_eap_key_lifetime_in_memory(dev, params, id, password)
def run_eap_key_lifetime_in_memory(dev, params, id, password):
pid = find_wpas_process(dev[0])
# The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
# event has been delivered, so verify that wpa_supplicant has returned to
# eloop before reading process memory.
time.sleep(1)
dev[0].ping()
password = password.encode()
buf = read_process_memory(pid, password)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].relog()
msk = None
emsk = None
pmk = None
ptk = None
gtk = None
with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
for l in f.readlines():
if "EAP-TTLS: Derived key - hexdump" in l or \
"EAP-PEAP: Derived key - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
msk = binascii.unhexlify(val)
if "EAP-TTLS: Derived EMSK - hexdump" in l or \
"EAP-PEAP: Derived EMSK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
emsk = binascii.unhexlify(val)
if "WPA: PMK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
pmk = binascii.unhexlify(val)
if "WPA: PTK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
ptk = binascii.unhexlify(val)
if "WPA: Group Key - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
gtk = binascii.unhexlify(val)
if not msk or not emsk or not pmk or not ptk or not gtk:
raise Exception("Could not find keys from debug log")
if len(gtk) != 16:
raise Exception("Unexpected GTK length")
kck = ptk[0:16]
kek = ptk[16:32]
tk = ptk[32:48]
fname = os.path.join(params['logdir'],
'wpa2_eap_ttls_pap_key_lifetime_in_memory.memctx-')
logger.info("Checking keys in memory while associated")
get_key_locations(buf, password, "Password")
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, msk, "MSK")
get_key_locations(buf, emsk, "EMSK")
if password not in buf:
raise HwsimSkip("Password not found while associated")
if pmk not in buf:
raise HwsimSkip("PMK not found while associated")
if kck not in buf:
raise Exception("KCK not found while associated")
if kek not in buf:
raise Exception("KEK not found while associated")
#if tk in buf:
# raise Exception("TK found from memory")
logger.info("Checking keys in memory after disassociation")
buf = read_process_memory(pid, password)
# Note: Password is still present in network configuration
# Note: PMK is in PMKSA cache and EAP fast re-auth data
get_key_locations(buf, password, "Password")
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, msk, "MSK")
get_key_locations(buf, emsk, "EMSK")
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
if gtk in buf:
get_key_locations(buf, gtk, "GTK")
verify_not_present(buf, gtk, fname, "GTK")
dev[0].request("PMKSA_FLUSH")
dev[0].set_network_quoted(id, "identity", "foo")
logger.info("Checking keys in memory after PMKSA cache and EAP fast reauth flush")
buf = read_process_memory(pid, password)
get_key_locations(buf, password, "Password")
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, msk, "MSK")
get_key_locations(buf, emsk, "EMSK")
verify_not_present(buf, pmk, fname, "PMK")
dev[0].request("REMOVE_NETWORK all")
logger.info("Checking keys in memory after network profile removal")
buf = read_process_memory(pid, password)
get_key_locations(buf, password, "Password")
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, msk, "MSK")
get_key_locations(buf, emsk, "EMSK")
verify_not_present(buf, password, fname, "password")
verify_not_present(buf, pmk, fname, "PMK")
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
verify_not_present(buf, gtk, fname, "GTK")
verify_not_present(buf, msk, fname, "MSK")
verify_not_present(buf, emsk, fname, "EMSK")
def test_ap_wpa2_eap_unexpected_wep_eapol_key(dev, apdev):
"""WPA2-Enterprise connection and unexpected WEP EAPOL-Key"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
# Send unexpected WEP EAPOL-Key; this gets dropped
res = dev[0].request("EAPOL_RX " + bssid + " 0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
def test_ap_wpa2_eap_in_bridge(dev, apdev):
"""WPA2-EAP and wpas interface in a bridge"""
br_ifname = 'sta-br0'
ifname = 'wlan5'
try:
_test_ap_wpa2_eap_in_bridge(dev, apdev)
finally:
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
subprocess.call(['brctl', 'delif', br_ifname, ifname])
subprocess.call(['brctl', 'delbr', br_ifname])
subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
def _test_ap_wpa2_eap_in_bridge(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
br_ifname = 'sta-br0'
ifname = 'wlan5'
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
subprocess.call(['brctl', 'addbr', br_ifname])
subprocess.call(['brctl', 'setfd', br_ifname, '0'])
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
wpas.interface_add(ifname, br_ifname=br_ifname)
wpas.dump_monitor()
id = eap_connect(wpas, hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
wpas.dump_monitor()
eap_reauth(wpas, "PAX")
wpas.dump_monitor()
# Try again as a regression test for packet socket workaround
eap_reauth(wpas, "PAX")
wpas.dump_monitor()
wpas.request("DISCONNECT")
wpas.wait_disconnected()
wpas.dump_monitor()
wpas.request("RECONNECT")
wpas.wait_connected()
wpas.dump_monitor()
def test_ap_wpa2_eap_session_ticket(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS and TLS session ticket enabled"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "WPA-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem",
phase1="tls_disable_session_ticket=0", phase2="auth=PAP")
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_no_workaround(dev, apdev):
"""WPA2-Enterprise connection using EAP-TTLS and eap_workaround=0"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "WPA-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", eap_workaround='0',
phase2="auth=PAP")
eap_reauth(dev[0], "TTLS")
def test_ap_wpa2_eap_tls_check_crl(dev, apdev):
"""EAP-TLS and server checking CRL"""
params = int_eap_server_params()
params['check_crl'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
# check_crl=1 and no CRL available --> reject connection
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
hapd.disable()
hapd.set("ca_cert", "auth_serv/ca-and-crl.pem")
hapd.enable()
# check_crl=1 and valid CRL --> accept
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[0].request("REMOVE_NETWORK all")
hapd.disable()
hapd.set("check_crl", "2")
hapd.enable()
# check_crl=2 and valid CRL --> accept
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_tls_check_crl_not_strict(dev, apdev):
"""EAP-TLS and server checking CRL with check_crl_strict=0"""
params = int_eap_server_params()
params['check_crl'] = '1'
params['ca_cert'] = "auth_serv/ca-and-crl-expired.pem"
hapd = hostapd.add_ap(apdev[0], params)
# check_crl_strict=1 and expired CRL --> reject connection
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
hapd.disable()
hapd.set("check_crl_strict", "0")
hapd.enable()
# check_crl_strict=0 --> accept
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[0].request("REMOVE_NETWORK all")
def test_ap_wpa2_eap_tls_crl_reload(dev, apdev, params):
"""EAP-TLS and server reloading CRL from ca_cert"""
ca_cert = os.path.join(params['logdir'],
"ap_wpa2_eap_tls_crl_reload.ca_cert")
with open('auth_serv/ca.pem', 'r') as f:
only_cert = f.read()
with open('auth_serv/ca-and-crl.pem', 'r') as f:
cert_and_crl = f.read()
with open(ca_cert, 'w') as f:
f.write(only_cert)
params = int_eap_server_params()
params['ca_cert'] = ca_cert
params['check_crl'] = '1'
params['crl_reload_interval'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
# check_crl=1 and no CRL available --> reject connection
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key", expect_failure=True)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
with open(ca_cert, 'w') as f:
f.write(cert_and_crl)
time.sleep(1)
# check_crl=1 and valid CRL --> accept
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_tls_check_cert_subject(dev, apdev):
"""EAP-TLS and server checking client subject name"""
params = int_eap_server_params()
params['check_cert_subject'] = 'C=FI/O=w1.fi/CN=Test User'
hapd = hostapd.add_ap(apdev[0], params)
check_check_cert_subject_support(hapd)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
def test_ap_wpa2_eap_tls_check_cert_subject_neg(dev, apdev):
"""EAP-TLS and server checking client subject name (negative)"""
params = int_eap_server_params()
params['check_cert_subject'] = 'C=FI/O=example'
hapd = hostapd.add_ap(apdev[0], params)
check_check_cert_subject_support(hapd)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key", expect_failure=True)
def test_ap_wpa2_eap_tls_oom(dev, apdev):
"""EAP-TLS and OOM"""
check_subject_match_support(dev[0])
check_altsubject_match_support(dev[0])
check_domain_match(dev[0])
check_domain_match_full(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
tests = [(1, "tls_connection_set_subject_match"),
(2, "tls_connection_set_subject_match"),
(3, "tls_connection_set_subject_match"),
(4, "tls_connection_set_subject_match")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/",
domain_suffix_match="server.w1.fi",
domain_match="server.w1.fi",
wait_connect=False, scan_freq="2412")
# TLS parameter configuration error results in CTRL-REQ-PASSPHRASE
ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"], timeout=5)
if ev is None:
raise Exception("No passphrase request")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_tls_macacl(dev, apdev):
"""WPA2-Enterprise connection using MAC ACL"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params["macaddr_acl"] = "2"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[1], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
def test_ap_wpa2_eap_oom(dev, apdev):
"""EAP server and OOM"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
with alloc_fail(hapd, 1, "eapol_auth_alloc"):
# The first attempt fails, but STA will send EAPOL-Start to retry and
# that succeeds.
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
scan_freq="2412")
def check_tls_ver(dev, hapd, phase1, expected):
eap_connect(dev, hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
phase1=phase1)
ver = dev.get_status_field("eap_tls_version")
if ver != expected:
raise Exception("Unexpected TLS version (expected %s): %s" % (expected, ver))
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
def test_ap_wpa2_eap_tls_versions(dev, apdev):
"""EAP-TLS and TLS version configuration"""
params = {"ssid": "test-wpa2-eap",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"tls_flags": "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][ENABLE-TLSv1.3]",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ca.pem",
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key"}
hapd = hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if tls.startswith("OpenSSL"):
if "build=OpenSSL 1.0.1" not in tls and "run=OpenSSL 1.0.1" not in tls:
check_tls_ver(dev[0], hapd,
"tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
"TLSv1.2")
if tls.startswith("wolfSSL"):
if ("build=3.10.0" in tls and "run=3.10.0" in tls) or \
("build=3.13.0" in tls and "run=3.13.0" in tls):
check_tls_ver(dev[0], hapd,
"tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
"TLSv1.2")
elif tls.startswith("internal"):
check_tls_ver(dev[0], hapd,
"tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
check_tls_ver(dev[1], hapd,
"tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
check_tls_ver(dev[2], hapd,
"tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
if "run=OpenSSL 1.1.1" in tls:
check_tls_ver(dev[0], hapd,
"tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
"""EAP-TLS and TLS version configuration on server side"""
params = {"ssid": "test-wpa2-eap",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ca.pem",
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key"}
hapd = hostapd.add_ap(apdev[0], params)
tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
for exp, flags in tests:
hapd.disable()
hapd.set("tls_flags", flags)
hapd.enable()
check_tls_ver(dev[0], hapd, "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=0 tls_disable_tlsv1_3=0", exp)
def test_ap_wpa2_eap_tls_13(dev, apdev):
"""EAP-TLS and TLS 1.3"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "run=OpenSSL 1.1.1" not in tls:
raise HwsimSkip("TLS v1.3 not supported")
id = eap_connect(dev[0], hapd, "TLS", "tls user",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0")
ver = dev[0].get_status_field("eap_tls_version")
if ver != "TLSv1.3":
raise Exception("Unexpected TLS version")
eap_reauth(dev[0], "TLS")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("PMKSA_FLUSH")
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_wpa2_eap_ttls_13(dev, apdev):
"""EAP-TTLS and TLS 1.3"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "run=OpenSSL 1.1.1" not in tls:
raise HwsimSkip("TLS v1.3 not supported")
id = eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem",
phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0",
phase2="auth=PAP")
ver = dev[0].get_status_field("eap_tls_version")
if ver != "TLSv1.3":
raise Exception("Unexpected TLS version")
eap_reauth(dev[0], "TTLS")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("PMKSA_FLUSH")
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_wpa2_eap_peap_13(dev, apdev):
"""PEAP and TLS 1.3"""
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
tls = dev[0].request("GET tls_library")
if "run=OpenSSL 1.1.1" not in tls:
raise HwsimSkip("TLS v1.3 not supported")
id = eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem",
phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0",
phase2="auth=MSCHAPV2")
ver = dev[0].get_status_field("eap_tls_version")
if ver != "TLSv1.3":
raise Exception("Unexpected TLS version")
eap_reauth(dev[0], "PEAP")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("PMKSA_FLUSH")
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_wpa2_eap_tls_13_ec(dev, apdev):
"""EAP-TLS and TLS 1.3 (EC certificates)"""
params = {"ssid": "test-wpa2-eap",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ec-ca.pem",
"server_cert": "auth_serv/ec-server.pem",
"private_key": "auth_serv/ec-server.key",
"tls_flags": "[ENABLE-TLSv1.3]"}
hapd = hostapd.add_ap(apdev[0], params)
tls = hapd.request("GET tls_library")
if "run=OpenSSL 1.1.1" not in tls:
raise HwsimSkip("TLS v1.3 not supported")
tls = dev[0].request("GET tls_library")
if "run=OpenSSL 1.1.1" not in tls:
raise HwsimSkip("TLS v1.3 not supported")
id = eap_connect(dev[0], hapd, "TLS", "tls user",
ca_cert="auth_serv/ec-ca.pem",
client_cert="auth_serv/ec-user.pem",
private_key="auth_serv/ec-user.key",
phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0")
ver = dev[0].get_status_field("eap_tls_version")
if ver != "TLSv1.3":
raise Exception("Unexpected TLS version")
def test_ap_wpa2_eap_tls_rsa_and_ec(dev, apdev, params):
"""EAP-TLS and both RSA and EC sertificates certificates"""
check_ec_support(dev[0])
ca = os.path.join(params['logdir'], "ap_wpa2_eap_tls_rsa_and_ec.ca.pem")
with open(ca, "w") as f:
with open("auth_serv/ca.pem", "r") as f2:
f.write(f2.read())
with open("auth_serv/ec-ca.pem", "r") as f2:
f.write(f2.read())
params = {"ssid": "test-wpa2-eap",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": ca,
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key",
"server_cert2": "auth_serv/ec-server.pem",
"private_key2": "auth_serv/ec-server.key"}
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user",
ca_cert="auth_serv/ec-ca.pem",
client_cert="auth_serv/ec-user.pem",
private_key="auth_serv/ec-user.key")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
# TODO: Make wpa_supplicant automatically filter out cipher suites that
# would require ECDH/ECDSA keys when those are not configured in the
# selected client certificate. And for no-client-cert case, deprioritize
# those cipher suites based on configured ca_cert value so that the most
# likely to work cipher suites are selected by the server. Only do these
# when an explicit openssl_ciphers parameter is not set.
eap_connect(dev[1], hapd, "TLS", "tls user",
openssl_ciphers="DEFAULT:-aECDH:-aECDSA",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[1].request("REMOVE_NETWORK all")
dev[1].wait_disconnected()
def test_ap_wpa2_eap_tls_ec_and_rsa(dev, apdev, params):
"""EAP-TLS and both EC and RSA sertificates certificates"""
check_ec_support(dev[0])
ca = os.path.join(params['logdir'], "ap_wpa2_eap_tls_ec_and_rsa.ca.pem")
with open(ca, "w") as f:
with open("auth_serv/ca.pem", "r") as f2:
f.write(f2.read())
with open("auth_serv/ec-ca.pem", "r") as f2:
f.write(f2.read())
params = {"ssid": "test-wpa2-eap",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": ca,
"private_key2": "auth_serv/server-extra.pkcs12",
"private_key_passwd2": "whatever",
"server_cert": "auth_serv/ec-server.pem",
"private_key": "auth_serv/ec-server.key"}
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user",
ca_cert="auth_serv/ec-ca.pem",
client_cert="auth_serv/ec-user.pem",
private_key="auth_serv/ec-user.key")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
# TODO: Make wpa_supplicant automatically filter out cipher suites that
# would require ECDH/ECDSA keys when those are not configured in the
# selected client certificate. And for no-client-cert case, deprioritize
# those cipher suites based on configured ca_cert value so that the most
# likely to work cipher suites are selected by the server. Only do these
# when an explicit openssl_ciphers parameter is not set.
eap_connect(dev[1], hapd, "TLS", "tls user",
openssl_ciphers="DEFAULT:-aECDH:-aECDSA",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
dev[1].request("REMOVE_NETWORK all")
dev[1].wait_disconnected()
def test_rsn_ie_proto_eap_sta(dev, apdev):
"""RSN element protocol testing for EAP cases on STA side"""
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
# This is the RSN element used normally by hostapd
params['own_ie_override'] = '30140100000fac040100000fac040100000fac010c00'
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
tests = [('No RSN Capabilities field',
'30120100000fac040100000fac040100000fac01'),
('No AKM Suite fields',
'300c0100000fac040100000fac04'),
('No Pairwise Cipher Suite fields',
'30060100000fac04'),
('No Group Data Cipher Suite field',
'30020100')]
for txt, ie in tests:
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
logger.info(txt)
hapd.disable()
hapd.set('own_ie_override', ie)
hapd.enable()
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def check_tls_session_resumption_capa(dev, hapd):
tls = hapd.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("hostapd TLS library is not OpenSSL or wolfSSL: " + tls)
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("Session resumption not supported with this TLS library: " + tls)
def test_eap_ttls_pap_session_resumption(dev, apdev):
"""EAP-TTLS/PAP session resumption"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", eap_workaround='0',
phase2="auth=PAP")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_eap_ttls_chap_session_resumption(dev, apdev):
"""EAP-TTLS/CHAP session resumption"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TTLS", "chap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_ttls_mschap_session_resumption(dev, apdev):
"""EAP-TTLS/MSCHAP session resumption"""
check_domain_suffix_match(dev[0])
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TTLS", "mschap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
domain_suffix_match="server.w1.fi")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_ttls_mschapv2_session_resumption(dev, apdev):
"""EAP-TTLS/MSCHAPv2 session resumption"""
check_domain_suffix_match(dev[0])
check_eap_capa(dev[0], "MSCHAPV2")
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
domain_suffix_match="server.w1.fi")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_ttls_eap_gtc_session_resumption(dev, apdev):
"""EAP-TTLS/EAP-GTC session resumption"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TTLS", "user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_ttls_no_session_resumption(dev, apdev):
"""EAP-TTLS session resumption disabled on server"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '0'
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "pap user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", eap_workaround='0',
phase2="auth=PAP")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the second connection")
def test_eap_peap_session_resumption(dev, apdev):
"""EAP-PEAP session resumption"""
check_eap_capa(dev[0], "MSCHAPV2")
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_peap_session_resumption_crypto_binding(dev, apdev):
"""EAP-PEAP session resumption with crypto binding"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
phase1="peapver=0 crypto_binding=2",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_peap_no_session_resumption(dev, apdev):
"""EAP-PEAP session resumption disabled on server"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PEAP", "user",
anonymous_identity="peap", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the second connection")
def test_eap_tls_session_resumption(dev, apdev):
"""EAP-TLS session resumption"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '60'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the third connection")
def test_eap_tls_session_resumption_expiration(dev, apdev):
"""EAP-TLS session resumption"""
params = int_eap_server_params()
params['tls_session_lifetime'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
check_tls_session_resumption_capa(dev[0], hapd)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
# Allow multiple attempts since OpenSSL may not expire the cached entry
# immediately.
for i in range(10):
time.sleep(1.2)
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") == '0':
break
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Session resumption used after lifetime expiration")
def test_eap_tls_no_session_resumption(dev, apdev):
"""EAP-TLS session resumption disabled on server"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the second connection")
def test_eap_tls_session_resumption_radius(dev, apdev):
"""EAP-TLS session resumption (RADIUS)"""
params = {"ssid": "as", "beacon_int": "2000",
"radius_server_clients": "auth_serv/radius_clients.conf",
"radius_server_auth_port": '18128',
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ca.pem",
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key",
"tls_session_lifetime": "60"}
authsrv = hostapd.add_ap(apdev[1], params)
check_tls_session_resumption_capa(dev[0], authsrv)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['auth_server_port'] = "18128"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '1':
raise Exception("Session resumption not used on the second connection")
def test_eap_tls_no_session_resumption_radius(dev, apdev):
"""EAP-TLS session resumption disabled (RADIUS)"""
params = {"ssid": "as", "beacon_int": "2000",
"radius_server_clients": "auth_serv/radius_clients.conf",
"radius_server_auth_port": '18128',
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ca.pem",
"server_cert": "auth_serv/server.pem",
"private_key": "auth_serv/server.key",
"tls_session_lifetime": "0"}
hostapd.add_ap(apdev[1], params)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['auth_server_port'] = "18128"
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the first connection")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("Key handshake with the AP timed out")
if dev[0].get_status_field("tls_session_reused") != '0':
raise Exception("Unexpected session resumption on the second connection")
def test_eap_mschapv2_errors(dev, apdev):
"""EAP-MSCHAPv2 error cases"""
check_eap_capa(dev[0], "MSCHAPV2")
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="test-wpa-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
identity="phase1-user", password="password",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "hash_nt_password_hash;mschapv2_derive_response"),
(1, "nt_password_hash;mschapv2_derive_response"),
(1, "nt_password_hash;=mschapv2_derive_response"),
(1, "generate_nt_response;mschapv2_derive_response"),
(1, "generate_authenticator_response;mschapv2_derive_response"),
(1, "nt_password_hash;=mschapv2_derive_response"),
(1, "get_master_key;mschapv2_derive_response"),
(1, "os_get_random;eap_mschapv2_challenge_reply")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
identity="phase1-user", password="password",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "hash_nt_password_hash;mschapv2_derive_response"),
(1, "hash_nt_password_hash;=mschapv2_derive_response"),
(1, "generate_nt_response_pwhash;mschapv2_derive_response"),
(1, "generate_authenticator_response_pwhash;mschapv2_derive_response")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
identity="phase1-user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_mschapv2_init"),
(1, "eap_msg_alloc;eap_mschapv2_challenge_reply"),
(1, "eap_msg_alloc;eap_mschapv2_success"),
(1, "eap_mschapv2_getKey")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
identity="phase1-user", password="password",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_msg_alloc;eap_mschapv2_failure")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
identity="phase1-user", password="wrong password",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(2, "eap_mschapv2_init"),
(3, "eap_mschapv2_init")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="FAST",
anonymous_identity="FAST", identity="user",
password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_gpsk_errors(dev, apdev):
"""EAP-GPSK error cases"""
params = hostapd.wpa2_eap_params(ssid="test-wpa-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "os_get_random;eap_gpsk_send_gpsk_2", None),
(1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2",
"cipher=1"),
(1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2",
"cipher=2"),
(1, "eap_gpsk_derive_keys_helper", None),
(2, "eap_gpsk_derive_keys_helper", None),
(3, "eap_gpsk_derive_keys_helper", None),
(1, "eap_gpsk_compute_mic_aes;eap_gpsk_compute_mic;eap_gpsk_send_gpsk_2",
"cipher=1"),
(1, "hmac_sha256;eap_gpsk_compute_mic;eap_gpsk_send_gpsk_2",
"cipher=2"),
(1, "eap_gpsk_compute_mic;eap_gpsk_validate_gpsk_3_mic", None),
(1, "eap_gpsk_compute_mic;eap_gpsk_send_gpsk_4", None),
(1, "eap_gpsk_derive_mid_helper", None)]
for count, func, phase1 in tests:
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
phase1=phase1,
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_gpsk_init"),
(2, "eap_gpsk_init"),
(3, "eap_gpsk_init"),
(1, "eap_gpsk_process_id_server"),
(1, "eap_msg_alloc;eap_gpsk_send_gpsk_2"),
(1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2"),
(1, "eap_gpsk_derive_mid_helper;eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2"),
(1, "eap_gpsk_derive_keys"),
(1, "eap_gpsk_derive_keys_helper"),
(1, "eap_msg_alloc;eap_gpsk_send_gpsk_4"),
(1, "eap_gpsk_getKey"),
(1, "eap_gpsk_get_emsk"),
(1, "eap_gpsk_get_session_id")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].request("ERP_FLUSH")
dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user@domain", erp="1",
password="abcdefghijklmnop0123456789abcdef",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_db(dev, apdev, params):
"""EAP-SIM DB error cases"""
sockpath = '/tmp/hlr_auc_gw.sock-test'
try:
os.remove(sockpath)
except:
pass
hparams = int_eap_server_params()
hparams['eap_sim_db'] = 'unix:' + sockpath
hapd = hostapd.add_ap(apdev[0], hparams)
# Initial test with hlr_auc_gw socket not available
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["EAP-ERROR-CODE"], timeout=10)
if ev is None:
raise Exception("EAP method specific error code not reported")
if int(ev.split()[1]) != 16384:
raise Exception("Unexpected EAP method specific error code: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP-Failure not reported")
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
# Test with invalid responses and response timeout
class test_handler(SocketServer.DatagramRequestHandler):
def handle(self):
data = self.request[0].decode().strip()
socket = self.request[1]
logger.debug("Received hlr_auc_gw request: " + data)
# EAP-SIM DB: Failed to parse response string
socket.sendto(b"FOO", self.client_address)
# EAP-SIM DB: Failed to parse response string
socket.sendto(b"FOO 1", self.client_address)
# EAP-SIM DB: Unknown external response
socket.sendto(b"FOO 1 2", self.client_address)
logger.info("No proper response - wait for pending eap_sim_db request timeout")
server = SocketServer.UnixDatagramServer(sockpath, test_handler)
server.timeout = 1
dev[0].select_network(id)
server.handle_request()
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP-Failure not reported")
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
# Test with a valid response
class test_handler2(SocketServer.DatagramRequestHandler):
def handle(self):
data = self.request[0].decode().strip()
socket = self.request[1]
logger.debug("Received hlr_auc_gw request: " + data)
fname = os.path.join(params['logdir'],
'hlr_auc_gw.milenage_db')
cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
'-m', fname, data],
stdout=subprocess.PIPE)
res = cmd.stdout.read().decode().strip()
cmd.stdout.close()
logger.debug("hlr_auc_gw response: " + res)
socket.sendto(res.encode(), self.client_address)
server.RequestHandlerClass = test_handler2
dev[0].select_network(id)
server.handle_request()
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_sim_db_sqlite(dev, apdev, params):
"""EAP-SIM DB error cases (SQLite)"""
sockpath = '/tmp/hlr_auc_gw.sock-test'
try:
os.remove(sockpath)
except:
pass
hparams = int_eap_server_params()
hparams['eap_sim_db'] = 'unix:' + sockpath
hapd = hostapd.add_ap(apdev[0], hparams)
fname = params['prefix'] + ".milenage_db.sqlite"
cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
'-D', fname, "FOO"],
stdout=subprocess.PIPE)
res = cmd.stdout.read().decode().strip()
cmd.stdout.close()
logger.debug("hlr_auc_gw response: " + res)
try:
import sqlite3
except ImportError:
raise HwsimSkip("No sqlite3 module available")
con = sqlite3.connect(fname)
with con:
cur = con.cursor()
try:
cur.execute("INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES ('232010000000000', '90dca4eda45b53cf0f12d7c9c3bc6a89', 'cb9cccc4b9258e6dca4760379fb82581', '61df', '000000000000')")
except sqlite3.IntegrityError as e:
pass
class test_handler3(SocketServer.DatagramRequestHandler):
def handle(self):
data = self.request[0].decode().strip()
socket = self.request[1]
logger.debug("Received hlr_auc_gw request: " + data)
cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
'-D', fname, data],
stdout=subprocess.PIPE)
res = cmd.stdout.read().decode().strip()
cmd.stdout.close()
logger.debug("hlr_auc_gw response: " + res)
socket.sendto(res.encode(), self.client_address)
server = SocketServer.UnixDatagramServer(sockpath, test_handler3)
server.timeout = 1
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
scan_freq="2412", wait_connect=False)
server.handle_request()
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_eap_tls_sha512(dev, apdev, params):
"""EAP-TLS with SHA512 signature"""
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/sha512-ca.pem"
params["server_cert"] = "auth_serv/sha512-server.pem"
params["private_key"] = "auth_serv/sha512-server.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user sha512",
ca_cert="auth_serv/sha512-ca.pem",
client_cert="auth_serv/sha512-user.pem",
private_key="auth_serv/sha512-user.key",
scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user sha512",
ca_cert="auth_serv/sha512-ca.pem",
client_cert="auth_serv/sha384-user.pem",
private_key="auth_serv/sha384-user.key",
scan_freq="2412")
def test_eap_tls_sha384(dev, apdev, params):
"""EAP-TLS with SHA384 signature"""
params = int_eap_server_params()
params["ca_cert"] = "auth_serv/sha512-ca.pem"
params["server_cert"] = "auth_serv/sha384-server.pem"
params["private_key"] = "auth_serv/sha384-server.key"
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user sha512",
ca_cert="auth_serv/sha512-ca.pem",
client_cert="auth_serv/sha512-user.pem",
private_key="auth_serv/sha512-user.key",
scan_freq="2412")
dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user sha512",
ca_cert="auth_serv/sha512-ca.pem",
client_cert="auth_serv/sha384-user.pem",
private_key="auth_serv/sha384-user.key",
scan_freq="2412")
def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
"""WPA2-Enterprise AP and association request RSN IE differences"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap-11w")
params["ieee80211w"] = "2"
hostapd.add_ap(apdev[1], params)
# Success cases with optional RSN IE fields removed one by one
tests = [("Normal wpa_supplicant assoc req RSN IE",
"30140100000fac040100000fac040100000fac010000"),
("Extra PMKIDCount field in RSN IE",
"30160100000fac040100000fac040100000fac0100000000"),
("Extra Group Management Cipher Suite in RSN IE",
"301a0100000fac040100000fac040100000fac0100000000000fac06"),
("Extra undefined extension field in RSN IE",
"301c0100000fac040100000fac040100000fac0100000000000fac061122"),
("RSN IE without RSN Capabilities",
"30120100000fac040100000fac040100000fac01"),
("RSN IE without AKM", "300c0100000fac040100000fac04"),
("RSN IE without pairwise", "30060100000fac04"),
("RSN IE without group", "30020100")]
for title, ie in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [("Normal wpa_supplicant assoc req RSN IE",
"30140100000fac040100000fac040100000fac01cc00"),
("Group management cipher included in assoc req RSN IE",
"301a0100000fac040100000fac040100000fac01cc000000000fac06")]
for title, ie in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect("test-wpa2-eap-11w", key_mgmt="WPA-EAP", ieee80211w="1",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [("Invalid group cipher", "30060100000fac02", [40, 41]),
("Invalid pairwise cipher", "300c0100000fac040100000fac02", 42)]
for title, ie, status in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
if ev is None:
raise Exception("Association rejection not reported")
ok = False
if isinstance(status, list):
for i in status:
ok = "status_code=" + str(i) in ev
if ok:
break
else:
ok = "status_code=" + str(status) in ev
if not ok:
raise Exception("Unexpected status code: " + ev)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [("Management frame protection not enabled",
"30140100000fac040100000fac040100000fac010000", 31),
("Unsupported management group cipher",
"301a0100000fac040100000fac040100000fac01cc000000000fac0b", 46)]
for title, ie, status in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect("test-wpa2-eap-11w", key_mgmt="WPA-EAP", ieee80211w="1",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
if ev is None:
raise Exception("Association rejection not reported")
if "status_code=" + str(status) not in ev:
raise Exception("Unexpected status code: " + ev)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_eap_tls_ext_cert_check(dev, apdev):
"""EAP-TLS and external server certification validation"""
# With internal server certificate chain validation
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
phase1="tls_ext_cert_check=1", scan_freq="2412",
only_add_network=True)
run_ext_cert_check(dev, apdev, id)
def test_eap_ttls_ext_cert_check(dev, apdev):
"""EAP-TTLS and external server certification validation"""
# Without internal server certificate chain validation
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
phase1="tls_ext_cert_check=1", scan_freq="2412",
only_add_network=True)
run_ext_cert_check(dev, apdev, id)
def test_eap_peap_ext_cert_check(dev, apdev):
"""EAP-PEAP and external server certification validation"""
# With internal server certificate chain validation
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="user", anonymous_identity="peap",
ca_cert="auth_serv/ca.pem",
password="password", phase2="auth=MSCHAPV2",
phase1="tls_ext_cert_check=1", scan_freq="2412",
only_add_network=True)
run_ext_cert_check(dev, apdev, id)
def test_eap_fast_ext_cert_check(dev, apdev):
"""EAP-FAST and external server certification validation"""
check_eap_capa(dev[0], "FAST")
# With internal server certificate chain validation
dev[0].request("SET blob fast_pac_auth_ext ")
id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
identity="user", anonymous_identity="FAST",
ca_cert="auth_serv/ca.pem",
password="password", phase2="auth=GTC",
phase1="tls_ext_cert_check=1 fast_provisioning=2",
pac_file="blob://fast_pac_auth_ext",
scan_freq="2412",
only_add_network=True)
run_ext_cert_check(dev, apdev, id)
def run_ext_cert_check(dev, apdev, net_id):
check_ext_cert_check_support(dev[0])
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].select_network(net_id)
certs = {}
while True:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT",
"CTRL-REQ-EXT_CERT_CHECK",
"CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("No peer server certificate event seen")
if "CTRL-EVENT-EAP-PEER-CERT" in ev:
depth = None
cert = None
vals = ev.split(' ')
for v in vals:
if v.startswith("depth="):
depth = int(v.split('=')[1])
elif v.startswith("cert="):
cert = v.split('=')[1]
if depth is not None and cert:
certs[depth] = binascii.unhexlify(cert)
elif "CTRL-EVENT-EAP-SUCCESS" in ev:
raise Exception("Unexpected EAP-Success")
elif "CTRL-REQ-EXT_CERT_CHECK" in ev:
id = ev.split(':')[0].split('-')[-1]
break
if 0 not in certs:
raise Exception("Server certificate not received")
if 1 not in certs:
raise Exception("Server certificate issuer not received")
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1,
certs[0])
cn = cert.get_subject().commonName
logger.info("Server certificate CN=" + cn)
issuer = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1,
certs[1])
icn = issuer.get_subject().commonName
logger.info("Issuer certificate CN=" + icn)
if cn != "server.w1.fi":
raise Exception("Unexpected server certificate CN: " + cn)
if icn != "Root CA":
raise Exception("Unexpected server certificate issuer CN: " + icn)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=0.1)
if ev:
raise Exception("Unexpected EAP-Success before external check result indication")
dev[0].request("CTRL-RSP-EXT_CERT_CHECK-" + id + ":good")
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
dev[0].request("SET blob fast_pac_auth_ext ")
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["CTRL-REQ-EXT_CERT_CHECK"], timeout=10)
if ev is None:
raise Exception("No peer server certificate event seen (2)")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-EXT_CERT_CHECK-" + id + ":bad")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP-Failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_tls_errors(dev, apdev):
"""EAP-TLS error cases"""
params = int_eap_server_params()
params['fragment_size'] = '100'
hostapd.add_ap(apdev[0], params)
with alloc_fail(dev[0], 1,
"eap_peer_tls_reassemble_fragment"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_tls_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
engine="1",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = dev[0].wait_event(["CTRL-REQ-PIN"], timeout=5)
if ev is None:
raise Exception("No CTRL-REQ-PIN seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = ["eap_peer_tls_derive_key;eap_tls_success",
"eap_peer_tls_derive_session_id;eap_tls_success",
"eap_tls_getKey",
"eap_tls_get_emsk",
"eap_tls_get_session_id"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
identity="tls user@domain",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
erp="1",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_unauth_tls_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="UNAUTH-TLS",
identity="unauth-tls", ca_cert="auth_serv/ca.pem",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init;eap_unauth_tls_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="UNAUTH-TLS",
identity="unauth-tls", ca_cert="auth_serv/ca.pem",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_wfa_unauth_tls_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="WFA-UNAUTH-TLS",
identity="osen@example.com", ca_cert="auth_serv/ca.pem",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init;eap_wfa_unauth_tls_init"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="WFA-UNAUTH-TLS",
identity="osen@example.com", ca_cert="auth_serv/ca.pem",
wait_connect=False, scan_freq="2412")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_status(dev, apdev):
"""EAP state machine status information"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
identity="cert user",
ca_cert="auth_serv/ca.pem", phase2="auth=TLS",
ca_cert2="auth_serv/ca.pem",
client_cert2="auth_serv/user.pem",
private_key2="auth_serv/user.key",
scan_freq="2412", wait_connect=False)
success = False
states = []
method_states = []
decisions = []
req_methods = []
selected_methods = []
connected = False
for i in range(100000):
if not connected and i % 10 == 9:
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.0001)
if ev:
connected = True
s = dev[0].get_status(extra="VERBOSE")
if 'EAP state' in s:
state = s['EAP state']
if state:
if state not in states:
states.append(state)
if state == "SUCCESS":
success = True
break
if 'methodState' in s:
val = s['methodState']
if val not in method_states:
method_states.append(val)
if 'decision' in s:
val = s['decision']
if val not in decisions:
decisions.append(val)
if 'reqMethod' in s:
val = s['reqMethod']
if val not in req_methods:
req_methods.append(val)
if 'selectedMethod' in s:
val = s['selectedMethod']
if val not in selected_methods:
selected_methods.append(val)
logger.info("Iterations: %d" % i)
logger.info("EAP states: " + str(states))
logger.info("methodStates: " + str(method_states))
logger.info("decisions: " + str(decisions))
logger.info("reqMethods: " + str(req_methods))
logger.info("selectedMethods: " + str(selected_methods))
if not success:
raise Exception("EAP did not succeed")
if not connected:
dev[0].wait_connected()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_eap_gpsk_ptk_rekey_ap(dev, apdev):
"""WPA2-Enterprise with EAP-GPSK and PTK rekey enforced by AP"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['wpa_ptk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
id = eap_connect(dev[0], hapd, "GPSK", "gpsk user",
password="abcdefghijklmnop0123456789abcdef")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_eap_wildcard_ssid(dev, apdev):
"""WPA2-Enterprise connection using EAP-GPSK and wildcard SSID"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(bssid=apdev[0]['bssid'], key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
def test_ap_wpa2_eap_psk_mac_addr_change(dev, apdev):
"""WPA2-Enterprise connection using EAP-PSK after MAC address change"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
cmd = subprocess.Popen(['ps', '-eo', 'pid,command'], stdout=subprocess.PIPE)
res = cmd.stdout.read().decode()
cmd.stdout.close()
pid = 0
for p in res.splitlines():
if "wpa_supplicant" not in p:
continue
if dev[0].ifname not in p:
continue
pid = int(p.strip().split(' ')[0])
if pid == 0:
logger.info("Could not find wpa_supplicant PID")
else:
logger.info("wpa_supplicant PID %d" % pid)
addr = dev[0].get_status_field("address")
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
'02:11:22:33:44:55'])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
addr1 = dev[0].get_status_field("address")
if addr1 != '02:11:22:33:44:55':
raise Exception("Failed to change MAC address")
# Scan using the externally set MAC address, stop the wpa_supplicant
# process to avoid it from processing the ifdown event before the interface
# is already UP, change the MAC address back, allow the wpa_supplicant
# process to continue. This will result in the ifdown + ifup sequence of
# RTM_NEWLINK events to be processed while the interface is already UP.
try:
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
os.kill(pid, signal.SIGSTOP)
time.sleep(0.1)
finally:
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
addr])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
time.sleep(0.1)
os.kill(pid, signal.SIGCONT)
eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
addr2 = dev[0].get_status_field("address")
if addr != addr2:
raise Exception("Failed to restore MAC address")
def test_ap_wpa2_eap_server_get_id(dev, apdev):
"""Internal EAP server and dot1xAuthSessionUserName"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
sta = hapd.get_sta(dev[0].own_addr())
if 'dot1xAuthSessionUserName' not in sta:
raise Exception("No dot1xAuthSessionUserName included")
user = sta['dot1xAuthSessionUserName']
if user != "tls user":
raise Exception("Unexpected dot1xAuthSessionUserName value: " + user)
def test_ap_wpa2_radius_server_get_id(dev, apdev):
"""External RADIUS server and dot1xAuthSessionUserName"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "TTLS", "test-user",
anonymous_identity="ttls", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
sta = hapd.get_sta(dev[0].own_addr())
if 'dot1xAuthSessionUserName' not in sta:
raise Exception("No dot1xAuthSessionUserName included")
user = sta['dot1xAuthSessionUserName']
if user != "real-user":
raise Exception("Unexpected dot1xAuthSessionUserName value: " + user)
def test_openssl_systemwide_policy(dev, apdev, test_params):
"""OpenSSL systemwide policy and overrides"""
prefix = "openssl_systemwide_policy"
pidfile = os.path.join(test_params['logdir'], prefix + '.pid-wpas')
try:
with HWSimRadio() as (radio, iface):
run_openssl_systemwide_policy(iface, apdev, test_params)
finally:
if os.path.exists(pidfile):
with open(pidfile, 'r') as f:
pid = int(f.read().strip())
os.kill(pid, signal.SIGTERM)
def write_openssl_cnf(cnf, MinProtocol=None, CipherString=None):
with open(cnf, "w") as f:
f.write("""openssl_conf = default_conf
[default_conf]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
""")
if MinProtocol:
f.write("MinProtocol = %s\n" % MinProtocol)
if CipherString:
f.write("CipherString = %s\n" % CipherString)
def run_openssl_systemwide_policy(iface, apdev, test_params):
prefix = "openssl_systemwide_policy"
logfile = os.path.join(test_params['logdir'], prefix + '.log-wpas')
pidfile = os.path.join(test_params['logdir'], prefix + '.pid-wpas')
conffile = os.path.join(test_params['logdir'], prefix + '.conf')
openssl_cnf = os.path.join(test_params['logdir'], prefix + '.openssl.cnf')
write_openssl_cnf(openssl_cnf, "TLSv1.2", "DEFAULT@SECLEVEL=2")
with open(conffile, 'w') as f:
f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
params = int_eap_server_params()
params['tls_flags'] = "[DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"
hapd = hostapd.add_ap(apdev[0], params)
prg = os.path.join(test_params['logdir'],
'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
if not os.path.exists(prg):
prg = '../../wpa_supplicant/wpa_supplicant'
arg = [prg, '-BddtK', '-P', pidfile, '-f', logfile,
'-Dnl80211', '-c', conffile, '-i', iface]
logger.info("Start wpa_supplicant: " + str(arg))
subprocess.call(arg, env={'OPENSSL_CONF': openssl_cnf})
wpas = WpaSupplicant(ifname=iface)
try:
finish_openssl_systemwide_policy(wpas)
finally:
wpas.close_monitor()
wpas.request("TERMINATE")
def finish_openssl_systemwide_policy(wpas):
if "PONG" not in wpas.request("PING"):
raise Exception("Could not PING wpa_supplicant")
tls = wpas.request("GET tls_library")
if not tls.startswith("OpenSSL"):
raise HwsimSkip("Not using OpenSSL")
# Use default configuration without any TLS version overrides. This should
# end up using OpenSSL systemwide policy and result in failure to find a
# compatible protocol version.
ca_file = os.path.join(os.getcwd(), "auth_serv/ca.pem")
id = wpas.connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
identity="pap user", anonymous_identity="ttls",
password="password", phase2="auth=PAP",
ca_cert=ca_file,
scan_freq="2412", wait_connect=False)
ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
if ev is None:
raise Exception("EAP not started")
ev = wpas.wait_event(["CTRL-EVENT-EAP-STATUS status='local TLS alert'"],
timeout=1)
if ev is None:
raise HwsimSkip("OpenSSL systemwide policy not supported")
wpas.request("DISCONNECT")
wpas.wait_disconnected()
wpas.dump_monitor()
# Explicitly allow TLSv1.0 to be used to override OpenSSL systemwide policy
wpas.set_network_quoted(id, "openssl_ciphers", "DEFAULT@SECLEVEL=1")
wpas.set_network_quoted(id, "phase1", "tls_disable_tlsv1_0=0")
wpas.select_network(id, freq="2412")
wpas.wait_connected()
def test_ap_wpa2_eap_tls_tod(dev, apdev):
"""EAP-TLS server certificate validation and TOD-STRICT"""
check_tls_tod(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-certpol.pem"
params["private_key"] = "auth_serv/server-certpol.key"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="TLS", identity="tls user",
wait_connect=False, scan_freq="2412",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
tod0 = None
tod1 = None
while tod0 is None or tod1 is None:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT"], timeout=10)
if ev is None:
raise Exception("Peer certificate not reported")
if "depth=1 " in ev and "hash=" in ev:
tod1 = " tod=1" in ev
if "depth=0 " in ev and "hash=" in ev:
tod0 = " tod=1" in ev
dev[0].wait_connected()
if not tod0:
raise Exception("TOD-STRICT policy not reported for server certificate")
if tod1:
raise Exception("TOD-STRICT policy unexpectedly reported for CA certificate")
def test_ap_wpa2_eap_tls_tod_tofu(dev, apdev):
"""EAP-TLS server certificate validation and TOD-TOFU"""
check_tls_tod(dev[0])
params = int_eap_server_params()
params["server_cert"] = "auth_serv/server-certpol2.pem"
params["private_key"] = "auth_serv/server-certpol2.key"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="TLS", identity="tls user",
wait_connect=False, scan_freq="2412",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key")
tod0 = None
tod1 = None
while tod0 is None or tod1 is None:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT"], timeout=10)
if ev is None:
raise Exception("Peer certificate not reported")
if "depth=1 " in ev and "hash=" in ev:
tod1 = " tod=2" in ev
if "depth=0 " in ev and "hash=" in ev:
tod0 = " tod=2" in ev
dev[0].wait_connected()
if not tod0:
raise Exception("TOD-TOFU policy not reported for server certificate")
if tod1:
raise Exception("TOD-TOFU policy unexpectedly reported for CA certificate")
def test_ap_wpa2_eap_sake_no_control_port(dev, apdev):
"""WPA2-Enterprise connection using EAP-SAKE without nl80211 control port"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['driver_params'] = "control_port=0"
hapd = hostapd.add_ap(apdev[0], params)
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="control_port=0")
eap_connect(wpas, hapd, "SAKE", "sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
eap_reauth(wpas, "SAKE")
logger.info("Negative test with incorrect password")
wpas.request("REMOVE_NETWORK all")
eap_connect(wpas, hapd, "SAKE", "sake user",
password_hex="ff23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expect_failure=True)
def test_ap_wpa3_eap_transition_disable(dev, apdev):
"""WPA3-Enterprise transition disable indication"""
skip_without_tkip(dev[0])
params = hostapd.wpa2_eap_params(ssid="test-wpa3-eap")
params["ieee80211w"] = "1"
params['transition_disable'] = '0x04'
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test-wpa3-eap", key_mgmt="WPA-EAP", ieee80211w="1",
proto="WPA WPA2", pairwise="CCMP", group="TKIP CCMP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
if ev is None:
raise Exception("Transition disable not indicated")
if ev.split(' ')[1] != "04":
raise Exception("Unexpected transition disable bitmap: " + ev)
val = dev[0].get_network(id, "ieee80211w")
if val != "2":
raise Exception("Unexpected ieee80211w value: " + val)
val = dev[0].get_network(id, "key_mgmt")
if val != "WPA-EAP":
raise Exception("Unexpected key_mgmt value: " + val)
val = dev[0].get_network(id, "group")
if val != "CCMP":
raise Exception("Unexpected group value: " + val)
val = dev[0].get_network(id, "proto")
if val != "RSN":
raise Exception("Unexpected proto value: " + val)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("RECONNECT")
dev[0].wait_connected()
diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
index c28dd26e1e68..00b1635db072 100644
--- a/tests/hwsim/test_ap_ft.py
+++ b/tests/hwsim/test_ap_ft.py
@@ -1,3437 +1,3461 @@
# Fast BSS Transition tests
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import binascii
import os
import time
import logging
logger = logging.getLogger()
import signal
import struct
import subprocess
import hwsim_utils
from hwsim import HWSimRadio
import hostapd
from tshark import run_tshark
from utils import *
from wlantest import Wlantest
from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
from test_rrm import check_beacon_req
from test_suite_b import check_suite_b_192_capa
def ft_base_rsn():
params = {"wpa": "2",
"wpa_key_mgmt": "FT-PSK",
"rsn_pairwise": "CCMP"}
return params
def ft_base_mixed():
params = {"wpa": "3",
"wpa_key_mgmt": "WPA-PSK FT-PSK",
"wpa_pairwise": "TKIP",
"rsn_pairwise": "CCMP"}
return params
def ft_params(rsn=True, ssid=None, passphrase=None):
if rsn:
params = ft_base_rsn()
else:
params = ft_base_mixed()
if ssid:
params["ssid"] = ssid
if passphrase:
params["wpa_passphrase"] = passphrase
params["mobility_domain"] = "a1b2"
params["r0_key_lifetime"] = "10000"
params["pmk_r1_push"] = "1"
params["reassociation_deadline"] = "1000"
return params
def ft_params1a(rsn=True, ssid=None, passphrase=None):
params = ft_params(rsn, ssid, passphrase)
params['nas_identifier'] = "nas1.w1.fi"
params['r1_key_holder'] = "000102030405"
return params
def ft_params1(rsn=True, ssid=None, passphrase=None, discovery=False):
params = ft_params1a(rsn, ssid, passphrase)
if discovery:
params['r0kh'] = "ff:ff:ff:ff:ff:ff * 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
params['r1kh'] = "00:00:00:00:00:00 00:00:00:00:00:00 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
else:
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
return params
def ft_params1_old_key(rsn=True, ssid=None, passphrase=None):
params = ft_params1a(rsn, ssid, passphrase)
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
return params
def ft_params2a(rsn=True, ssid=None, passphrase=None):
params = ft_params(rsn, ssid, passphrase)
params['nas_identifier'] = "nas2.w1.fi"
params['r1_key_holder'] = "000102030406"
return params
def ft_params2(rsn=True, ssid=None, passphrase=None, discovery=False):
params = ft_params2a(rsn, ssid, passphrase)
if discovery:
params['r0kh'] = "ff:ff:ff:ff:ff:ff * 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
params['r1kh'] = "00:00:00:00:00:00 00:00:00:00:00:00 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
else:
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
return params
def ft_params2_old_key(rsn=True, ssid=None, passphrase=None):
params = ft_params2a(rsn, ssid, passphrase)
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
return params
def ft_params1_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
params = ft_params(rsn, ssid, passphrase)
params['nas_identifier'] = "nas1.w1.fi"
params['r1_key_holder'] = "000102030405"
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
"12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
return params
def ft_params2_incorrect_rrb_key(rsn=True, ssid=None, passphrase=None):
params = ft_params(rsn, ssid, passphrase)
params['nas_identifier'] = "nas2.w1.fi"
params['r1_key_holder'] = "000102030406"
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0ef1200102030405060708090a0b0c0d0ef1",
"02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2000102030405060708090a0b0c0d0ef2"]
params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3300102030405060708090a0b0c0d0ef3"
return params
def ft_params2_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
params = ft_params(rsn, ssid, passphrase)
params['nas_identifier'] = "nas2.w1.fi"
params['r1_key_holder'] = "000102030406"
params['r0kh'] = ["12:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
return params
def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
sae=False, eap=False, fail_test=False, roams=1,
pairwise_cipher="CCMP", group_cipher="CCMP", ptk_rekey="0",
test_connectivity=True, eap_identity="gpsk user", conndev=False,
force_initial_conn_to_first_ap=False, sha384=False,
group_mgmt=None, ocv=None, sae_password=None,
sae_password_id=None, sae_and_psk=False, pmksa_caching=False,
roam_with_reassoc=False, also_non_ft=False, only_one_way=False,
wait_before_roam=0, return_after_initial=False, ieee80211w="1",
- sae_transition=False):
+ sae_transition=False, beacon_prot=False):
logger.info("Connect to first AP")
copts = {}
copts["proto"] = "WPA2"
copts["ieee80211w"] = ieee80211w
copts["scan_freq"] = "2412"
copts["pairwise"] = pairwise_cipher
copts["group"] = group_cipher
copts["wpa_ptk_rekey"] = ptk_rekey
if group_mgmt:
copts["group_mgmt"] = group_mgmt
if ocv:
copts["ocv"] = ocv
+ if beacon_prot:
+ copts["beacon_prot"] = "1"
if eap:
if pmksa_caching:
copts["ft_eap_pmksa_caching"] = "1"
if also_non_ft:
copts["key_mgmt"] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384" if sha384 else "WPA-EAP FT-EAP"
else:
copts["key_mgmt"] = "FT-EAP-SHA384" if sha384 else "FT-EAP"
copts["eap"] = "GPSK"
copts["identity"] = eap_identity
copts["password"] = "abcdefghijklmnop0123456789abcdef"
else:
if sae_transition:
copts["key_mgmt"] = "FT-SAE FT-PSK"
elif sae:
copts["key_mgmt"] = "SAE FT-SAE" if sae_and_psk else "FT-SAE"
else:
copts["key_mgmt"] = "FT-PSK"
if passphrase:
copts["psk"] = passphrase
if sae_password:
copts["sae_password"] = sae_password
if sae_password_id:
copts["sae_password_id"] = sae_password_id
if force_initial_conn_to_first_ap:
copts["bssid"] = apdev[0]['bssid']
netw = dev.connect(ssid, **copts)
if pmksa_caching:
if dev.get_status_field('bssid') == apdev[0]['bssid']:
hapd0.wait_sta()
else:
hapd1.wait_sta()
dev.request("DISCONNECT")
dev.wait_disconnected()
dev.request("RECONNECT")
ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-EAP-STARTED"],
timeout=15)
if ev is None:
raise Exception("Reconnect timed out")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Unexpected disconnection after RECONNECT")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP start after RECONNECT")
if dev.get_status_field('bssid') == apdev[0]['bssid']:
ap1 = apdev[0]
ap2 = apdev[1]
hapd1ap = hapd0
hapd2ap = hapd1
else:
ap1 = apdev[1]
ap2 = apdev[0]
hapd1ap = hapd1
hapd2ap = hapd0
if test_connectivity:
hapd1ap.wait_sta()
if conndev:
hwsim_utils.test_connectivity_iface(dev, hapd1ap, conndev)
else:
hwsim_utils.test_connectivity(dev, hapd1ap)
if return_after_initial:
return ap2['bssid']
if wait_before_roam:
time.sleep(wait_before_roam)
dev.scan_for_bss(ap2['bssid'], freq="2412")
for i in range(0, roams):
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+
# Roaming artificially fast can make data test fail because the key is
# set later.
time.sleep(0.01)
logger.info("Roam to the second AP")
if roam_with_reassoc:
dev.set_network(netw, "bssid", ap2['bssid'])
dev.request("REASSOCIATE")
dev.wait_connected()
elif over_ds:
dev.roam_over_ds(ap2['bssid'], fail_test=fail_test)
else:
dev.roam(ap2['bssid'], fail_test=fail_test)
if fail_test:
return
if dev.get_status_field('bssid') != ap2['bssid']:
raise Exception("Did not connect to correct AP")
if (i == 0 or i == roams - 1) and test_connectivity:
hapd2ap.wait_sta()
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
if conndev:
hwsim_utils.test_connectivity_iface(dev, hapd2ap, conndev)
else:
hwsim_utils.test_connectivity(dev, hapd2ap)
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+
if only_one_way:
return
# Roaming artificially fast can make data test fail because the key is
# set later.
time.sleep(0.01)
logger.info("Roam back to the first AP")
if roam_with_reassoc:
dev.set_network(netw, "bssid", ap1['bssid'])
dev.request("REASSOCIATE")
dev.wait_connected()
elif over_ds:
dev.roam_over_ds(ap1['bssid'])
else:
dev.roam(ap1['bssid'])
if dev.get_status_field('bssid') != ap1['bssid']:
raise Exception("Did not connect to correct AP")
if (i == 0 or i == roams - 1) and test_connectivity:
hapd1ap.wait_sta()
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
if conndev:
hwsim_utils.test_connectivity_iface(dev, hapd1ap, conndev)
else:
hwsim_utils.test_connectivity(dev, hapd1ap)
def test_ap_ft(dev, apdev):
"""WPA2-PSK-FT AP"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
def test_ap_ft_old_key(dev, apdev):
"""WPA2-PSK-FT AP (old key)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1_old_key(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_old_key(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
def test_ap_ft_multi_akm(dev, apdev):
"""WPA2-PSK-FT AP with non-FT AKMs enabled"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "FT-PSK WPA-PSK WPA-PSK-SHA256"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "FT-PSK WPA-PSK WPA-PSK-SHA256"
hapd1 = hostapd.add_ap(apdev[1], params)
Wlantest.setup(hapd0)
wt = Wlantest()
wt.flush()
wt.add_passphrase(passphrase)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
if "[WPA2-PSK+FT/PSK+PSK-SHA256-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
dev[2].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
scan_freq="2412")
def test_ap_ft_local_key_gen(dev, apdev):
"""WPA2-PSK-FT AP with local key generation (without pull/push)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1a(ssid=ssid, passphrase=passphrase)
params['ft_psk_generate_local'] = "1"
del params['pmk_r1_push']
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2a(ssid=ssid, passphrase=passphrase)
params['ft_psk_generate_local'] = "1"
del params['pmk_r1_push']
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
def test_ap_ft_vlan(dev, apdev):
"""WPA2-PSK-FT AP with VLAN"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1")
if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_ft_vlan_disconnected(dev, apdev):
"""WPA2-PSK-FT AP with VLAN and local key generation"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1a(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
params['ft_psk_generate_local'] = "1"
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2a(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
params['ft_psk_generate_local'] = "1"
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1")
if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_ft_vlan_2(dev, apdev):
"""WPA2-PSK-FT AP with VLAN and dest-AP does not have VLAN info locally"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1",
force_initial_conn_to_first_ap=True)
if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_ft_many(dev, apdev):
"""WPA2-PSK-FT AP multiple times"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50)
def test_ap_ft_many_vlan(dev, apdev):
"""WPA2-PSK-FT AP with VLAN multiple times"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50,
conndev="brvlan1")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_ft_mixed(dev, apdev):
"""WPA2-PSK-FT mixed-mode AP"""
skip_without_tkip(dev[0])
ssid = "test-ft-mixed"
passphrase = "12345678"
params = ft_params1(rsn=False, ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
vals = key_mgmt.split(' ')
if vals[0] != "WPA-PSK" or vals[1] != "FT-PSK":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
params = ft_params2(rsn=False, ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase,
group_cipher="TKIP CCMP")
def test_ap_ft_pmf(dev, apdev):
"""WPA2-PSK-FT AP with PMF"""
run_ap_ft_pmf(dev, apdev, "1")
def test_ap_ft_pmf_over_ds(dev, apdev):
"""WPA2-PSK-FT AP with PMF (over DS)"""
run_ap_ft_pmf(dev, apdev, "1", over_ds=True)
def test_ap_ft_pmf_required(dev, apdev):
"""WPA2-PSK-FT AP with PMF required on STA"""
run_ap_ft_pmf(dev, apdev, "2")
def test_ap_ft_pmf_required_over_ds(dev, apdev):
"""WPA2-PSK-FT AP with PMF required on STA (over DS)"""
run_ap_ft_pmf(dev, apdev, "2", over_ds=True)
-def run_ap_ft_pmf(dev, apdev, ieee80211w, over_ds=False):
+def test_ap_ft_pmf_beacon_prot(dev, apdev):
+ """WPA2-PSK-FT AP with PMF and beacon protection"""
+ run_ap_ft_pmf(dev, apdev, "1", beacon_prot=True)
+
+def run_ap_ft_pmf(dev, apdev, ieee80211w, over_ds=False, beacon_prot=False):
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
Wlantest.setup(hapd0)
wt = Wlantest()
wt.flush()
wt.add_passphrase(passphrase)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
- ieee80211w=ieee80211w, over_ds=over_ds)
+ ieee80211w=ieee80211w, over_ds=over_ds, beacon_prot=beacon_prot)
def test_ap_ft_pmf_required_mismatch(dev, apdev):
"""WPA2-PSK-FT AP with PMF required on STA but AP2 not enabling PMF"""
run_ap_ft_pmf_required_mismatch(dev, apdev)
def test_ap_ft_pmf_required_mismatch_over_ds(dev, apdev):
"""WPA2-PSK-FT AP with PMF required on STA but AP2 not enabling PMF (over DS)"""
run_ap_ft_pmf_required_mismatch(dev, apdev, over_ds=True)
def run_ap_ft_pmf_required_mismatch(dev, apdev, over_ds=False):
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "0"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, ieee80211w="2",
force_initial_conn_to_first_ap=True, fail_test=True,
over_ds=over_ds)
def test_ap_ft_pmf_bip_cmac_128(dev, apdev):
"""WPA2-PSK-FT AP with PMF/BIP-CMAC-128"""
run_ap_ft_pmf_bip(dev, apdev, "AES-128-CMAC")
def test_ap_ft_pmf_bip_gmac_128(dev, apdev):
"""WPA2-PSK-FT AP with PMF/BIP-GMAC-128"""
run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-128")
def test_ap_ft_pmf_bip_gmac_256(dev, apdev):
"""WPA2-PSK-FT AP with PMF/BIP-GMAC-256"""
run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-256")
def test_ap_ft_pmf_bip_cmac_256(dev, apdev):
"""WPA2-PSK-FT AP with PMF/BIP-CMAC-256"""
run_ap_ft_pmf_bip(dev, apdev, "BIP-CMAC-256")
def run_ap_ft_pmf_bip(dev, apdev, cipher):
if cipher not in dev[0].get_capability("group_mgmt"):
raise HwsimSkip("Cipher %s not supported" % cipher)
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["group_mgmt_cipher"] = cipher
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["group_mgmt_cipher"] = cipher
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
group_mgmt=cipher)
def test_ap_ft_ocv(dev, apdev):
"""WPA2-PSK-FT AP with OCV"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
try:
hapd0 = hostapd.add_ap(apdev[0], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, ocv="1")
def test_ap_ft_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4")])
def cleanup_ap_ft_separate_hostapd():
subprocess.call(["brctl", "delif", "br0ft", "veth0"],
stderr=open('/dev/null', 'w'))
subprocess.call(["brctl", "delif", "br1ft", "veth1"],
stderr=open('/dev/null', 'w'))
subprocess.call(["ip", "link", "del", "veth0"],
stderr=open('/dev/null', 'w'))
subprocess.call(["ip", "link", "del", "veth1"],
stderr=open('/dev/null', 'w'))
for ifname in ['br0ft', 'br1ft', 'br-ft']:
subprocess.call(['ip', 'link', 'set', 'dev', ifname, 'down'],
stderr=open('/dev/null', 'w'))
subprocess.call(['brctl', 'delbr', ifname],
stderr=open('/dev/null', 'w'))
def test_ap_ft_separate_hostapd(dev, apdev, params):
"""WPA2-PSK-FT AP and separate hostapd process"""
try:
run_ap_ft_separate_hostapd(dev, apdev, params, False)
finally:
cleanup_ap_ft_separate_hostapd()
def test_ap_ft_over_ds_separate_hostapd(dev, apdev, params):
"""WPA2-PSK-FT AP over DS and separate hostapd process"""
try:
run_ap_ft_separate_hostapd(dev, apdev, params, True)
finally:
cleanup_ap_ft_separate_hostapd()
def run_ap_ft_separate_hostapd(dev, apdev, params, over_ds):
ssid = "test-ft"
passphrase = "12345678"
logdir = params['logdir']
pidfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.pid')
logfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.hapd')
global_ctrl = '/var/run/hostapd-ft'
br_ifname = 'br-ft'
try:
subprocess.check_call(['brctl', 'addbr', br_ifname])
subprocess.check_call(['brctl', 'setfd', br_ifname, '0'])
subprocess.check_call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth",
"peer", "name", "veth0br"])
subprocess.check_call(["ip", "link", "add", "veth1", "type", "veth",
"peer", "name", "veth1br"])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0br', 'up'])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1br', 'up'])
subprocess.check_call(['brctl', 'addif', br_ifname, 'veth0br'])
subprocess.check_call(['brctl', 'addif', br_ifname, 'veth1br'])
subprocess.check_call(['brctl', 'addbr', 'br0ft'])
subprocess.check_call(['brctl', 'setfd', 'br0ft', '0'])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'br0ft', 'up'])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0', 'up'])
subprocess.check_call(['brctl', 'addif', 'br0ft', 'veth0'])
subprocess.check_call(['brctl', 'addbr', 'br1ft'])
subprocess.check_call(['brctl', 'setfd', 'br1ft', '0'])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'br1ft', 'up'])
subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1', 'up'])
subprocess.check_call(['brctl', 'addif', 'br1ft', 'veth1'])
except subprocess.CalledProcessError:
raise HwsimSkip("Bridge or veth not supported (kernel CONFIG_VETH)")
with HWSimRadio() as (radio, iface):
prg = os.path.join(logdir, 'alt-hostapd/hostapd/hostapd')
if not os.path.exists(prg):
prg = '../../hostapd/hostapd'
cmd = [prg, '-B', '-ddKt',
'-P', pidfile, '-f', logfile, '-g', global_ctrl]
subprocess.check_call(cmd)
hglobal = hostapd.HostapdGlobal(global_ctrl_override=global_ctrl)
apdev_ft = {'ifname': iface}
apdev2 = [apdev_ft, apdev[1]]
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params['bridge'] = 'br0ft'
hapd0 = hostapd.add_ap(apdev2[0], params,
global_ctrl_override=global_ctrl)
apdev2[0]['bssid'] = hapd0.own_addr()
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params['bridge'] = 'br1ft'
hapd1 = hostapd.add_ap(apdev2[1], params)
run_roams(dev[0], apdev2, hapd0, hapd1, ssid, passphrase,
over_ds=over_ds, test_connectivity=False, roams=2)
hglobal.terminate()
if os.path.exists(pidfile):
with open(pidfile, 'r') as f:
pid = int(f.read())
f.close()
os.kill(pid, signal.SIGTERM)
def test_ap_ft_over_ds_ocv(dev, apdev):
"""WPA2-PSK-FT AP over DS"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
try:
hapd0 = hostapd.add_ap(apdev[0], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
ocv="1")
def test_ap_ft_over_ds_disabled(dev, apdev):
"""WPA2-PSK-FT AP over DS disabled"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['ft_over_ds'] = '0'
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['ft_over_ds'] = '0'
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True)
def test_ap_ft_vlan_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with VLAN"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
conndev="brvlan1")
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4")])
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_ft_over_ds_many(dev, apdev):
"""WPA2-PSK-FT AP over DS multiple times"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
roams=50)
def test_ap_ft_vlan_over_ds_many(dev, apdev):
"""WPA2-PSK-FT AP over DS with VLAN multiple times"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
roams=50, conndev="brvlan1")
if filename.startswith('/tmp/'):
os.unlink(filename)
@remote_compatible
def test_ap_ft_over_ds_unknown_target(dev, apdev):
"""WPA2-PSK-FT AP"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
dev[0].roam_over_ds("02:11:22:33:44:55", fail_test=True)
@remote_compatible
def test_ap_ft_over_ds_unexpected(dev, apdev):
"""WPA2-PSK-FT AP over DS and unexpected response"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
ap1 = apdev[0]
ap2 = apdev[1]
hapd1ap = hapd0
hapd2ap = hapd1
else:
ap1 = apdev[1]
ap2 = apdev[0]
hapd1ap = hapd1
hapd2ap = hapd0
addr = dev[0].own_addr()
hapd1ap.set("ext_mgmt_frame_handling", "1")
logger.info("Foreign STA address")
msg = {}
msg['fc'] = 13 << 4
msg['da'] = addr
msg['sa'] = ap1['bssid']
msg['bssid'] = ap1['bssid']
msg['payload'] = binascii.unhexlify("06021122334455660102030405060000")
hapd1ap.mgmt_tx(msg)
logger.info("No over-the-DS in progress")
msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
hapd1ap.mgmt_tx(msg)
logger.info("Non-zero status code")
msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060100")
hapd1ap.mgmt_tx(msg)
hapd1ap.dump_monitor()
dev[0].scan_for_bss(ap2['bssid'], freq="2412")
if "OK" not in dev[0].request("FT_DS " + ap2['bssid']):
raise Exception("FT_DS failed")
req = hapd1ap.mgmt_rx()
logger.info("Foreign Target AP")
msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
hapd1ap.mgmt_tx(msg)
addrs = addr.replace(':', '') + ap2['bssid'].replace(':', '')
logger.info("No IEs")
msg['payload'] = binascii.unhexlify("0602" + addrs + "0000")
hapd1ap.mgmt_tx(msg)
logger.info("Invalid IEs (trigger parsing failure)")
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003700")
hapd1ap.mgmt_tx(msg)
logger.info("Too short MDIE")
msg['payload'] = binascii.unhexlify("0602" + addrs + "000036021122")
hapd1ap.mgmt_tx(msg)
logger.info("Mobility domain mismatch")
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603112201")
hapd1ap.mgmt_tx(msg)
logger.info("No FTIE")
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201")
hapd1ap.mgmt_tx(msg)
logger.info("FTIE SNonce mismatch")
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "1000000000000000000000000000000000000000000000000000000000000001" + "030a6e6173322e77312e6669")
hapd1ap.mgmt_tx(msg)
logger.info("No R0KH-ID subelem in FTIE")
snonce = binascii.hexlify(req['payload'][111:111+32]).decode()
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137520000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce)
hapd1ap.mgmt_tx(msg)
logger.info("No R0KH-ID subelem mismatch in FTIE")
snonce = binascii.hexlify(req['payload'][111:111+32]).decode()
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a11223344556677889900")
hapd1ap.mgmt_tx(msg)
logger.info("No R1KH-ID subelem in FTIE")
r0khid = binascii.hexlify(req['payload'][145:145+10]).decode()
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid)
hapd1ap.mgmt_tx(msg)
logger.info("No RSNE")
r0khid = binascii.hexlify(req['payload'][145:145+10]).decode()
msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137660000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid + "0106000102030405")
hapd1ap.mgmt_tx(msg)
def test_ap_ft_pmf_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with PMF"""
run_ap_ft_pmf_bip_over_ds(dev, apdev, None)
def test_ap_ft_pmf_bip_cmac_128_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-128"""
run_ap_ft_pmf_bip_over_ds(dev, apdev, "AES-128-CMAC")
def test_ap_ft_pmf_bip_gmac_128_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-128"""
run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-128")
def test_ap_ft_pmf_bip_gmac_256_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-256"""
run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-256")
def test_ap_ft_pmf_bip_cmac_256_over_ds(dev, apdev):
"""WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-256"""
run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-CMAC-256")
def run_ap_ft_pmf_bip_over_ds(dev, apdev, cipher):
if cipher and cipher not in dev[0].get_capability("group_mgmt"):
raise HwsimSkip("Cipher %s not supported" % cipher)
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
if cipher:
params["group_mgmt_cipher"] = cipher
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
if cipher:
params["group_mgmt_cipher"] = cipher
hapd1 = hostapd.add_ap(apdev[1], params)
Wlantest.setup(hapd0)
wt = Wlantest()
wt.flush()
wt.add_passphrase(passphrase)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
group_mgmt=cipher)
def test_ap_ft_over_ds_pull(dev, apdev):
"""WPA2-PSK-FT AP over DS (pull PMK)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
def test_ap_ft_over_ds_pull_old_key(dev, apdev):
"""WPA2-PSK-FT AP over DS (pull PMK; old key)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1_old_key(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_old_key(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
def test_ap_ft_over_ds_pull_vlan(dev, apdev):
"""WPA2-PSK-FT AP over DS (pull PMK) with VLAN"""
ssid = "test-ft"
passphrase = "12345678"
filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
hostapd.send_file(apdev[0], filename, filename)
hostapd.send_file(apdev[1], filename, filename)
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['dynamic_vlan'] = "1"
params['accept_mac_file'] = filename
hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
conndev="brvlan1")
if filename.startswith('/tmp/'):
os.unlink(filename)
def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None,
rsne_override=None, rsnxe_override=None,
no_beacon_rsnxe2=False, ext_key_id=False,
skip_prune_assoc=False, ft_rsnxe_used=False,
sae_transition=False):
if "SAE" not in dev.get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
if sae_pwe is not None:
params['sae_pwe'] = sae_pwe
if rsne_override:
params['rsne_override_ft'] = rsne_override
if rsnxe_override:
params['rsnxe_override_ft'] = rsnxe_override
if ext_key_id:
params['extended_key_id'] = '1'
if skip_prune_assoc:
params['skip_prune_assoc'] = '1'
if ft_rsnxe_used:
params['ft_rsnxe_used'] = '1'
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
if not sae_transition:
params['wpa_key_mgmt'] = "FT-SAE"
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
if sae_pwe is not None:
params['sae_pwe'] = sae_pwe
if rsne_override:
params['rsne_override_ft'] = rsne_override
if rsnxe_override:
params['rsnxe_override_ft'] = rsnxe_override
if no_beacon_rsnxe2:
params['no_beacon_rsnxe'] = "1"
if ext_key_id:
params['extended_key_id'] = '1'
if skip_prune_assoc:
params['skip_prune_assoc'] = '1'
if ft_rsnxe_used:
params['ft_rsnxe_used'] = '1'
hapd1 = hostapd.add_ap(apdev[1], params)
key_mgmt = hapd1.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-SAE" and not sae_transition:
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
dev.request("SET sae_groups ")
return hapd0, hapd1
def test_ap_ft_sae(dev, apdev):
"""WPA2-PSK-FT-SAE AP"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
def test_ap_ft_sae_transition(dev, apdev):
"""WPA2-PSK-FT-SAE/PSK AP"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_transition=True)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
sae_transition=True)
def test_ap_ft_sae_h2e(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E)"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_and_loop(dev, apdev):
"""WPA2-PSK-FT-SAE AP (AP H2E, STA loop)"""
dev[0].set("sae_pwe", "0")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
def test_ap_ft_sae_h2e_and_loop2(dev, apdev):
"""WPA2-PSK-FT-SAE AP (AP loop, STA H2E)"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="0")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_downgrade_attack(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E downgrade attack)"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
no_beacon_rsnxe2=True)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
force_initial_conn_to_first_ap=True,
return_after_initial=True)
dev[0].scan_for_bss(hapd1.own_addr(), freq="2412")
if "OK" not in dev[0].request("ROAM " + hapd1.own_addr()):
raise Exception("ROAM command failed")
# The target AP is expected to discard Reassociation Response frame due
# to RSNXE Used mismatch. This will result in roaming timeout and
# returning back to the old AP.
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev and "CTRL-EVENT-ASSOC-REJECT" in ev:
pass
elif ev and hapd1.own_addr() in ev:
raise Exception("Roaming succeeded unexpectedly")
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_ptk_rekey0(dev, apdev):
"""WPA2-PSK-FT-SAE AP and PTK rekey triggered by station"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
ptk_rekey="1", roams=0)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_ptk_rekey1(dev, apdev):
"""WPA2-PSK-FT-SAE AP and PTK rekey triggered by station"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
ptk_rekey="1", only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_ptk_rekey_ap(dev, apdev):
"""WPA2-PSK-FT-SAE AP and PTK rekey triggered by AP"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_ptk_rekey_ap_ext_key_id(dev, apdev):
"""WPA2-PSK-FT-SAE AP and PTK rekey triggered by AP (Ext Key ID)"""
check_ext_key_id_capa(dev[0])
try:
dev[0].set("extended_key_id", "1")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2,
ext_key_id=True)
check_ext_key_id_capa(hapd0)
check_ext_key_id_capa(hapd1)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
idx = int(dev[0].request("GET last_tk_key_idx"))
if idx != 1:
raise Exception("Unexpected Key ID after TK rekey: %d" % idx)
finally:
dev[0].set("extended_key_id", "0")
def test_ap_ft_sae_over_ds(dev, apdev):
"""WPA2-PSK-FT-SAE AP over DS"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
over_ds=True)
def test_ap_ft_sae_over_ds_ptk_rekey0(dev, apdev):
"""WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by station"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
over_ds=True, ptk_rekey="1", roams=0)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_over_ds_ptk_rekey1(dev, apdev):
"""WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by station"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
over_ds=True, ptk_rekey="1", only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_over_ds_ptk_rekey_ap(dev, apdev):
"""WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by AP"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
over_ds=True, only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_sae_h2e_rsne_override(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E) and RSNE override (same value)"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
rsne_override="30260100000fac040100000fac040100000fac090c000100" + 16*"ff")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_rsnxe_override(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E) and RSNXE override (same value)"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
rsnxe_override="F40120")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_rsne_mismatch(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E) and RSNE mismatch"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
rsne_override="30260100000fac040100000fac040100000fac090c010100" + 16*"ff")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
fail_test=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_rsne_mismatch_pmkr1name(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E) and RSNE mismatch in PMKR1Name"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
rsne_override="30260100000fac040100000fac040100000fac090c000100" + 16*"00")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
fail_test=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_h2e_rsnxe_mismatch(dev, apdev):
"""WPA2-PSK-FT-SAE AP (H2E) and RSNXE mismatch"""
try:
dev[0].set("sae_pwe", "2")
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
rsnxe_override="F40160")
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
fail_test=True)
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_rsnxe_used_mismatch(dev, apdev):
"""FT-SAE AP and unexpected RSNXE Used in ReassocReq"""
try:
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
dev[0].set("sae_pwe", "0")
dev[0].set("ft_rsnxe_used", "1")
next = run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
sae=True, return_after_initial=True)
if "OK" not in dev[0].request("ROAM " + next):
raise Exception("ROAM command failed")
# The target AP is expected to discard Reassociation Request frame due
# to RSNXE Used mismatch. This will result in roaming timeout and
# returning back to the old AP.
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
if ev and next in ev:
raise Exception("Roaming succeeded unexpectedly")
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_rsnxe_used_mismatch2(dev, apdev):
"""FT-SAE AP and unexpected RSNXE Used in ReassocResp"""
try:
hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="0",
ft_rsnxe_used=True)
dev[0].set("sae_pwe", "2")
next = run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
sae=True, return_after_initial=True)
if "OK" not in dev[0].request("ROAM " + next):
raise Exception("ROAM command failed")
# The STA is expected to discard Reassociation Response frame due to
# RSNXE Used mismatch. This will result in returning back to the old AP.
ev = dev[0].wait_disconnected()
if next not in ev:
raise Exception("Unexpected disconnection BSSID: " + ev)
if "reason=13 locally_generated=1" not in ev:
raise Exception("Unexpected disconnection reason: " + ev)
ev = dev[0].wait_connected()
if next in ev:
raise Exception("Roaming succeeded unexpectedly")
hapd0.set("ft_rsnxe_used", "0")
hapd1.set("ft_rsnxe_used", "0")
dev[0].roam(next);
finally:
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_pw_id(dev, apdev):
"""FT-SAE with Password Identifier"""
if "SAE" not in dev[0].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
params = ft_params1(ssid=ssid)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-SAE"
params['sae_password'] = 'secret|id=pwid'
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-SAE"
params['sae_password'] = 'secret|id=pwid'
hapd = hostapd.add_ap(apdev[1], params)
dev[0].request("SET sae_groups ")
run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase=None, sae=True,
sae_password="secret", sae_password_id="pwid")
def test_ap_ft_sae_with_both_akms(dev, apdev):
"""SAE + FT-SAE configuration"""
if "SAE" not in dev[0].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE SAE"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE SAE"
hapd = hostapd.add_ap(apdev[1], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-SAE":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
dev[0].request("SET sae_groups ")
run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
sae_and_psk=True)
def test_ap_ft_sae_pmksa_caching(dev, apdev):
"""WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association"""
if "SAE" not in dev[0].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
hapd = hostapd.add_ap(apdev[1], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-SAE":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
dev[0].request("SET sae_groups ")
run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
pmksa_caching=True)
def test_ap_ft_sae_pmksa_caching_pwe(dev, apdev):
"""WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association (STA PWE both)"""
if "SAE" not in dev[0].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
hapd = hostapd.add_ap(apdev[1], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-SAE":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
try:
dev[0].request("SET sae_groups ")
dev[0].set("sae_pwe", "2")
run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
pmksa_caching=True)
finally:
dev[0].set("sae_groups", "")
dev[0].set("sae_pwe", "0")
def test_ap_ft_sae_pmksa_caching_h2e(dev, apdev):
"""WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association (H2E)"""
if "SAE" not in dev[0].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
params['sae_pwe'] = "1"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-SAE"
params['sae_pwe'] = "1"
hapd = hostapd.add_ap(apdev[1], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-SAE":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
try:
dev[0].request("SET sae_groups ")
dev[0].set("sae_pwe", "1")
run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
pmksa_caching=True)
finally:
dev[0].set("sae_groups", "")
dev[0].set("sae_pwe", "0")
def generic_ap_ft_eap(dev, apdev, vlan=False, cui=False, over_ds=False,
discovery=False, roams=1, wpa_ptk_rekey=0,
only_one_way=False):
ssid = "test-ft"
passphrase = "12345678"
if vlan:
identity = "gpsk-vlan1"
conndev = "brvlan1"
elif cui:
identity = "gpsk-cui"
conndev = False
else:
identity = "gpsk user"
conndev = False
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=discovery)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
if vlan:
params["dynamic_vlan"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=discovery)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
if vlan:
params["dynamic_vlan"] = "1"
if wpa_ptk_rekey:
params["wpa_ptk_rekey"] = str(wpa_ptk_rekey)
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True,
over_ds=over_ds, roams=roams, eap_identity=identity,
conndev=conndev, only_one_way=only_one_way)
if "[WPA2-FT/EAP-CCMP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-3"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-3")])
if only_one_way:
return
# Verify EAPOL reauthentication after FT protocol
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
ap = hapd
else:
ap = hapd1
ap.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
if conndev:
hwsim_utils.test_connectivity_iface(dev[0], ap, conndev)
else:
hwsim_utils.test_connectivity(dev[0], ap)
def test_ap_ft_eap(dev, apdev):
"""WPA2-EAP-FT AP"""
generic_ap_ft_eap(dev, apdev)
def test_ap_ft_eap_cui(dev, apdev):
"""WPA2-EAP-FT AP with CUI"""
generic_ap_ft_eap(dev, apdev, vlan=False, cui=True)
def test_ap_ft_eap_vlan(dev, apdev):
"""WPA2-EAP-FT AP with VLAN"""
generic_ap_ft_eap(dev, apdev, vlan=True)
def test_ap_ft_eap_vlan_multi(dev, apdev):
"""WPA2-EAP-FT AP with VLAN"""
generic_ap_ft_eap(dev, apdev, vlan=True, roams=50)
def test_ap_ft_eap_over_ds(dev, apdev):
"""WPA2-EAP-FT AP using over-the-DS"""
generic_ap_ft_eap(dev, apdev, over_ds=True)
def test_ap_ft_eap_dis(dev, apdev):
"""WPA2-EAP-FT AP with AP discovery"""
generic_ap_ft_eap(dev, apdev, discovery=True)
def test_ap_ft_eap_dis_over_ds(dev, apdev):
"""WPA2-EAP-FT AP with AP discovery and over-the-DS"""
generic_ap_ft_eap(dev, apdev, over_ds=True, discovery=True)
def test_ap_ft_eap_vlan(dev, apdev):
"""WPA2-EAP-FT AP with VLAN"""
generic_ap_ft_eap(dev, apdev, vlan=True)
def test_ap_ft_eap_vlan_multi(dev, apdev):
"""WPA2-EAP-FT AP with VLAN"""
generic_ap_ft_eap(dev, apdev, vlan=True, roams=50)
def test_ap_ft_eap_vlan_over_ds(dev, apdev):
"""WPA2-EAP-FT AP with VLAN + over_ds"""
generic_ap_ft_eap(dev, apdev, vlan=True, over_ds=True)
def test_ap_ft_eap_vlan_over_ds_multi(dev, apdev):
"""WPA2-EAP-FT AP with VLAN + over_ds"""
generic_ap_ft_eap(dev, apdev, vlan=True, over_ds=True, roams=50)
def generic_ap_ft_eap_pull(dev, apdev, vlan=False):
"""WPA2-EAP-FT AP (pull PMK)"""
ssid = "test-ft"
passphrase = "12345678"
if vlan:
identity = "gpsk-vlan1"
conndev = "brvlan1"
else:
identity = "gpsk user"
conndev = False
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
if vlan:
params["dynamic_vlan"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
if vlan:
params["dynamic_vlan"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True,
eap_identity=identity, conndev=conndev)
def test_ap_ft_eap_pull(dev, apdev):
"""WPA2-EAP-FT AP (pull PMK)"""
generic_ap_ft_eap_pull(dev, apdev)
def test_ap_ft_eap_pull_vlan(dev, apdev):
"""WPA2-EAP-FT AP (pull PMK) - with VLAN"""
generic_ap_ft_eap_pull(dev, apdev, vlan=True)
def test_ap_ft_eap_pull_wildcard(dev, apdev):
"""WPA2-EAP-FT AP (pull PMK) - wildcard R0KH/R1KH"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["ft_psk_generate_local"] = "1"
params["eap_server"] = "0"
params["rkh_pos_timeout"] = "100"
params["rkh_neg_timeout"] = "50"
params["rkh_pull_timeout"] = "1234"
params["rkh_pull_retries"] = "10"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=True)
params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["ft_psk_generate_local"] = "1"
params["eap_server"] = "0"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
def test_ap_ft_eap_pull_wildcard_multi_bss(dev, apdev, params):
"""WPA2-EAP-FT AP (pull PMK) - wildcard R0KH/R1KH with multiple BSSs"""
bssconf = os.path.join(params['logdir'],
'ap_ft_eap_pull_wildcard_multi_bss.bss.conf')
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["eap_server"] = "0"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
ifname2 = apdev[0]['ifname'] + "-2"
bssid2 = "02:00:00:00:03:01"
params['nas_identifier'] = "nas1b.w1.fi"
params['r1_key_holder'] = "000102030415"
with open(bssconf, 'w') as f:
f.write("driver=nl80211\n")
f.write("hw_mode=g\n")
f.write("channel=1\n")
f.write("ieee80211n=1\n")
f.write("interface=%s\n" % ifname2)
f.write("bssid=%s\n" % bssid2)
f.write("ctrl_interface=/var/run/hostapd\n")
fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
"wpa", "rsn_pairwise", "auth_server_addr"]
for name in fields:
f.write("%s=%s\n" % (name, params[name]))
for name, val in params.items():
if name in fields:
continue
f.write("%s=%s\n" % (name, val))
hapd2 = hostapd.add_bss(apdev[0], ifname2, bssconf)
params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=True)
params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["eap_server"] = "0"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
# The first iteration of the roaming test will use wildcard R0KH discovery
# and RRB sequence number synchronization while the second iteration shows
# the clean RRB exchange where those extra steps are not needed.
for i in range(2):
hapd.note("Test iteration %d" % i)
dev[0].note("Test iteration %d" % i)
id = dev[0].connect(ssid, key_mgmt="FT-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
bssid=bssid2,
scan_freq="2412")
res = dev[0].get_status_field("bssid")
if res != bssid2:
raise Exception("Unexpected BSSID after initial connection: " + res)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].set_network(id, "bssid", "00:00:00:00:00:00")
dev[0].roam(apdev[1]['bssid'])
res = dev[0].get_status_field("bssid")
if res != apdev[1]['bssid']:
raise Exception("Unexpected BSSID after first roam: " + res)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].roam(apdev[0]['bssid'])
res = dev[0].get_status_field("bssid")
if res != apdev[0]['bssid']:
raise Exception("Unexpected BSSID after second roam: " + res)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.dump_monitor()
hapd2.dump_monitor()
@remote_compatible
def test_ap_ft_mismatching_rrb_key_push(dev, apdev):
"""WPA2-PSK-FT AP over DS with mismatching RRB key (push)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True)
@remote_compatible
def test_ap_ft_mismatching_rrb_key_pull(dev, apdev):
"""WPA2-PSK-FT AP over DS with mismatching RRB key (pull)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True)
@remote_compatible
def test_ap_ft_mismatching_r0kh_id_pull(dev, apdev):
"""WPA2-PSK-FT AP over DS with mismatching R0KH-ID (pull)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params["nas_identifier"] = "nas0.w1.fi"
hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
@remote_compatible
def test_ap_ft_mismatching_rrb_r0kh_push(dev, apdev):
"""WPA2-PSK-FT AP over DS with mismatching R0KH key (push)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True)
@remote_compatible
def test_ap_ft_mismatching_rrb_r0kh_pull(dev, apdev):
"""WPA2-PSK-FT AP over DS with mismatching R0KH key (pull)"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True)
def test_ap_ft_mismatching_rrb_key_push_eap(dev, apdev):
"""WPA2-EAP-FT AP over DS with mismatching RRB key (push)"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True, eap=True)
def test_ap_ft_mismatching_rrb_key_pull_eap(dev, apdev):
"""WPA2-EAP-FT AP over DS with mismatching RRB key (pull)"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True, eap=True)
def test_ap_ft_mismatching_r0kh_id_pull_eap(dev, apdev):
"""WPA2-EAP-FT AP over DS with mismatching R0KH-ID (pull)"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params["nas_identifier"] = "nas0.w1.fi"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
def test_ap_ft_mismatching_rrb_r0kh_push_eap(dev, apdev):
"""WPA2-EAP-FT AP over DS with mismatching R0KH key (push)"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True, eap=True)
def test_ap_ft_mismatching_rrb_r0kh_pull_eap(dev, apdev):
"""WPA2-EAP-FT AP over DS with mismatching R0KH key (pull)"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["pmk_r1_push"] = "0"
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
fail_test=True, eap=True)
def test_ap_ft_gtk_rekey(dev, apdev):
"""WPA2-PSK-FT AP and GTK rekey"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_group_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412")
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out after initial association")
hwsim_utils.test_connectivity(dev[0], hapd)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_group_rekey'] = '1'
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].roam(apdev[1]['bssid'])
if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
raise Exception("Did not connect to correct AP")
hwsim_utils.test_connectivity(dev[0], hapd1)
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out after FT protocol")
hwsim_utils.test_connectivity(dev[0], hapd1)
def test_ft_psk_key_lifetime_in_memory(dev, apdev, params):
"""WPA2-PSK-FT and key lifetime in memory"""
ssid = "test-ft"
passphrase = "04c2726b4b8d5f1b4db9c07aa4d9e9d8f765cb5d25ec817e6cc4fcdd5255db0"
psk = '93c90846ff67af9037ed83fb72b63dbeddaa81d47f926c20909b5886f1d9358d'
pmk = binascii.unhexlify(psk)
p = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], p)
p = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], p)
pid = find_wpas_process(dev[0])
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
# The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
# event has been delivered, so verify that wpa_supplicant has returned to
# eloop before reading process memory.
time.sleep(1)
dev[0].ping()
buf = read_process_memory(pid, pmk)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].relog()
pmkr0 = None
pmkr1 = None
ptk = None
gtk = None
with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
for l in f.readlines():
if "FT: PMK-R0 - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
pmkr0 = binascii.unhexlify(val)
if "FT: PMK-R1 - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
pmkr1 = binascii.unhexlify(val)
if "FT: KCK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
kck = binascii.unhexlify(val)
if "FT: KEK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
kek = binascii.unhexlify(val)
if "FT: TK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
tk = binascii.unhexlify(val)
if "WPA: Group Key - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
gtk = binascii.unhexlify(val)
if not pmkr0 or not pmkr1 or not kck or not kek or not tk or not gtk:
raise Exception("Could not find keys from debug log")
if len(gtk) != 16:
raise Exception("Unexpected GTK length")
logger.info("Checking keys in memory while associated")
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, pmkr0, "PMK-R0")
get_key_locations(buf, pmkr1, "PMK-R1")
if pmk not in buf:
raise HwsimSkip("PMK not found while associated")
if pmkr0 not in buf:
raise HwsimSkip("PMK-R0 not found while associated")
if pmkr1 not in buf:
raise HwsimSkip("PMK-R1 not found while associated")
if kck not in buf:
raise Exception("KCK not found while associated")
if kek not in buf:
raise Exception("KEK not found while associated")
#if tk in buf:
# raise Exception("TK found from memory")
logger.info("Checking keys in memory after disassociation")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, pmkr0, "PMK-R0")
get_key_locations(buf, pmkr1, "PMK-R1")
# Note: PMK/PSK is still present in network configuration
fname = os.path.join(params['logdir'],
'ft_psk_key_lifetime_in_memory.memctx-')
verify_not_present(buf, pmkr0, fname, "PMK-R0")
verify_not_present(buf, pmkr1, fname, "PMK-R1")
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
if gtk in buf:
get_key_locations(buf, gtk, "GTK")
verify_not_present(buf, gtk, fname, "GTK")
dev[0].request("REMOVE_NETWORK all")
logger.info("Checking keys in memory after network profile removal")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
get_key_locations(buf, pmkr0, "PMK-R0")
get_key_locations(buf, pmkr1, "PMK-R1")
verify_not_present(buf, pmk, fname, "PMK")
verify_not_present(buf, pmkr0, fname, "PMK-R0")
verify_not_present(buf, pmkr1, fname, "PMK-R1")
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
verify_not_present(buf, gtk, fname, "GTK")
@remote_compatible
def test_ap_ft_invalid_resp(dev, apdev):
"""WPA2-PSK-FT AP and invalid response IEs"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
tests = [
# Various IEs for test coverage. The last one is FTIE with invalid
# R1KH-ID subelement.
"020002000000" + "3800" + "38051122334455" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010100",
# FTIE with invalid R0KH-ID subelement (len=0).
"020002000000" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010300",
# FTIE with invalid R0KH-ID subelement (len=49).
"020002000000" + "378500010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001033101020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849",
# Invalid RSNE.
"020002000000" + "3000",
# Required IEs missing from protected IE count.
"020002000000" + "3603a1b201" + "375200010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
# RIC missing from protected IE count.
"020002000000" + "3603a1b201" + "375200020203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
# Protected IE missing.
"020002000000" + "3603a1b201" + "375200ff0203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900" + "0000"]
for t in tests:
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
hapd1.set("ext_mgmt_frame_handling", "1")
hapd1.dump_monitor()
if "OK" not in dev[0].request("ROAM " + apdev[1]['bssid']):
raise Exception("ROAM failed")
auth = None
for i in range(20):
msg = hapd1.mgmt_rx()
if msg['subtype'] == 11:
auth = msg
break
if not auth:
raise Exception("Authentication frame not seen")
resp = {}
resp['fc'] = auth['fc']
resp['da'] = auth['sa']
resp['sa'] = auth['da']
resp['bssid'] = auth['bssid']
resp['payload'] = binascii.unhexlify(t)
hapd1.mgmt_tx(resp)
hapd1.set("ext_mgmt_frame_handling", "0")
dev[0].wait_disconnected()
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_ft_gcmp_256(dev, apdev):
"""WPA2-PSK-FT AP with GCMP-256 cipher"""
if "GCMP-256" not in dev[0].get_capability("pairwise"):
raise HwsimSkip("Cipher GCMP-256 not supported")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['rsn_pairwise'] = "GCMP-256"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['rsn_pairwise'] = "GCMP-256"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
pairwise_cipher="GCMP-256", group_cipher="GCMP-256")
def setup_ap_ft_oom(dev, apdev):
skip_with_fips(dev[0])
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
dst = apdev[1]['bssid']
else:
dst = apdev[0]['bssid']
dev[0].scan_for_bss(dst, freq="2412")
return dst
def test_ap_ft_oom(dev, apdev):
"""WPA2-PSK-FT and OOM"""
dst = setup_ap_ft_oom(dev, apdev)
with alloc_fail(dev[0], 1, "wpa_ft_gen_req_ies"):
dev[0].roam(dst, check_bssid=False, fail_test=True)
def test_ap_ft_oom2(dev, apdev):
"""WPA2-PSK-FT and OOM (2)"""
dst = setup_ap_ft_oom(dev, apdev)
with fail_test(dev[0], 1, "wpa_ft_mic"):
dev[0].roam(dst, fail_test=True, assoc_reject_ok=True)
def test_ap_ft_oom3(dev, apdev):
"""WPA2-PSK-FT and OOM (3)"""
dst = setup_ap_ft_oom(dev, apdev)
with fail_test(dev[0], 1, "os_get_random;wpa_ft_prepare_auth_request"):
dev[0].roam(dst)
def test_ap_ft_oom4(dev, apdev):
"""WPA2-PSK-FT and OOM (4)"""
ssid = "test-ft"
passphrase = "12345678"
dst = setup_ap_ft_oom(dev, apdev)
dev[0].request("REMOVE_NETWORK all")
with alloc_fail(dev[0], 1, "=sme_update_ft_ies"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
def test_ap_ft_ap_oom(dev, apdev):
"""WPA2-PSK-FT and AP OOM"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
with alloc_fail(hapd0, 1, "wpa_ft_store_pmk_r0"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
# This roam will fail due to missing PMK-R0 (OOM prevented storing it)
dev[0].roam(bssid1, check_bssid=False)
def test_ap_ft_ap_oom2(dev, apdev):
"""WPA2-PSK-FT and AP OOM 2"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
with alloc_fail(hapd0, 1, "wpa_ft_store_pmk_r1"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
dev[0].roam(bssid1)
if dev[0].get_status_field('bssid') != bssid1:
raise Exception("Did not roam to AP1")
# This roam will fail due to missing PMK-R1 (OOM prevented storing it)
dev[0].roam(bssid0)
def test_ap_ft_ap_oom3(dev, apdev):
"""WPA2-PSK-FT and AP OOM 3"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd1, 1, "wpa_ft_pull_pmk_r1"):
# This will fail due to not being able to send out PMK-R1 pull request
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 2, "os_get_random;wpa_ft_pull_pmk_r1"):
# This will fail due to not being able to send out PMK-R1 pull request
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 2, "aes_siv_encrypt;wpa_ft_pull_pmk_r1"):
# This will fail due to not being able to send out PMK-R1 pull request
dev[0].roam(bssid1, check_bssid=False)
def test_ap_ft_ap_oom3b(dev, apdev):
"""WPA2-PSK-FT and AP OOM 3b"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with fail_test(hapd1, 1, "os_get_random;wpa_ft_pull_pmk_r1"):
# This will fail due to not being able to send out PMK-R1 pull request
dev[0].roam(bssid1)
def test_ap_ft_ap_oom4(dev, apdev):
"""WPA2-PSK-FT and AP OOM 4"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd1, 1, "wpa_ft_gtk_subelem"):
dev[0].roam(bssid1)
if dev[0].get_status_field('bssid') != bssid1:
raise Exception("Did not roam to AP1")
with fail_test(hapd0, 1, "i802_get_seqnum;wpa_ft_gtk_subelem"):
dev[0].roam(bssid0)
if dev[0].get_status_field('bssid') != bssid0:
raise Exception("Did not roam to AP0")
with fail_test(hapd0, 1, "aes_wrap;wpa_ft_gtk_subelem"):
dev[0].roam(bssid1)
if dev[0].get_status_field('bssid') != bssid1:
raise Exception("Did not roam to AP1")
def test_ap_ft_ap_oom5(dev, apdev):
"""WPA2-PSK-FT and AP OOM 5"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd1, 1, "=wpa_ft_process_auth_req"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 1, "os_get_random;wpa_ft_process_auth_req"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 1, "wpa_derive_pmk_r1_name;wpa_ft_process_auth_req"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
def test_ap_ft_ap_oom6(dev, apdev):
"""WPA2-PSK-FT and AP OOM 6"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
with fail_test(hapd0, 1, "wpa_derive_pmk_r0;wpa_auth_derive_ptk_ft"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_auth_derive_ptk_ft"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(hapd0, 1, "wpa_pmk_r1_to_ptk;wpa_auth_derive_ptk_ft"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
def test_ap_ft_ap_oom7a(dev, apdev):
"""WPA2-PSK-FT and AP OOM 7a"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="2", scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd1, 1, "wpa_ft_igtk_subelem"):
# This will fail to roam
dev[0].roam(bssid1)
def test_ap_ft_ap_oom7b(dev, apdev):
"""WPA2-PSK-FT and AP OOM 7b"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="2", scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with fail_test(hapd1, 1, "aes_wrap;wpa_ft_igtk_subelem"):
# This will fail to roam
dev[0].roam(bssid1)
def test_ap_ft_ap_oom7c(dev, apdev):
"""WPA2-PSK-FT and AP OOM 7c"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="2", scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd1, 1, "=wpa_sm_write_assoc_resp_ies"):
# This will fail to roam
dev[0].roam(bssid1)
def test_ap_ft_ap_oom7d(dev, apdev):
"""WPA2-PSK-FT and AP OOM 7d"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="2", scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with fail_test(hapd1, 1, "wpa_ft_mic;wpa_sm_write_assoc_resp_ies"):
# This will fail to roam
dev[0].roam(bssid1)
def test_ap_ft_ap_oom8(dev, apdev):
"""WPA2-PSK-FT and AP OOM 8"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['ft_psk_generate_local'] = "1"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['ft_psk_generate_local'] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with fail_test(hapd1, 1, "wpa_derive_pmk_r0;wpa_ft_psk_pmk_r1"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
with fail_test(hapd1, 1, "wpa_derive_pmk_r1;wpa_ft_psk_pmk_r1"):
# This will fail to roam
dev[0].roam(bssid1, check_bssid=False)
def test_ap_ft_ap_oom9(dev, apdev):
"""WPA2-PSK-FT and AP OOM 9"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with alloc_fail(hapd0, 1, "wpa_ft_action_rx"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd0, "GET_ALLOC_FAIL")
with alloc_fail(hapd1, 1, "wpa_ft_rrb_rx_request"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd1, "GET_ALLOC_FAIL")
with alloc_fail(hapd1, 1, "wpa_ft_send_rrb_auth_resp"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd1, "GET_ALLOC_FAIL")
def test_ap_ft_ap_oom10(dev, apdev):
"""WPA2-PSK-FT and AP OOM 10"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq="2412")
with fail_test(hapd0, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_pull"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd0, "GET_FAIL")
with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_ft_rrb_rx_pull"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd0, "GET_FAIL")
with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_rrb_rx_pull"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd0, "GET_FAIL")
with fail_test(hapd1, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_resp"):
# This will fail to roam
if "OK" not in dev[0].request("FT_DS " + bssid1):
raise Exception("FT_DS failed")
wait_fail_trigger(hapd1, "GET_FAIL")
def test_ap_ft_ap_oom11(dev, apdev):
"""WPA2-PSK-FT and AP OOM 11"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
dev[0].scan_for_bss(bssid0, freq="2412")
with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_ft_generate_pmk_r1"):
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
wait_fail_trigger(hapd0, "GET_FAIL")
dev[1].scan_for_bss(bssid0, freq="2412")
with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_generate_pmk_r1"):
dev[1].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
wait_fail_trigger(hapd0, "GET_FAIL")
def test_ap_ft_over_ds_proto_ap(dev, apdev):
"""WPA2-PSK-FT AP over DS protocol testing for AP processing"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
_bssid0 = bssid0.replace(':', '')
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
addr = dev[0].own_addr()
_addr = addr.replace(':', '')
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
_bssid1 = bssid1.replace(':', '')
hapd0.set("ext_mgmt_frame_handling", "1")
hdr = "d0003a01" + _bssid0 + _addr + _bssid0 + "1000"
valid = "0601" + _addr + _bssid1
tests = ["0601",
"0601" + _addr,
"0601" + _addr + _bssid0,
"0601" + _addr + "ffffffffffff",
"0601" + _bssid0 + _bssid0,
valid,
valid + "01",
valid + "3700",
valid + "3600",
valid + "3603ffffff",
valid + "3603a1b2ff",
valid + "3603a1b2ff" + "3700",
valid + "3603a1b2ff" + "37520000" + 16*"00" + 32*"00" + 32*"00",
valid + "3603a1b2ff" + "37520001" + 16*"00" + 32*"00" + 32*"00",
valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa",
valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "3000",
valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac040100000facff00000100a225368fe0983b5828a37a0acb37f253",
valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac030100000fac0400000100a225368fe0983b5828a37a0acb37f253",
valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac040100000fac0400000100a225368fe0983b5828a37a0acb37f253",
valid + "0001"]
for t in tests:
hapd0.dump_monitor()
if "OK" not in hapd0.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
hapd0.set("ext_mgmt_frame_handling", "0")
def test_ap_ft_over_ds_proto(dev, apdev):
"""WPA2-PSK-FT AP over DS protocol testing"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
# FT Action Response while no FT-over-DS in progress
msg = {}
msg['fc'] = 13 << 4
msg['da'] = dev[0].own_addr()
msg['sa'] = apdev[0]['bssid']
msg['bssid'] = apdev[0]['bssid']
msg['payload'] = binascii.unhexlify("06020200000000000200000004000000")
hapd0.mgmt_tx(msg)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
hapd0.set("ext_mgmt_frame_handling", "1")
hapd0.dump_monitor()
dev[0].request("FT_DS " + apdev[1]['bssid'])
for i in range(0, 10):
req = hapd0.mgmt_rx()
if req is None:
raise Exception("MGMT RX wait timed out")
if req['subtype'] == 13:
break
req = None
if not req:
raise Exception("FT Action frame not received")
# FT Action Response for unexpected Target AP
msg['payload'] = binascii.unhexlify("0602020000000000" + "f20000000400" + "0000")
hapd0.mgmt_tx(msg)
# FT Action Response without MDIE
msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000")
hapd0.mgmt_tx(msg)
# FT Action Response without FTIE
msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201")
hapd0.mgmt_tx(msg)
# FT Action Response with FTIE SNonce mismatch
msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201" + "3766000000000000000000000000000000000000c4e67ac1999bebd00ff4ae4d5dcaf87896bb060b469f7c78d49623fb395c3455ffffff6b693fe6f8d8c5dfac0a22344750775bd09437f98b238c9f87b97f790c0106000102030406030a6e6173312e77312e6669")
hapd0.mgmt_tx(msg)
@remote_compatible
def test_ap_ft_rrb(dev, apdev):
"""WPA2-PSK-FT RRB protocol testing"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
_dst_ll = binascii.unhexlify(apdev[0]['bssid'].replace(':', ''))
_src_ll = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
proto = b'\x89\x0d'
ehdr = _dst_ll + _src_ll + proto
# Too short RRB frame
pkt = ehdr + b'\x01'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# RRB discarded frame wikth unrecognized type
pkt = ehdr + b'\x02' + b'\x02' + b'\x01\x00' + _src_ll
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# RRB frame too short for action frame
pkt = ehdr + b'\x01' + b'\x02' + b'\x01\x00' + _src_ll
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Too short RRB frame (not enough room for Action Frame body)
pkt = ehdr + b'\x01' + b'\x02' + b'\x00\x00' + _src_ll
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Unexpected Action frame category
pkt = ehdr + b'\x01' + b'\x02' + b'\x0e\x00' + _src_ll + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Unexpected Action in RRB Request
pkt = ehdr + b'\x01' + b'\x00' + b'\x0e\x00' + _src_ll + b'\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Target AP address in RRB Request does not match with own address
pkt = ehdr + b'\x01' + b'\x00' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Not enough room for status code in RRB Response
pkt = ehdr + b'\x01' + b'\x01' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# RRB discarded frame with unknown packet_type
pkt = ehdr + b'\x01' + b'\x02' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# RRB Response with non-zero status code; no STA match
pkt = ehdr + b'\x01' + b'\x01' + b'\x10\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\xff\xff'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# RRB Response with zero status code and extra data; STA match
pkt = ehdr + b'\x01' + b'\x01' + b'\x11\x00' + _src_ll + b'\x06\x01' + _src_ll + b'\x00\x00\x00\x00\x00\x00' + b'\x00\x00' + b'\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Too short PMK-R1 pull
pkt = ehdr + b'\x01' + b'\xc8' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Too short PMK-R1 resp
pkt = ehdr + b'\x01' + b'\xc9' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# Too short PMK-R1 push
pkt = ehdr + b'\x01' + b'\xca' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
# No matching R0KH address found for PMK-R0 pull response
pkt = ehdr + b'\x01' + b'\xc9' + b'\x5a\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + 76 * b'\00'
if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
raise Exception("DATA_TEST_FRAME failed")
@remote_compatible
def test_rsn_ie_proto_ft_psk_sta(dev, apdev):
"""RSN element protocol testing for FT-PSK + PMF cases on STA side"""
bssid = apdev[0]['bssid']
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "1"
# This is the RSN element used normally by hostapd
params['own_ie_override'] = '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412",
pairwise="CCMP", group="CCMP")
tests = [('PMKIDCount field included',
'30160100000fac040100000fac040100000fac048c000000' + '3603a1b201'),
('Extra IE before RSNE',
'dd0400000000' + '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'),
('PMKIDCount and Group Management Cipher suite fields included',
'301a0100000fac040100000fac040100000fac048c000000000fac06' + '3603a1b201'),
('Extra octet after defined fields (future extensibility)',
'301b0100000fac040100000fac040100000fac048c000000000fac0600' + '3603a1b201'),
('No RSN Capabilities field (PMF disabled in practice)',
'30120100000fac040100000fac040100000fac04' + '3603a1b201')]
for txt, ie in tests:
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
logger.info(txt)
hapd.disable()
hapd.set('own_ie_override', ie)
hapd.enable()
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
logger.info('Invalid RSNE causing internal hostapd error')
hapd.disable()
hapd.set('own_ie_override', '30130100000fac040100000fac040100000fac048c' + '3603a1b201')
hapd.enable()
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
dev[0].select_network(id, freq=2412)
# hostapd fails to generate EAPOL-Key msg 3/4, so this connection cannot
# complete.
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected connection")
dev[0].request("DISCONNECT")
def start_ft(apdev, wpa_ptk_rekey=None):
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
hapd1 = hostapd.add_ap(apdev[1], params)
return hapd0, hapd1
def check_ptk_rekey(dev, hapd0=None, hapd1=None):
ev = dev.wait_event(["CTRL-EVENT-DISCONNECTED",
"WPA: Key negotiation completed"], timeout=5)
if ev is None:
raise Exception("No event received after roam")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Unexpected disconnection after roam")
if not hapd0 or not hapd1:
return
if dev.get_status_field('bssid') == hapd0.own_addr():
hapd = hapd0
else:
hapd = hapd1
time.sleep(0.1)
hwsim_utils.test_connectivity(dev, hapd)
def test_ap_ft_ptk_rekey(dev, apdev):
"""WPA2-PSK-FT PTK rekeying triggered by station after roam"""
hapd0, hapd1 = start_ft(apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", ptk_rekey="1")
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_ptk_rekey2(dev, apdev):
"""WPA2-PSK-FT PTK rekeying triggered by station after one roam"""
hapd0, hapd1 = start_ft(apdev)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", ptk_rekey="1",
only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_ptk_rekey_ap(dev, apdev):
"""WPA2-PSK-FT PTK rekeying triggered by AP after roam"""
hapd0, hapd1 = start_ft(apdev, wpa_ptk_rekey=2)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678")
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_ptk_rekey_ap2(dev, apdev):
"""WPA2-PSK-FT PTK rekeying triggered by AP after one roam"""
hapd0, hapd1 = start_ft(apdev, wpa_ptk_rekey=2)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
only_one_way=True)
check_ptk_rekey(dev[0], hapd0, hapd1)
def test_ap_ft_eap_ptk_rekey_ap(dev, apdev):
"""WPA2-EAP-FT PTK rekeying triggered by AP"""
generic_ap_ft_eap(dev, apdev, only_one_way=True, wpa_ptk_rekey=2)
check_ptk_rekey(dev[0])
def test_ap_ft_internal_rrb_check(dev, apdev):
"""RRB internal delivery only to WPA enabled BSS"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "FT-EAP":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
hapd1 = hostapd.add_ap(apdev[1], {"ssid": ssid})
# Connect to WPA enabled AP
dev[0].connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
# Try over_ds roaming to non-WPA-enabled AP.
# If hostapd does not check hapd->wpa_auth internally, it will crash now.
dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
def test_ap_ft_extra_ie(dev, apdev):
"""WPA2-PSK-FT AP with WPA2-PSK enabled and unexpected MDE"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "WPA-PSK FT-PSK"
hapd0 = hostapd.add_ap(apdev[0], params)
dev[1].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
dev[2].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK", proto="WPA2",
scan_freq="2412")
try:
# Add Mobility Domain element to test AP validation code.
dev[0].request("VENDOR_ELEM_ADD 13 3603a1b201")
dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK", proto="WPA2",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-ASSOC-REJECT"], timeout=10)
if ev is None:
raise Exception("No connection result")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Non-FT association accepted with MDE")
if "status_code=43" not in ev:
raise Exception("Unexpected status code: " + ev)
dev[0].request("DISCONNECT")
finally:
dev[0].request("VENDOR_ELEM_REMOVE 13 *")
def test_ap_ft_ric(dev, apdev):
"""WPA2-PSK-FT AP and RIC"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].set("ric_ies", "")
dev[0].set("ric_ies", '""')
if "FAIL" not in dev[0].request("SET ric_ies q"):
raise Exception("Invalid ric_ies value accepted")
tests = ["3900",
"3900ff04eeeeeeee",
"390400000000",
"390400000000" + "390400000000",
"390400000000" + "dd050050f20202",
"390400000000" + "dd3d0050f2020201" + 55*"00",
"390400000000" + "dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000",
"390401010000" + "dd3d0050f2020201aa3000dc050000000000000000000000000000000000000000000000000000dc050000000000000000000000000000808d5b0028230000"]
for t in tests:
dev[0].set("ric_ies", t)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
test_connectivity=False)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def ie_hex(ies, id):
return binascii.hexlify(struct.pack('BB', id, len(ies[id])) + ies[id]).decode()
def test_ap_ft_reassoc_proto(dev, apdev):
"""WPA2-PSK-FT AP Reassociation Request frame parsing"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412")
if dev[0].get_status_field('bssid') == hapd0.own_addr():
hapd1ap = hapd0
hapd2ap = hapd1
else:
hapd1ap = hapd1
hapd2ap = hapd0
dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
hapd2ap.set("ext_mgmt_frame_handling", "1")
dev[0].request("ROAM " + hapd2ap.own_addr())
while True:
req = hapd2ap.mgmt_rx()
hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
if req['subtype'] == 11:
break
while True:
req = hapd2ap.mgmt_rx()
if req['subtype'] == 2:
break
hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
# IEEE 802.11 header + fixed fields before IEs
hdr = binascii.hexlify(req['frame'][0:34]).decode()
ies = parse_ie(binascii.hexlify(req['frame'][34:]))
# First elements: SSID, Supported Rates, Extended Supported Rates
ies1 = ie_hex(ies, 0) + ie_hex(ies, 1) + ie_hex(ies, 50)
rsne = ie_hex(ies, 48)
mde = ie_hex(ies, 54)
fte = ie_hex(ies, 55)
tests = []
# RSN: Trying to use FT, but MDIE not included
tests += [rsne]
# RSN: Attempted to use unknown MDIE
tests += [rsne + "3603000000"]
# Invalid RSN pairwise cipher
tests += ["30260100000fac040100000fac030100000fac040000010029208a42cd25c85aa571567dce10dae3"]
# FT: No PMKID in RSNIE
tests += ["30160100000fac040100000fac040100000fac0400000000" + ie_hex(ies, 54)]
# FT: Invalid FTIE
tests += [rsne + mde]
# FT: RIC IE(s) in the frame, but not included in protected IE count
# FT: Failed to parse FT IEs
tests += [rsne + mde + fte + "3900"]
# FT: SNonce mismatch in FTIE
tests += [rsne + mde + "37520000" + 16*"00" + 32*"00" + 32*"00"]
# FT: ANonce mismatch in FTIE
tests += [rsne + mde + fte[0:40] + 32*"00" + fte[104:]]
# FT: No R0KH-ID subelem in FTIE
tests += [rsne + mde + "3752" + fte[4:168]]
# FT: R0KH-ID in FTIE did not match with the current R0KH-ID
tests += [rsne + mde + "3755" + fte[4:168] + "0301ff"]
# FT: No R1KH-ID subelem in FTIE
tests += [rsne + mde + "375e" + fte[4:168] + "030a" + binascii.hexlify(b"nas1.w1.fi").decode()]
# FT: Unknown R1KH-ID used in ReassocReq
tests += [rsne + mde + "3766" + fte[4:168] + "030a" + binascii.hexlify(b"nas1.w1.fi").decode() + "0106000000000000"]
# FT: PMKID in Reassoc Req did not match with the PMKR1Name derived from auth request
tests += [rsne[:-32] + 16*"00" + mde + fte]
# Invalid MIC in FTIE
tests += [rsne + mde + fte[0:8] + 16*"00" + fte[40:]]
for t in tests:
hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + ies1 + t)
def test_ap_ft_reassoc_local_fail(dev, apdev):
"""WPA2-PSK-FT AP Reassociation Request frame and local failure"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412")
if dev[0].get_status_field('bssid') == hapd0.own_addr():
hapd1ap = hapd0
hapd2ap = hapd1
else:
hapd1ap = hapd1
hapd2ap = hapd0
dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
# FT: Failed to calculate MIC
with fail_test(hapd2ap, 1, "wpa_ft_validate_reassoc"):
dev[0].request("ROAM " + hapd2ap.own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("Association reject not seen")
def test_ap_ft_reassoc_replay(dev, apdev, params):
"""WPA2-PSK-FT AP and replayed Reassociation Request frame"""
capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
if dev[0].get_status_field('bssid') == hapd0.own_addr():
hapd1ap = hapd0
hapd2ap = hapd1
else:
hapd1ap = hapd1
hapd2ap = hapd0
dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
hapd2ap.set("ext_mgmt_frame_handling", "1")
dev[0].dump_monitor()
if "OK" not in dev[0].request("ROAM " + hapd2ap.own_addr()):
raise Exception("ROAM failed")
reassocreq = None
count = 0
while count < 100:
req = hapd2ap.mgmt_rx()
count += 1
hapd2ap.dump_monitor()
hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
if req['subtype'] == 2:
reassocreq = req
ev = hapd2ap.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd2ap.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
break
hapd2ap.set("ext_mgmt_frame_handling", "0")
if reassocreq is None:
raise Exception("No Reassociation Request frame seen")
dev[0].wait_connected()
dev[0].dump_monitor()
hapd2ap.dump_monitor()
hwsim_utils.test_connectivity(dev[0], hapd2ap)
logger.info("Replay the last Reassociation Request frame")
hapd2ap.dump_monitor()
hapd2ap.set("ext_mgmt_frame_handling", "1")
hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
ev = hapd2ap.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd2ap.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
hapd2ap.set("ext_mgmt_frame_handling", "0")
try:
hwsim_utils.test_connectivity(dev[0], hapd2ap)
ok = True
except:
ok = False
ap = hapd2ap.own_addr()
sta = dev[0].own_addr()
filt = "wlan.fc.type == 2 && " + \
"wlan.da == " + sta + " && " + \
"wlan.sa == " + ap + " && " + \
"wlan.fc.protected == 1"
fields = ["wlan.ccmp.extiv"]
res = run_tshark(capfile, filt, fields)
vals = res.splitlines()
logger.info("CCMP PN: " + str(vals))
if len(vals) < 2:
raise Exception("Could not find all CCMP protected frames from capture")
if len(set(vals)) < len(vals):
raise Exception("Duplicate CCMP PN used")
if not ok:
raise Exception("The second hwsim connectivity test failed")
def test_ap_ft_psk_file(dev, apdev):
"""WPA2-PSK-FT AP with PSK from a file"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1a(ssid=ssid, passphrase=passphrase)
params['wpa_psk_file'] = 'hostapd.wpa_psk'
hapd = hostapd.add_ap(apdev[0], params)
dev[1].connect(ssid, psk="very secret",
key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
scan_freq="2412", wait_connect=False)
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect(ssid, psk="very secret", key_mgmt="FT-PSK", proto="WPA2",
ieee80211w="1", scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect(ssid, psk="secret passphrase",
key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
scan_freq="2412")
dev[2].connect(ssid, psk="another passphrase for all STAs",
key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
scan_freq="2412")
ev = dev[1].wait_event(["WPA: 4-Way Handshake failed"], timeout=10)
if ev is None:
raise Exception("Timed out while waiting for failure report")
dev[1].request("REMOVE_NETWORK all")
def test_ap_ft_eap_ap_config_change(dev, apdev):
"""WPA2-EAP-FT AP changing from 802.1X-only to FT-only"""
ssid = "test-ft"
passphrase = "12345678"
bssid = apdev[0]['bssid']
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
params['wpa_key_mgmt'] = "WPA-EAP"
params["ieee8021x"] = "1"
params["pmk_r1_push"] = "0"
params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
params["eap_server"] = "0"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="FT-EAP WPA-EAP", proto="WPA2",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.disable()
hapd.set('wpa_key_mgmt', "FT-EAP")
hapd.enable()
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_ft_eap_sha384(dev, apdev):
"""WPA2-EAP-FT with SHA384"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
conf = hapd0.request("GET_CONFIG")
if "key_mgmt=FT-EAP-SHA384" not in conf.splitlines():
logger.info("GET_CONFIG:\n" + conf)
raise Exception("GET_CONFIG did not report correct key_mgmt")
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
sha384=True)
def test_ap_ft_eap_sha384_reassoc(dev, apdev):
"""WPA2-EAP-FT with SHA384 using REASSOCIATE"""
check_suite_b_192_capa(dev)
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
sha384=True, also_non_ft=True, roam_with_reassoc=True)
def test_ap_ft_eap_sha384_over_ds(dev, apdev):
"""WPA2-EAP-FT with SHA384 over DS"""
ssid = "test-ft"
passphrase = "12345678"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
eap=True, sha384=True)
def test_ap_ft_roam_rrm(dev, apdev):
"""WPA2-PSK-FT AP and radio measurement request"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["rrm_beacon_report"] = "1"
hapd0 = hostapd.add_ap(apdev[0], params)
bssid0 = hapd0.own_addr()
addr = dev[0].own_addr()
dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
scan_freq="2412")
check_beacon_req(hapd0, addr, 1)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["rrm_beacon_report"] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = hapd1.own_addr()
dev[0].scan_for_bss(bssid1, freq=2412)
dev[0].roam(bssid1)
check_beacon_req(hapd1, addr, 2)
dev[0].scan_for_bss(bssid0, freq=2412)
dev[0].roam(bssid0)
check_beacon_req(hapd0, addr, 3)
def test_ap_ft_pmksa_caching(dev, apdev):
"""FT-EAP and PMKSA caching for initial mobility domain association"""
ssid = "test-ft"
identity = "gpsk user"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params["mobility_domain"] = "c3d4"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid)
params['wpa_key_mgmt'] = "FT-EAP"
params["ieee8021x"] = "1"
params["mobility_domain"] = "c3d4"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, None, eap=True,
eap_identity=identity, pmksa_caching=True)
def test_ap_ft_pmksa_caching_sha384(dev, apdev):
"""FT-EAP-SHA384 and PMKSA caching for initial mobility domain association"""
ssid = "test-ft"
identity = "gpsk user"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid)
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params["mobility_domain"] = "c3d4"
params = dict(list(radius.items()) + list(params.items()))
hapd = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid)
params['wpa_key_mgmt'] = "FT-EAP-SHA384"
params["ieee8021x"] = "1"
params["mobility_domain"] = "c3d4"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd, hapd1, ssid, None, eap=True,
eap_identity=identity, pmksa_caching=True, sha384=True)
def test_ap_ft_r1_key_expiration(dev, apdev):
"""WPA2-PSK-FT and PMK-R1 expiration"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['r1_max_key_lifetime'] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['r1_max_key_lifetime'] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
# This succeeds, but results in having to run another PMK-R1 pull before the
# second AP can complete FT protocol.
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, wait_before_roam=4)
def test_ap_ft_r0_key_expiration(dev, apdev):
"""WPA2-PSK-FT and PMK-R0 expiration"""
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params.pop('r0_key_lifetime', None)
params['ft_r0_key_lifetime'] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params.pop('r0_key_lifetime', None)
params['ft_r0_key_lifetime'] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
bssid2 = run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
return_after_initial=True)
time.sleep(4)
dev[0].scan_for_bss(bssid2, freq="2412")
if "OK" not in dev[0].request("ROAM " + bssid2):
raise Exception("ROAM failed")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-AUTH-REJECT",
"CTRL-EVENT-ASSOC-REJECT"], timeout=5)
dev[0].request("DISCONNECT")
if ev is None or "CTRL-EVENT-AUTH-REJECT" not in ev:
raise Exception("FT protocol failure not reported")
if "status_code=53" not in ev:
raise Exception("Unexpected status in FT protocol failure: " + ev)
# Generate a new PMK-R0
dev[0].dump_monitor()
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_ap_ft_no_full_ap_client_state(dev, apdev):
"""WPA2-PSK-FT AP with full_ap_client_state=0"""
run_ap_ft_skip_prune_assoc(dev, apdev, False, False)
def test_ap_ft_skip_prune_assoc(dev, apdev):
"""WPA2-PSK-FT AP with skip_prune_assoc"""
run_ap_ft_skip_prune_assoc(dev, apdev, True, True)
def test_ap_ft_skip_prune_assoc2(dev, apdev):
"""WPA2-PSK-FT AP with skip_prune_assoc (disable full_ap_client_state)"""
run_ap_ft_skip_prune_assoc(dev, apdev, True, False, test_connectivity=False)
def test_ap_ft_skip_prune_assoc_pmf(dev, apdev):
"""WPA2-PSK-FT/PMF AP with skip_prune_assoc"""
run_ap_ft_skip_prune_assoc(dev, apdev, True, True, pmf=True)
def test_ap_ft_skip_prune_assoc_pmf_over_ds(dev, apdev):
"""WPA2-PSK-FT/PMF AP with skip_prune_assoc (over DS)"""
run_ap_ft_skip_prune_assoc(dev, apdev, True, True, pmf=True, over_ds=True)
def run_ap_ft_skip_prune_assoc(dev, apdev, skip_prune_assoc,
full_ap_client_state, test_connectivity=True,
pmf=False, over_ds=False):
ssid = "test-ft"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
if skip_prune_assoc:
params['skip_prune_assoc'] = '1'
if not full_ap_client_state:
params['driver_params'] = "full_ap_client_state=0"
if pmf:
params["ieee80211w"] = "2"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
if skip_prune_assoc:
params['skip_prune_assoc'] = '1'
if not full_ap_client_state:
params['driver_params'] = "full_ap_client_state=0"
if pmf:
params["ieee80211w"] = "2"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
ieee80211w="2" if pmf else "0",
over_ds=over_ds, test_connectivity=test_connectivity)
def test_ap_ft_sae_skip_prune_assoc(dev, apdev):
"""WPA2-PSK-FT-SAE AP with skip_prune_assoc"""
hapd0, hapd1 = start_ft_sae(dev[0], apdev, skip_prune_assoc=True)
run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
diff --git a/tests/hwsim/test_ap_ht.py b/tests/hwsim/test_ap_ht.py
index 99adb12a36a4..510fe0836fc5 100644
--- a/tests/hwsim/test_ap_ht.py
+++ b/tests/hwsim/test_ap_ht.py
@@ -1,1609 +1,1644 @@
# Test cases for HT operations with hostapd
# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import time
import logging
logger = logging.getLogger()
import struct
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
import hwsim_utils
def test_ap_ht40_scan(dev, apdev):
"""HT40 co-ex scan"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("Unexpected secondary channel")
status = hapd.get_status()
logger.info("hostapd STATUS: " + str(status))
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
sta = hapd.get_sta(dev[0].own_addr())
logger.info("hostapd STA: " + str(sta))
res = dev[0].request("SIGNAL_POLL")
logger.info("STA SIGNAL_POLL:\n" + res.strip())
sig = res.splitlines()
if "WIDTH=40 MHz" not in sig:
raise Exception("Not a 40 MHz connection")
if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
raise Exception("No Supported Operating Classes information for STA")
opclass = int(sta['supp_op_classes'][0:2], 16)
if opclass != 84:
raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
def test_ap_ht_wifi_generation(dev, apdev):
"""HT and wifi_generation"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht",
"channel": "6"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("test-ht", key_mgmt="NONE", scan_freq="2437")
status = dev[0].get_status()
if 'wifi_generation' not in status:
# For now, assume this is because of missing kernel support
raise HwsimSkip("Association Request IE reporting not supported")
#raise Exception("Missing wifi_generation information")
if status['wifi_generation'] != "4":
raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.connect("test-ht", key_mgmt="NONE", scan_freq="2437")
status = wpas.get_status()
if 'wifi_generation' not in status:
# For now, assume this is because of missing kernel support
raise HwsimSkip("Association Request IE reporting not supported")
#raise Exception("Missing wifi_generation information (connect)")
if status['wifi_generation'] != "4":
raise Exception("Unexpected wifi_generation value (connect): " + status['wifi_generation'])
@remote_compatible
def test_ap_ht40_scan_conflict(dev, apdev):
"""HT40 co-ex scan conflict"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "6",
"ht_capab": "[HT40+]"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
@remote_compatible
def test_ap_ht40_scan_conflict2(dev, apdev):
"""HT40 co-ex scan conflict (HT40-)"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "11",
"ht_capab": "[HT40-]"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "1",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2412":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "1":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_scan_not_affected(dev, apdev):
"""HT40 co-ex scan and other BSS not affected"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht20",
"channel": "11"}
hostapd.add_ap(apdev[1], params)
hostapd.cmd_execute(apdev[0], ['ifconfig', apdev[0]['ifname'], 'up'])
hostapd.cmd_execute(apdev[0], ['iw', apdev[0]['ifname'], 'scan', 'trigger',
'freq', '2462'])
time.sleep(0.5)
hostapd.cmd_execute(apdev[0], ['iw', apdev[0]['ifname'], 'scan', 'dump'])
time.sleep(0.1)
hostapd.cmd_execute(apdev[0], ['ifconfig', apdev[0]['ifname'], 'down'])
params = {"ssid": "test-ht40",
"channel": "1",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2412":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "1":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
@remote_compatible
def test_ap_ht40_scan_legacy_conflict(dev, apdev):
"""HT40 co-ex scan conflict with legacy 20 MHz AP"""
clear_scan_cache(apdev[0])
params = {"ssid": "legacy-20",
"channel": "7", "ieee80211n": "0"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
@remote_compatible
def test_ap_ht40_scan_ht20_conflict(dev, apdev):
"""HT40 co-ex scan conflict with HT 20 MHz AP"""
clear_scan_cache(apdev[0])
params = {"ssid": "ht-20",
"channel": "7", "ieee80211n": "1"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_scan_intolerant(dev, apdev):
"""HT40 co-ex scan finding an AP advertising 40 MHz intolerant"""
clear_scan_cache(apdev[0])
params = {"ssid": "another-bss",
"channel": "1",
"ht_capab": "[40-INTOLERANT]"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "1",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2412":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "1":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_scan_match(dev, apdev):
"""HT40 co-ex scan matching configuration"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_5ghz_match(dev, apdev):
"""HT40 co-ex scan on 5 GHz with matching pri/sec channel"""
clear_scan_cache(apdev[0])
try:
hapd = None
hapd2 = None
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "36",
"country_code": "US",
"ht_capab": "[HT40+]"}
hapd2 = hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "5180":
raise Exception("Unexpected frequency")
pri = hapd.get_status_field("channel")
if pri != "36":
raise Exception("Unexpected primary channel")
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
def test_ap_ht40_5ghz_switch(dev, apdev):
"""HT40 co-ex scan on 5 GHz switching pri/sec channel"""
clear_scan_cache(apdev[0])
try:
hapd = None
hapd2 = None
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "36",
"country_code": "US",
"ht_capab": "[HT40+]"}
hapd2 = hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "40",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "5180":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "36":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
set_world_reg(apdev[0], apdev[1], dev[0])
def test_ap_ht40_5ghz_switch2(dev, apdev):
"""HT40 co-ex scan on 5 GHz switching pri/sec channel (2)"""
clear_scan_cache(apdev[0])
try:
hapd = None
hapd2 = None
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "36",
"country_code": "US",
"ht_capab": "[HT40+]"}
hapd2 = hostapd.add_ap(apdev[1], params)
id = dev[0].add_network()
dev[0].set_network(id, "mode", "2")
dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
dev[0].set_network(id, "key_mgmt", "NONE")
dev[0].set_network(id, "frequency", "5200")
dev[0].set_network(id, "scan_freq", "5200")
dev[0].select_network(id)
time.sleep(1)
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "40",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "5180":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "36":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
def test_obss_scan(dev, apdev):
"""Overlapping BSS scan request"""
clear_scan_cache(apdev[0])
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40-]",
"obss_interval": "10"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "another-bss",
"channel": "9",
"ieee80211n": "0"}
hostapd.add_ap(apdev[1], params)
run_obss_scan(hapd, dev)
def test_obss_scan_ht40_plus(dev, apdev):
"""Overlapping BSS scan request (HT40+)"""
clear_scan_cache(apdev[0])
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40+]",
"obss_interval": "10"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "another-bss",
"channel": "9",
"ieee80211n": "0"}
hostapd.add_ap(apdev[1], params)
run_obss_scan(hapd, dev, ht40plus=True)
def run_obss_scan(hapd, dev, ht40plus=False):
dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
res = dev[0].request("SIGNAL_POLL")
logger.info("SIGNAL_POLL:\n" + res)
sig = res.splitlines()
if "FREQUENCY=2437" not in sig:
raise Exception("Unexpected frequency")
if "WIDTH=40 MHz" not in sig:
raise Exception("Not a 40 MHz connection")
if ht40plus and "CENTER_FRQ1=2447" not in sig:
raise Exception("Not HT40+")
if not ht40plus and "CENTER_FRQ1=2427" not in sig:
raise Exception("Not HT40-")
hapd.set("ext_mgmt_frame_handling", "1")
logger.info("Waiting for OBSS scan to occur")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
if ev is None:
raise Exception("Timed out while waiting for OBSS scan to start")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("Timed out while waiting for OBSS scan results")
received = False
for i in range(0, 4):
frame = hapd.mgmt_rx(timeout=5)
if frame is None:
raise Exception("MGMT RX wait timed out")
if frame['subtype'] != 13:
continue
payload = frame['payload']
if len(payload) < 3:
continue
(category, action, ie) = struct.unpack('BBB', payload[0:3])
if category != 4:
continue
if action != 0:
continue
if ie == 72:
logger.info("20/40 BSS Coexistence report received")
received = True
break
if not received:
raise Exception("20/40 BSS Coexistence report not seen")
def test_obss_scan_40_intolerant(dev, apdev):
"""Overlapping BSS scan request with 40 MHz intolerant AP"""
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40-]",
"obss_interval": "10"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "another-bss",
"channel": "7",
"ht_capab": "[40-INTOLERANT]"}
hostapd.add_ap(apdev[1], params)
dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
hapd.set("ext_mgmt_frame_handling", "1")
logger.info("Waiting for OBSS scan to occur")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
if ev is None:
raise Exception("Timed out while waiting for OBSS scan to start")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("Timed out while waiting for OBSS scan results")
received = False
for i in range(0, 4):
frame = hapd.mgmt_rx(timeout=5)
if frame is None:
raise Exception("MGMT RX wait timed out")
if frame['subtype'] != 13:
continue
payload = frame['payload']
if len(payload) < 3:
continue
(category, action, ie) = struct.unpack('BBB', payload[0:3])
if category != 4:
continue
if action != 0:
continue
if ie == 72:
logger.info("20/40 BSS Coexistence report received")
received = True
break
if not received:
raise Exception("20/40 BSS Coexistence report not seen")
def test_obss_coex_report_handling(dev, apdev):
"""Overlapping BSS scan report handling with obss_interval=0"""
clear_scan_cache(apdev[0])
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("AP is not using 40 MHz channel")
# 20/40 MHz co-ex report tests: number of invalid reports and a valid report
# that forces 20 MHz channel.
tests = ['0400', '040048', '04004801', '0400480000', '0400490100',
'040048ff0000', '04004801ff49ff00', '04004801004900',
'0400480100490101', '0400480100490201ff',
'040048010449020005']
for msg in tests:
req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
if "OK" not in dev[0].request(req):
raise Exception("Could not send management frame")
time.sleep(0.5)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("AP did not move to 20 MHz channel")
def test_obss_coex_report_handling1(dev, apdev):
"""Overlapping BSS scan report handling with obss_interval=1"""
clear_scan_cache(apdev[0])
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40+]",
"obss_interval": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("AP is not using 40 MHz channel")
# 20/40 MHz co-ex report forcing 20 MHz channel
msg = '040048010449020005'
req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
if "OK" not in dev[0].request(req):
raise Exception("Could not send management frame")
time.sleep(0.5)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("AP did not move to 20 MHz channel")
# No 20/40 MHz co-ex reports forcing 20 MHz channel during next interval
for i in range(20):
sec = hapd.get_status_field("secondary_channel")
if sec == "1":
break
time.sleep(0.5)
if sec != "1":
raise Exception("AP did not return to 40 MHz channel")
def test_obss_coex_report_handling2(dev, apdev):
"""Overlapping BSS scan report handling with obss_interval=1 and no overlap"""
clear_scan_cache(apdev[0])
params = {"ssid": "obss-scan",
"channel": "6",
"ht_capab": "[HT40+]",
"obss_interval": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("AP is not using 40 MHz channel")
# 20/40 MHz co-ex report that does not force a move to 20 MHz channel
# (out of affected range and matching primary channel cases)
msg = '0400' + '480100' + '49020001' + '49020006'
req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
if "OK" not in dev[0].request(req):
raise Exception("Could not send management frame")
time.sleep(0.5)
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected move to 20 MHz channel")
# 20/40 MHz co-ex report forcing 20 MHz channel
# (out of affected range and in affected range but not matching primary)
msg = '0400' + '480100' + '4903000105'
req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
if "OK" not in dev[0].request(req):
raise Exception("Could not send management frame")
time.sleep(0.5)
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("AP did not move to 20 MHz channel")
def test_olbc(dev, apdev):
"""OLBC detection"""
params = {"ssid": "test-olbc",
"channel": "6",
"ht_capab": "[HT40-]",
"ap_table_expiration_time": "2"}
hapd = hostapd.add_ap(apdev[0], params)
status = hapd.get_status()
if status['olbc'] != '0' or status['olbc_ht'] != '0':
raise Exception("Unexpected OLBC information")
params = {"ssid": "olbc-ap",
"hw_mode": "b",
"channel": "6",
"wmm_enabled": "0"}
hostapd.add_ap(apdev[1], params)
time.sleep(0.5)
status = hapd.get_status()
if status['olbc'] != '1' or status['olbc_ht'] != '1':
raise Exception("Missing OLBC information")
hostapd.remove_bss(apdev[1])
logger.info("Waiting for OLBC state to time out")
cleared = False
for i in range(0, 15):
time.sleep(1)
status = hapd.get_status()
if status['olbc'] == '0' and status['olbc_ht'] == '0':
cleared = True
break
if not cleared:
raise Exception("OLBC state did nto time out")
def test_olbc_table_limit(dev, apdev):
"""OLBC AP table size limit"""
ifname1 = apdev[0]['ifname']
ifname2 = apdev[0]['ifname'] + '-2'
ifname3 = apdev[0]['ifname'] + '-3'
hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
params = {"ssid": "test-olbc",
"channel": "1",
"ap_table_max_size": "2"}
hapd = hostapd.add_ap(apdev[1], params)
time.sleep(0.3)
with alloc_fail(hapd, 1, "ap_list_process_beacon"):
time.sleep(0.3)
hapd.set("ap_table_max_size", "1")
time.sleep(0.3)
hapd.set("ap_table_max_size", "0")
time.sleep(0.3)
def test_olbc_5ghz(dev, apdev):
"""OLBC detection on 5 GHz"""
try:
hapd = None
hapd2 = None
params = {"ssid": "test-olbc",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params)
status = hapd.get_status()
if status['olbc'] != '0' or status['olbc_ht'] != '0':
raise Exception("Unexpected OLBC information")
params = {"ssid": "olbc-ap",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ieee80211n": "0",
"wmm_enabled": "0"}
hapd2 = hostapd.add_ap(apdev[1], params)
found = False
for i in range(20):
time.sleep(0.1)
status = hapd.get_status()
logger.debug('olbc_ht: ' + status['olbc_ht'])
if status['olbc_ht'] == '1':
found = True
break
if not found:
raise Exception("Missing OLBC information")
finally:
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
set_world_reg(apdev[0], apdev[1], None)
def test_ap_require_ht(dev, apdev):
"""Require HT"""
params = {"ssid": "require-ht",
"require_ht": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
disable_ht="1", wait_connect=False)
dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
dev[1].request("DISCONNECT")
if ev is None:
raise Exception("Association rejection timed out")
if "status_code=27" not in ev:
raise Exception("Unexpected rejection status code")
dev[2].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
ht_mcs="0x01 00 00 00 00 00 00 00 00 00",
disable_max_amsdu="1", ampdu_factor="2",
ampdu_density="1", disable_ht40="1", disable_sgi="1",
disable_ldpc="1", rx_stbc="2", tx_stbc="1")
sta = hapd.get_sta(dev[0].own_addr())
if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
raise Exception("No Supported Operating Classes information for STA")
opclass = int(sta['supp_op_classes'][0:2], 16)
if opclass != 81:
raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
def test_ap_ht_stbc(dev, apdev):
"""HT STBC overrides"""
params = {"ssid": "ht"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="2412")
dev[1].connect("ht", key_mgmt="NONE", scan_freq="2412",
rx_stbc="0", tx_stbc="0")
dev[2].connect("ht", key_mgmt="NONE", scan_freq="2412",
rx_stbc="1", tx_stbc="1")
@remote_compatible
def test_ap_require_ht_limited_rates(dev, apdev):
"""Require HT with limited supported rates"""
params = {"ssid": "require-ht",
"supported_rates": "60 120 240 360 480 540",
"require_ht": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
disable_ht="1", wait_connect=False)
dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
dev[1].request("DISCONNECT")
if ev is None:
raise Exception("Association rejection timed out")
if "status_code=27" not in ev:
raise Exception("Unexpected rejection status code")
@remote_compatible
def test_ap_ht_capab_not_supported(dev, apdev):
"""HT configuration with driver not supporting all ht_capab entries"""
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-][LDPC][SMPS-STATIC][SMPS-DYNAMIC][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][RX-STBC12][RX-STBC123][DELAYED-BA][MAX-AMSDU-7935][DSSS_CCK-40][LSIG-TXOP-PROT]"}
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
def test_ap_ht_40mhz_intolerant_sta(dev, apdev):
"""Associated STA indicating 40 MHz intolerant"""
clear_scan_cache(apdev[0])
params = {"ssid": "intolerant",
"channel": "6",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params)
if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
raise Exception("Unexpected num_sta_ht40_intolerant value")
if hapd.get_status_field("secondary_channel") != "-1":
raise Exception("Unexpected secondary_channel")
dev[0].connect("intolerant", key_mgmt="NONE", scan_freq="2437")
if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
raise Exception("Unexpected num_sta_ht40_intolerant value")
if hapd.get_status_field("secondary_channel") != "-1":
raise Exception("Unexpected secondary_channel")
dev[2].connect("intolerant", key_mgmt="NONE", scan_freq="2437",
ht40_intolerant="1")
time.sleep(1)
if hapd.get_status_field("num_sta_ht40_intolerant") != "1":
raise Exception("Unexpected num_sta_ht40_intolerant value (expected 1)")
if hapd.get_status_field("secondary_channel") != "0":
raise Exception("Unexpected secondary_channel (did not disable 40 MHz)")
dev[2].request("DISCONNECT")
time.sleep(1)
if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
raise Exception("Unexpected num_sta_ht40_intolerant value (expected 0)")
if hapd.get_status_field("secondary_channel") != "-1":
raise Exception("Unexpected secondary_channel (did not re-enable 40 MHz)")
def test_ap_ht_40mhz_intolerant_sta_deinit(dev, apdev):
"""Associated STA indicating 40 MHz intolerant and hostapd deinit"""
clear_scan_cache(apdev[0])
params = {"ssid": "intolerant",
"channel": "6",
"ht_capab": "[HT40-]",
"obss_interval": "0"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("intolerant", key_mgmt="NONE", scan_freq="2437",
ht40_intolerant="1")
time.sleep(1)
if hapd.get_status_field("num_sta_ht40_intolerant") != "1":
raise Exception("Unexpected num_sta_ht40_intolerant value (expected 1)")
hglobal = hostapd.HostapdGlobal()
hglobal.remove(apdev[0]['ifname'])
dev[0].request("DISCONNECT")
def test_ap_ht_40mhz_intolerant_ap(dev, apdev):
"""Associated STA reports 40 MHz intolerant AP after association"""
clear_scan_cache(apdev[0])
params = {"ssid": "ht",
"channel": "6",
"ht_capab": "[HT40-]",
"obss_interval": "3"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="2437")
if hapd.get_status_field("secondary_channel") != "-1":
raise Exception("Unexpected secondary channel information")
logger.info("Start 40 MHz intolerant AP")
params = {"ssid": "intolerant",
"channel": "5",
"ht_capab": "[40-INTOLERANT]"}
hapd2 = hostapd.add_ap(apdev[1], params)
logger.info("Waiting for co-ex report from STA")
ok = False
for i in range(4):
ev = dev[0].wait_event(['CTRL-EVENT-SCAN-RESULTS'], timeout=20)
if ev is None:
raise Exception("No OBSS scan seen")
time.sleep(1)
if hapd.get_status_field("secondary_channel") == "0":
logger.info("AP moved to 20 MHz channel")
ok = True
break
if not ok:
raise Exception("AP did not move to 20 MHz channel")
if "OK" not in hapd2.request("DISABLE"):
raise Exception("Failed to disable 40 MHz intolerant AP")
# make sure the intolerant AP disappears from scan results more quickly
dev[0].scan(type="ONLY", freq="2432", only_new=True)
dev[0].scan(type="ONLY", freq="2432", only_new=True)
dev[0].dump_monitor()
logger.info("Waiting for AP to move back to 40 MHz channel")
ok = False
for i in range(0, 30):
time.sleep(1)
if hapd.get_status_field("secondary_channel") == "-1":
logger.info("AP moved to 40 MHz channel")
ok = True
break
if not ok:
raise Exception("AP did not move to 40 MHz channel")
def test_ap_ht40_csa(dev, apdev):
"""HT with 40 MHz channel width and CSA"""
csa_supported(dev[0])
try:
hapd = None
params = {"ssid": "ht",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5200 ht sec_channel_offset=-1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5200" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
set_world_reg(apdev[0], None, dev[0])
dev[0].flush_scan_cache()
def test_ap_ht40_csa2(dev, apdev):
"""HT with 40 MHz channel width and CSA"""
csa_supported(dev[0])
try:
hapd = None
params = {"ssid": "ht",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5220 ht sec_channel_offset=1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5220" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
set_world_reg(apdev[0], None, dev[0])
dev[0].flush_scan_cache()
def test_ap_ht40_csa3(dev, apdev):
"""HT with 40 MHz channel width and CSA"""
csa_supported(dev[0])
try:
hapd = None
params = {"ssid": "ht",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5240 ht sec_channel_offset=-1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5240" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
set_world_reg(apdev[0], None, dev[0])
dev[0].flush_scan_cache()
def test_ap_ht_20_to_40_csa(dev, apdev):
"""HT with 20 MHz channel width doing CSA to 40 MHz"""
csa_supported(dev[0])
params = {"ssid": "ht",
"channel": "1",
"ieee80211n": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ht", key_mgmt="NONE", scan_freq="2412")
hapd.wait_sta()
res = dev[0].request("SIGNAL_POLL")
logger.info("SIGNAL_POLL:\n" + res)
sig = res.splitlines()
if 'WIDTH=20 MHz' not in sig:
raise Exception("20 MHz channel bandwidth not used on the original channel")
hapd.request("CHAN_SWITCH 5 2462 ht sec_channel_offset=-1 bandwidth=40")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=2462" not in ev:
raise Exception("Unexpected channel in CSA finished event")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected STA disconnection during CSA")
res = dev[0].request("SIGNAL_POLL")
logger.info("SIGNAL_POLL:\n" + res)
sig = res.splitlines()
if 'WIDTH=40 MHz' not in sig:
raise Exception("40 MHz channel bandwidth not used on the new channel")
@remote_compatible
def test_prefer_ht20(dev, apdev):
"""Preference on HT20 over no-HT"""
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "0"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "1"}
hapd2 = hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
if dev[0].get_status_field('bssid') != bssid2:
raise Exception("Unexpected BSS selected")
est = dev[0].get_bss(bssid)['est_throughput']
if est != "54000":
raise Exception("Unexpected BSS0 est_throughput: " + est)
est = dev[0].get_bss(bssid2)['est_throughput']
if est != "65000":
raise Exception("Unexpected BSS1 est_throughput: " + est)
def test_prefer_ht40(dev, apdev):
"""Preference on HT40 over HT20"""
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "1",
"ht_capab": "[HT40+]"}
hapd2 = hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
if dev[0].get_status_field('bssid') != bssid2:
raise Exception("Unexpected BSS selected")
est = dev[0].get_bss(bssid)['est_throughput']
if est != "65000":
raise Exception("Unexpected BSS0 est_throughput: " + est)
est = dev[0].get_bss(bssid2)['est_throughput']
if est != "135000":
raise Exception("Unexpected BSS1 est_throughput: " + est)
@remote_compatible
def test_prefer_ht20_during_roam(dev, apdev):
"""Preference on HT20 over no-HT in roaming consideration"""
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "0"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
params = {"ssid": "test",
"channel": "1",
"ieee80211n": "1"}
hapd2 = hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].scan(freq=2412)
dev[0].wait_connected()
if dev[0].get_status_field('bssid') != bssid2:
raise Exception("Unexpected BSS selected")
@remote_compatible
def test_ap_ht40_5ghz_invalid_pair(dev, apdev):
"""HT40 on 5 GHz with invalid channel pair"""
clear_scan_cache(apdev[0])
try:
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "40",
"country_code": "US",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup failure timed out")
if "AP-ENABLED" in ev:
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Invalid 40 MHz channel accepted")
finally:
clear_regdom(hapd, dev)
@remote_compatible
def test_ap_ht40_5ghz_disabled_sec(dev, apdev):
"""HT40 on 5 GHz with disabled secondary channel"""
clear_scan_cache(apdev[0])
try:
params = {"ssid": "test-ht40",
"hw_mode": "a",
"channel": "48",
"country_code": "US",
"ht_capab": "[HT40+]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup failure timed out")
if "AP-ENABLED" in ev:
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Invalid 40 MHz channel accepted")
finally:
clear_regdom(hapd, dev)
def test_ap_ht40_scan_broken_ap(dev, apdev):
"""HT40 co-ex scan and broken legacy/HT AP"""
clear_scan_cache(apdev[0])
# Broken AP: Include HT Capabilities element but not HT Operation element
params = {"ssid": "legacy-20",
"channel": "7", "ieee80211n": "0",
"wmm_enabled": "1",
"vendor_elements": "2d1a0e001bffff000000000000000000000100000000000000000000"}
hapd2 = hostapd.add_ap(apdev[1], params)
params = {"ssid": "test-ht40",
"channel": "5",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
time.sleep(0.1)
state = hapd.get_status_field("state")
if state != "HT_SCAN":
raise Exception("Unexpected interface state - expected HT_SCAN")
ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state - expected ENABLED")
freq = hapd.get_status_field("freq")
if freq != "2432":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "5":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
dev[1].connect("legacy-20", key_mgmt="NONE", scan_freq="2442")
hwsim_utils.test_connectivity(dev[0], hapd)
hwsim_utils.test_connectivity(dev[1], hapd2)
def run_op_class(dev, apdev, hw_mode, channel, country, ht_capab, sec_chan,
- freq, opclass):
+ freq, opclass, use_op_class=False):
clear_scan_cache(apdev[0])
try:
params = {"ssid": "test-ht40",
"hw_mode": hw_mode,
"channel": channel,
"ht_capab": ht_capab}
+ if use_op_class:
+ params['op_class'] = str(opclass)
if country:
params['country_code'] = country
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
if not ev:
raise Exception("AP setup failure timed out")
if "AP-DISABLED" in ev:
raise HwsimSkip("Channel not supported")
sec = hapd.get_status_field("secondary_channel")
if sec != sec_chan:
raise Exception("Unexpected secondary_channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
bss = dev[0].get_bss(hapd.own_addr())
ie = parse_ie(bss['ie'])
if 59 not in ie:
raise Exception("Missing Supported Operating Classes element")
rx_opclass, = struct.unpack('B', ie[59][0:1])
if rx_opclass != opclass:
raise Exception("Unexpected operating class: %d" % rx_opclass)
hapd.disable()
+ hapd.dump_monitor()
dev[0].request("REMOVE_NETWORK all")
dev[0].request("ABORT_SCAN")
dev[0].wait_disconnected()
dev[0].dump_monitor()
finally:
set_world_reg(apdev[0], None, dev[0])
time.sleep(0.1)
def test_ap_ht_op_class_81(dev, apdev):
"""HT20 on operationg class 81"""
- run_op_class(dev, apdev, "g", "1", None, "", "0", "2412", 81)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "1", None, "", "0", "2412", 81,
+ use_op_class=o)
def test_ap_ht_op_class_83(dev, apdev):
"""HT40 on operationg class 83"""
- run_op_class(dev, apdev, "g", "1", None, "[HT40+]", "1", "2412", 83)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "1", None, "[HT40+]", "1", "2412", 83,
+ use_op_class=o)
def test_ap_ht_op_class_84(dev, apdev):
"""HT40 on operationg class 84"""
- run_op_class(dev, apdev, "g", "11", None, "[HT40-]", "-1", "2462", 84)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "11", None, "[HT40-]", "-1", "2462", 84,
+ use_op_class=o)
def test_ap_ht_op_class_115(dev, apdev):
"""HT20 on operationg class 115"""
- run_op_class(dev, apdev, "a", "36", "FI", "", "0", "5180", 115)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "36", "FI", "", "0", "5180", 115,
+ use_op_class=o)
def test_ap_ht_op_class_116(dev, apdev):
"""HT40 on operationg class 116"""
- run_op_class(dev, apdev, "a", "36", "FI", "[HT40+]", "1", "5180", 116)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "36", "FI", "[HT40+]", "1", "5180", 116,
+ use_op_class=o)
def test_ap_ht_op_class_117(dev, apdev):
"""HT40 on operationg class 117"""
- run_op_class(dev, apdev, "a", "40", "FI", "[HT40-]", "-1", "5200", 117)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "40", "FI", "[HT40-]", "-1", "5200", 117,
+ use_op_class=o)
def test_ap_ht_op_class_118(dev, apdev):
"""HT20 on operationg class 118"""
- run_op_class(dev, apdev, "a", "60", "RS", "", "0", "5300", 118)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "60", "PA", "", "0", "5300", 118,
+ use_op_class=o)
def test_ap_ht_op_class_119(dev, apdev):
"""HT40 on operationg class 119"""
- run_op_class(dev, apdev, "a", "60", "RS", "[HT40+]", "1", "5300", 119)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "60", "PA", "[HT40+]", "1", "5300", 119,
+ use_op_class=o)
def test_ap_ht_op_class_120(dev, apdev):
"""HT40 on operationg class 120"""
- run_op_class(dev, apdev, "a", "64", "RS", "[HT40-]", "-1", "5320", 120)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "64", "PA", "[HT40-]", "-1", "5320", 120,
+ use_op_class=o)
def test_ap_ht_op_class_121(dev, apdev):
"""HT20 on operationg class 121"""
- run_op_class(dev, apdev, "a", "100", "ZA", "", "0", "5500", 121)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "100", "ZA", "", "0", "5500", 121,
+ use_op_class=o)
def test_ap_ht_op_class_122(dev, apdev):
"""HT40 on operationg class 122"""
- run_op_class(dev, apdev, "a", "100", "ZA", "[HT40+]", "1", "5500", 122)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "100", "ZA", "[HT40+]", "1", "5500", 122,
+ use_op_class=o)
def test_ap_ht_op_class_123(dev, apdev):
"""HT40 on operationg class 123"""
- run_op_class(dev, apdev, "a", "104", "ZA", "[HT40-]", "-1", "5520", 123)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "104", "ZA", "[HT40-]", "-1", "5520", 123,
+ use_op_class=o)
def test_ap_ht_op_class_124(dev, apdev):
"""HT20 on operationg class 124"""
- run_op_class(dev, apdev, "a", "149", "US", "", "0", "5745", 124)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "149", "US", "", "0", "5745", 124,
+ use_op_class=o)
def test_ap_ht_op_class_125(dev, apdev):
"""HT20 on operationg class 125"""
- run_op_class(dev, apdev, "a", "169", "NL", "", "0", "5845", 125)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "169", "NL", "", "0", "5845", 125,
+ use_op_class=o)
def test_ap_ht_op_class_126(dev, apdev):
"""HT40 on operationg class 126"""
- run_op_class(dev, apdev, "a", "149", "US", "[HT40+]", "1", "5745", 126)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "149", "US", "[HT40+]", "1", "5745", 126,
+ use_op_class=o)
def test_ap_ht_op_class_127(dev, apdev):
"""HT40 on operationg class 127"""
- run_op_class(dev, apdev, "a", "153", "US", "[HT40-]", "-1", "5765", 127)
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "153", "US", "[HT40-]", "-1", "5765", 127,
+ use_op_class=o)
def test_ap_ht40_plus_minus1(dev, apdev):
"""HT40 with both plus and minus allowed (1)"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "11",
"ht_capab": "[HT40+][HT40-]"}
hapd = hostapd.add_ap(apdev[0], params)
freq = hapd.get_status_field("freq")
if freq != "2462":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "11":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_plus_minus2(dev, apdev):
"""HT40 with both plus and minus allowed (2)"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "1",
"ht_capab": "[HT40+][HT40-]"}
hapd = hostapd.add_ap(apdev[0], params)
freq = hapd.get_status_field("freq")
if freq != "2412":
raise Exception("Unexpected frequency: " + freq)
pri = hapd.get_status_field("channel")
if pri != "1":
raise Exception("Unexpected primary channel: " + pri)
sec = hapd.get_status_field("secondary_channel")
if sec != "1":
raise Exception("Unexpected secondary channel: " + sec)
dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
def test_ap_ht40_disable(dev, apdev):
"""HT40 disabling"""
clear_scan_cache(apdev[0])
params = {"ssid": "test-ht40",
"channel": "6",
"ht_capab": "[HT40-]"}
hapd = hostapd.add_ap(apdev[0], params)
sec = hapd.get_status_field("secondary_channel")
if sec != "-1":
raise Exception("Unexpected secondary channel: " + sec)
id = dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq="2437")
sig = dev[0].request("SIGNAL_POLL").splitlines()
logger.info("SIGNAL_POLL: " + str(sig))
if "WIDTH=40 MHz" not in sig:
raise Exception("Station did not report 40 MHz bandwidth")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
hapd.set("ht_capab", "")
hapd.enable()
sec = hapd.get_status_field("secondary_channel")
if sec != "0":
raise Exception("Unexpected secondary channel(2): " + sec)
dev[0].flush_scan_cache()
dev[0].select_network(id, freq=2437)
dev[0].wait_connected()
sig = dev[0].request("SIGNAL_POLL").splitlines()
logger.info("SIGNAL_POLL: " + str(sig))
if "WIDTH=20 MHz" not in sig:
raise Exception("Station did not report 20 MHz bandwidth")
def test_ap_ht_wmm_etsi(dev, apdev):
"""HT and WMM contents in ETSI"""
run_ap_ht_wmm(dev, apdev, "FI")
def test_ap_ht_wmm_fcc(dev, apdev):
"""HT and WMM contents in FCC"""
run_ap_ht_wmm(dev, apdev, "US")
def run_ap_ht_wmm(dev, apdev, country):
clear_scan_cache(apdev[0])
try:
hapd = None
params = {"ssid": "test",
"hw_mode": "a",
"channel": "36",
"country_code": country}
hapd = hostapd.add_ap(apdev[0], params)
freq = hapd.get_status_field("freq")
bssid = hapd.own_addr()
dev[0].connect("test", key_mgmt="NONE", scan_freq=freq)
bss = dev[0].get_bss(bssid)
ie = parse_ie(bss['ie'])
if 221 not in ie:
raise Exception("Could not find WMM IE")
wmm = ie[221]
if len(wmm) != 24:
raise Exception("Unexpected WMM IE length")
id, subtype, version, info, reserved = struct.unpack('>LBBBB', wmm[0:8])
if id != 0x0050f202 or subtype != 1 or version != 1:
raise Exception("Not a WMM IE")
ac = []
for i in range(4):
ac.append(struct.unpack('<BBH', wmm[8 + i * 4: 12 + i * 4]))
logger.info("WMM AC info: " + str(ac))
aifsn = (ac[0][0] & 0x0f, ac[1][0] & 0x0f,
ac[2][0] & 0x0f, ac[3][0] & 0x0f)
logger.info("AIFSN: " + str(aifsn))
if aifsn != (3, 7, 2, 2):
raise Exception("Unexpected AIFSN value: " + str(aifsn))
ecw_min = (ac[0][1] & 0x0f, ac[1][1] & 0x0f,
ac[2][1] & 0x0f, ac[3][1] & 0x0f)
logger.info("ECW min: " + str(ecw_min))
if ecw_min != (4, 4, 3, 2):
raise Exception("Unexpected ECW min value: " + str(ecw_min))
ecw_max = ((ac[0][1] & 0xf0) >> 4, (ac[1][1] & 0xf0) >> 4,
(ac[2][1] & 0xf0) >> 4, (ac[3][1] & 0xf0) >> 4)
logger.info("ECW max: " + str(ecw_max))
if ecw_max != (10, 10, 4, 3):
raise Exception("Unexpected ECW max value: " + str(ecw_max))
txop = (ac[0][2], ac[1][2], ac[2][2], ac[3][2])
logger.info("TXOP: " + str(txop))
if txop != (0, 0, 94, 47):
raise Exception("Unexpected TXOP value: " + str(txop))
finally:
dev[0].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
set_world_reg(apdev[0], None, dev[0])
dev[0].flush_scan_cache()
diff --git a/tests/hwsim/test_ap_params.py b/tests/hwsim/test_ap_params.py
index 5da861593948..72ac8e443ff9 100644
--- a/tests/hwsim/test_ap_params.py
+++ b/tests/hwsim/test_ap_params.py
@@ -1,911 +1,972 @@
# Test various AP mode parameters
# Copyright (c) 2014, Qualcomm Atheros, Inc.
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import logging
logger = logging.getLogger()
import os
import struct
import subprocess
import time
import hwsim_utils
import hostapd
from tshark import run_tshark
from utils import *
@remote_compatible
def test_ap_fragmentation_rts_set_high(dev, apdev):
"""WPA2-PSK AP with fragmentation and RTS thresholds larger than frame length"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['rts_threshold'] = "1000"
params['fragm_threshold'] = "2000"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
hapd.disable()
hapd.set('fragm_threshold', '-1')
hapd.set('rts_threshold', '-1')
hapd.enable()
@remote_compatible
def test_ap_fragmentation_open(dev, apdev):
"""Open AP with fragmentation threshold"""
ssid = "fragmentation"
params = {}
params['ssid'] = ssid
params['fragm_threshold'] = "1000"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
hapd.disable()
hapd.set('fragm_threshold', '-1')
hapd.enable()
@remote_compatible
def test_ap_fragmentation_wpa2(dev, apdev):
"""WPA2-PSK AP with fragmentation threshold"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['fragm_threshold'] = "1000"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
hapd.disable()
hapd.set('fragm_threshold', '-1')
hapd.enable()
def test_ap_vendor_elements(dev, apdev):
"""WPA2-PSK AP with vendor elements added"""
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['vendor_elements'] = "dd0411223301"
params['assocresp_elements'] = "dd0411223302"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
bss = dev[0].get_bss(bssid)
if "dd0411223301" not in bss['ie']:
raise Exception("Vendor element not shown in scan results")
hapd.set('vendor_elements', 'dd051122330203dd0400137400dd04001374ff')
if "OK" not in hapd.request("UPDATE_BEACON"):
raise Exception("UPDATE_BEACON failed")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
bss = dev[1].get_bss(bssid)
if "dd0411223301" in bss['ie']:
raise Exception("Old vendor element still in scan results")
if "dd051122330203" not in bss['ie']:
raise Exception("New vendor element not shown in scan results")
def test_ap_element_parse(dev, apdev):
"""Information element parsing - extra coverage"""
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
params = {'ssid': ssid,
'vendor_elements': "380501020304059e009e009e009e009e009e00"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
bss = dev[0].get_bss(bssid)
if "38050102030405" not in bss['ie']:
raise Exception("Timeout element not shown in scan results")
@remote_compatible
def test_ap_element_parse_oom(dev, apdev):
"""Information element parsing OOM"""
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
params = {'ssid': ssid,
'vendor_elements': "dd0d506f9a0a00000600411c440028"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with alloc_fail(dev[0], 1, "wpabuf_alloc;ieee802_11_vendor_ie_concat"):
bss = dev[0].get_bss(bssid)
logger.info(str(bss))
def test_ap_country(dev, apdev):
"""WPA2-PSK AP setting country code and using 5 GHz band"""
try:
hapd = None
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['country_code'] = 'FI'
params['ieee80211d'] = '1'
params['hw_mode'] = 'a'
params['channel'] = '36'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def test_ap_acl_accept(dev, apdev):
"""MAC ACL accept list"""
ssid = "acl"
params = {}
filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
hostapd.send_file(apdev[0], filename, filename)
params['ssid'] = ssid
params['accept_mac_file'] = filename
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
hapd.request("SET macaddr_acl 1")
dev[1].dump_monitor()
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected association")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_acl_deny(dev, apdev):
"""MAC ACL deny list"""
ssid = "acl"
params = {}
filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
hostapd.send_file(apdev[0], filename, filename)
params['ssid'] = ssid
params['deny_mac_file'] = filename
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", passive=True)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected association")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_acl_mgmt(dev, apdev):
"""MAC ACL accept/deny management"""
ssid = "acl"
params = {}
filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
hostapd.send_file(apdev[0], filename, filename)
params['ssid'] = ssid
params['deny_mac_file'] = filename
hapd = hostapd.add_ap(apdev[0], params)
accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
logger.info("accept: " + str(accept))
deny = hapd.request("DENY_ACL SHOW").splitlines()
logger.info("deny: " + str(deny))
if len(accept) != 0:
raise Exception("Unexpected number of accept entries")
if len(deny) != 3:
raise Exception("Unexpected number of deny entries")
if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
raise Exception("Missing deny entry")
if "OK" not in hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66:77"):
raise Exception("DEL_MAC with empty list failed")
if "FAIL" not in hapd.request("ACCEPT_ACL ADD_MAC 22:33:44:55:66"):
raise Exception("ADD_MAC with invalid MAC address accepted")
hapd.request("ACCEPT_ACL ADD_MAC 22:33:44:55:66:77")
if "FAIL" not in hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66"):
raise Exception("DEL_MAC with invalid MAC address accepted")
hapd.request("DENY_ACL ADD_MAC 22:33:44:55:66:88 VLAN_ID=2")
accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
logger.info("accept: " + str(accept))
deny = hapd.request("DENY_ACL SHOW").splitlines()
logger.info("deny: " + str(deny))
if len(accept) != 1:
raise Exception("Unexpected number of accept entries (2)")
if len(deny) != 4:
raise Exception("Unexpected number of deny entries (2)")
if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
raise Exception("Missing deny entry (2)")
if "22:33:44:55:66:88 VLAN_ID=2" not in deny:
raise Exception("Missing deny entry (2)")
if "22:33:44:55:66:77 VLAN_ID=0" not in accept:
raise Exception("Missing accept entry (2)")
hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66:77")
hapd.request("DENY_ACL DEL_MAC 22:33:44:55:66:88")
accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
logger.info("accept: " + str(accept))
deny = hapd.request("DENY_ACL SHOW").splitlines()
logger.info("deny: " + str(deny))
if len(accept) != 0:
raise Exception("Unexpected number of accept entries (3)")
if len(deny) != 3:
raise Exception("Unexpected number of deny entries (3)")
if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
raise Exception("Missing deny entry (3)")
hapd.request("ACCEPT_ACL CLEAR")
hapd.request("DENY_ACL CLEAR")
accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
logger.info("accept: " + str(accept))
deny = hapd.request("DENY_ACL SHOW").splitlines()
logger.info("deny: " + str(deny))
if len(accept) != 0:
raise Exception("Unexpected number of accept entries (4)")
if len(deny) != 0:
raise Exception("Unexpected number of deny entries (4)")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[0].dump_monitor()
hapd.request("DENY_ACL ADD_MAC " + dev[0].own_addr())
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
if filename.startswith('/tmp/'):
os.unlink(filename)
def test_ap_acl_accept_changes(dev, apdev):
"""MAC ACL accept list changes"""
ssid = "acl"
params = {}
params['ssid'] = ssid
params['macaddr_acl'] = "1"
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("ACCEPT_ACL ADD_MAC " + dev[0].own_addr())
hapd.request("ACCEPT_ACL ADD_MAC " + dev[1].own_addr())
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hapd.request("ACCEPT_ACL DEL_MAC " + dev[0].own_addr())
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected disconnection")
hapd.request("ACCEPT_ACL CLEAR")
dev[1].wait_disconnected()
dev[1].request("DISCONNECT")
@remote_compatible
def test_ap_wds_sta(dev, apdev):
"""WPA2-PSK AP with STA using 4addr mode"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wds_sta'] = "1"
params['wds_bridge'] = "wds-br0"
hapd = hostapd.add_ap(apdev[0], params)
try:
dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = hapd.wait_event(["WDS-STA-INTERFACE-ADDED"], timeout=10)
if ev is None:
raise Exception("No WDS-STA-INTERFACE-ADDED event seen")
if "sta_addr=" + dev[0].own_addr() not in ev:
raise Exception("No sta_addr match in " + ev)
if "ifname=" + hapd.ifname + ".sta" not in ev:
raise Exception("No ifname match in " + ev)
sta = hapd.get_sta(dev[0].own_addr())
if "wds_sta_ifname" not in sta:
raise Exception("Missing wds_sta_ifname in STA data")
if "ifname=" + sta['wds_sta_ifname'] not in ev:
raise Exception("wds_sta_ifname %s not in event: %s" %
(sta['wds_sta_ifname'], ev))
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("SET reassoc_same_bss_optim 1")
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=5, timeout=1)
finally:
dev[0].request("SET reassoc_same_bss_optim 0")
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
def test_ap_wds_sta_eap(dev, apdev):
"""WPA2-EAP AP with STA using 4addr mode"""
ssid = "test-wpa2-eap"
params = hostapd.wpa2_eap_params(ssid=ssid)
params['wds_sta'] = "1"
params['wds_bridge'] = "wds-br0"
hapd = hostapd.add_ap(apdev[0], params)
try:
dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
dev[0].connect(ssid, key_mgmt="WPA-EAP", eap="GPSK",
identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
ev = hapd.wait_event(["WDS-STA-INTERFACE-ADDED"], timeout=10)
if ev is None:
raise Exception("No WDS-STA-INTERFACE-ADDED event seen")
if "sta_addr=" + dev[0].own_addr() not in ev:
raise Exception("No sta_addr match in " + ev)
if "ifname=" + hapd.ifname + ".sta" not in ev:
raise Exception("No ifname match in " + ev)
sta = hapd.get_sta(dev[0].own_addr())
if "wds_sta_ifname" not in sta:
raise Exception("Missing wds_sta_ifname in STA data")
if "ifname=" + sta['wds_sta_ifname'] not in ev:
raise Exception("wds_sta_ifname %s not in event: %s" %
(sta['wds_sta_ifname'], ev))
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
finally:
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
def test_ap_wds_sta_open(dev, apdev):
"""Open AP with STA using 4addr mode"""
ssid = "test-wds-open"
params = {}
params['ssid'] = ssid
params['wds_sta'] = "1"
params['wds_bridge'] = "wds-br0"
hapd = hostapd.add_ap(apdev[0], params)
try:
dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("SET reassoc_same_bss_optim 1")
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=5, timeout=1)
finally:
dev[0].request("SET reassoc_same_bss_optim 0")
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
def test_ap_wds_sta_wep(dev, apdev):
"""WEP AP with STA using 4addr mode"""
check_wep_capa(dev[0])
ssid = "test-wds-wep"
params = {}
params['ssid'] = ssid
params["ieee80211n"] = "0"
params['wep_key0'] = '"hello"'
params['wds_sta'] = "1"
params['wds_bridge'] = "wds-br0"
hapd = hostapd.add_ap(apdev[0], params)
try:
dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
dev[0].connect(ssid, key_mgmt="NONE", wep_key0='"hello"',
scan_freq="2412")
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=15)
dev[0].request("SET reassoc_same_bss_optim 1")
dev[0].request("REATTACH")
dev[0].wait_connected()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
max_tries=5, timeout=1)
finally:
dev[0].request("SET reassoc_same_bss_optim 0")
dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
@remote_compatible
def test_ap_inactivity_poll(dev, apdev):
"""AP using inactivity poll"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['ap_max_inactivity'] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("DISCONNECT")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("MGMT RX wait timed out for Deauth")
hapd.set("ext_mgmt_frame_handling", "0")
ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
if ev is None:
raise Exception("STA disconnection on inactivity was not reported")
@remote_compatible
def test_ap_inactivity_disconnect(dev, apdev):
"""AP using inactivity disconnect"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['ap_max_inactivity'] = "1"
params['skip_inactivity_poll'] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("DISCONNECT")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("MGMT RX wait timed out for Deauth")
hapd.set("ext_mgmt_frame_handling", "0")
ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
if ev is None:
raise Exception("STA disconnection on inactivity was not reported")
@remote_compatible
def test_ap_basic_rates(dev, apdev):
"""Open AP with lots of basic rates"""
ssid = "basic rates"
params = {}
params['ssid'] = ssid
params['basic_rates'] = "10 20 55 110 60 90 120 180 240 360 480 540"
hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
@remote_compatible
def test_ap_short_preamble(dev, apdev):
"""Open AP with short preamble"""
ssid = "short preamble"
params = {}
params['ssid'] = ssid
params['preamble'] = "1"
hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
def test_ap_spectrum_management_required(dev, apdev):
"""Open AP with spectrum management required"""
ssid = "spectrum mgmt"
params = {}
params['ssid'] = ssid
params["country_code"] = "JP"
params["hw_mode"] = "a"
params["channel"] = "36"
params["ieee80211d"] = "1"
params["local_pwr_constraint"] = "3"
params['spectrum_mgmt_required'] = "1"
try:
hapd = None
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="5180")
dev[0].wait_regdom(country_ie=True)
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
@remote_compatible
def test_ap_max_listen_interval(dev, apdev):
"""Open AP with maximum listen interval limit"""
ssid = "listen"
params = {}
params['ssid'] = ssid
params['max_listen_interval'] = "1"
hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
if ev is None:
raise Exception("Association rejection not reported")
if "status_code=51" not in ev:
raise Exception("Unexpected ASSOC-REJECT reason")
@remote_compatible
def test_ap_max_num_sta(dev, apdev):
"""Open AP with maximum STA count"""
ssid = "max"
params = {}
params['ssid'] = ssid
params['max_num_sta'] = "1"
hostapd.add_ap(apdev[0], params)
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected association")
def test_ap_max_num_sta_no_probe_resp(dev, apdev, params):
"""Maximum STA count and limit on Probe Response frames"""
logdir = params['logdir']
dev[0].flush_scan_cache()
ssid = "max"
params = {}
params['ssid'] = ssid
params['beacon_int'] = "2000"
params['max_num_sta'] = "1"
params['no_probe_resp_if_max_sta'] = "1"
hostapd.add_ap(apdev[0], params)
dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
dev[0].scan(freq=2412, type="ONLY")
dev[0].scan(freq=2412, type="ONLY")
seen = dev[0].get_bss(apdev[0]['bssid']) != None
dev[1].scan(freq=2412, type="ONLY")
if seen:
out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
"wlan.fc.type_subtype == 5", ["wlan.da"])
if out:
if dev[0].own_addr() not in out:
# Discovery happened through Beacon frame reception. That's not
# an error case.
seen = False
if dev[1].own_addr() not in out:
raise Exception("No Probe Response frames to dev[1] seen")
if seen:
raise Exception("AP found unexpectedly")
@remote_compatible
def test_ap_tx_queue_params(dev, apdev):
"""Open AP with TX queue params set"""
ssid = "tx"
params = {}
params['ssid'] = ssid
params['tx_queue_data2_aifs'] = "4"
params['tx_queue_data2_cwmin'] = "7"
params['tx_queue_data2_cwmax'] = "1023"
params['tx_queue_data2_burst'] = "4.2"
params['tx_queue_data1_aifs'] = "4"
params['tx_queue_data1_cwmin'] = "7"
params['tx_queue_data1_cwmax'] = "1023"
params['tx_queue_data1_burst'] = "2"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_tx_queue_params_invalid(dev, apdev):
"""Invalid TX queue params set (cwmin/cwmax)"""
ssid = "tx"
params = {}
params['ssid'] = ssid
params['tx_queue_data2_aifs'] = "4"
params['tx_queue_data2_cwmin'] = "7"
params['tx_queue_data2_cwmax'] = "1023"
params['tx_queue_data2_burst'] = "4.2"
params['wmm_ac_bk_cwmin'] = "4"
params['wmm_ac_bk_cwmax'] = "10"
params['wmm_ac_bk_aifs'] = "7"
params['wmm_ac_bk_txop_limit'] = "0"
params['wmm_ac_bk_acm'] = "0"
hapd = hostapd.add_ap(apdev[0], params)
# Valid WMM change
hapd.set("wmm_ac_be_cwmin", "3")
# "Invalid TX queue cwMin/cwMax values. cwMin(7) greater than cwMax(3)"
if "FAIL" not in hapd.request('SET tx_queue_data2_cwmax 3'):
raise Exception("TX cwMax < cwMin accepted")
# "Invalid WMM AC cwMin/cwMax values. cwMin(4) greater than cwMax(3)"
if "FAIL" not in hapd.request('SET wmm_ac_bk_cwmax 3'):
raise Exception("AC cwMax < cwMin accepted")
hapd.request("SET tx_queue_data2_cwmax 1023")
hapd.set("wmm_ac_bk_cwmax", "10")
# Invalid IEs to cause WMM parameter update failing
hapd.set("vendor_elements", "dd04112233")
hapd.set("wmm_ac_be_cwmin", "3")
# Valid IEs to cause WMM parameter update succeeding
hapd.set("vendor_elements", "dd0411223344")
hapd.set("wmm_ac_be_cwmin", "3")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
def test_ap_beacon_rate_legacy(dev, apdev):
"""Open AP with Beacon frame TX rate 5.5 Mbps"""
hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
res = hapd.get_driver_status_field('capa.flags')
if (int(res, 0) & 0x0000080000000000) == 0:
raise HwsimSkip("Setting Beacon frame TX rate not supported")
hapd.disable()
hapd.set('beacon_rate', '55')
hapd.enable()
dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="2412")
time.sleep(0.5)
def test_ap_beacon_rate_legacy2(dev, apdev):
"""Open AP with Beacon frame TX rate 12 Mbps in VHT BSS"""
hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
res = hapd.get_driver_status_field('capa.flags')
if (int(res, 0) & 0x0000080000000000) == 0:
raise HwsimSkip("Setting Beacon frame TX rate not supported")
hapd.disable()
hapd.set('beacon_rate', '120')
hapd.set("country_code", "DE")
hapd.set("hw_mode", "a")
hapd.set("channel", "36")
hapd.set("ieee80211n", "1")
hapd.set("ieee80211ac", "1")
hapd.set("ht_capab", "[HT40+]")
hapd.set("vht_capab", "")
hapd.set("vht_oper_chwidth", "0")
hapd.set("vht_oper_centr_freq_seg0_idx", "0")
try:
hapd.enable()
dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
time.sleep(0.5)
finally:
dev[0].request("DISCONNECT")
hapd.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
def test_ap_beacon_rate_ht(dev, apdev):
"""Open AP with Beacon frame TX rate HT-MCS 0"""
hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
res = hapd.get_driver_status_field('capa.flags')
if (int(res, 0) & 0x0000100000000000) == 0:
raise HwsimSkip("Setting Beacon frame TX rate not supported")
hapd.disable()
hapd.set('beacon_rate', 'ht:0')
hapd.enable()
dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="2412")
time.sleep(0.5)
def test_ap_beacon_rate_ht2(dev, apdev):
"""Open AP with Beacon frame TX rate HT-MCS 1 in VHT BSS"""
hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
res = hapd.get_driver_status_field('capa.flags')
if (int(res, 0) & 0x0000100000000000) == 0:
raise HwsimSkip("Setting Beacon frame TX rate not supported")
hapd.disable()
hapd.set('beacon_rate', 'ht:1')
hapd.set("country_code", "DE")
hapd.set("hw_mode", "a")
hapd.set("channel", "36")
hapd.set("ieee80211n", "1")
hapd.set("ieee80211ac", "1")
hapd.set("ht_capab", "[HT40+]")
hapd.set("vht_capab", "")
hapd.set("vht_oper_chwidth", "0")
hapd.set("vht_oper_centr_freq_seg0_idx", "0")
try:
hapd.enable()
dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
time.sleep(0.5)
finally:
dev[0].request("DISCONNECT")
hapd.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
def test_ap_beacon_rate_vht(dev, apdev):
"""Open AP with Beacon frame TX rate VHT-MCS 0"""
hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
res = hapd.get_driver_status_field('capa.flags')
if (int(res, 0) & 0x0000200000000000) == 0:
raise HwsimSkip("Setting Beacon frame TX rate not supported")
hapd.disable()
hapd.set('beacon_rate', 'vht:0')
hapd.set("country_code", "DE")
hapd.set("hw_mode", "a")
hapd.set("channel", "36")
hapd.set("ieee80211n", "1")
hapd.set("ieee80211ac", "1")
hapd.set("ht_capab", "[HT40+]")
hapd.set("vht_capab", "")
hapd.set("vht_oper_chwidth", "0")
hapd.set("vht_oper_centr_freq_seg0_idx", "0")
try:
hapd.enable()
dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
time.sleep(0.5)
finally:
dev[0].request("DISCONNECT")
hapd.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
def test_ap_wep_to_wpa(dev, apdev):
"""WEP to WPA2-PSK configuration change in hostapd"""
check_wep_capa(dev[0])
hapd = hostapd.add_ap(apdev[0],
{"ssid": "wep-to-wpa",
"wep_key0": '"hello"'})
dev[0].flush_scan_cache()
dev[0].connect("wep-to-wpa", key_mgmt="NONE", wep_key0='"hello"',
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
hapd.set("wep_key0", "")
hapd.set("wpa_passphrase", "12345678")
hapd.set("wpa", "2")
hapd.set("wpa_key_mgmt", "WPA-PSK")
hapd.set("rsn_pairwise", "CCMP")
hapd.enable()
dev[0].connect("wep-to-wpa", psk="12345678", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_missing_psk(dev, apdev):
"""WPA2-PSK AP and no PSK configured"""
ssid = "test-wpa2-psk"
params = hostapd.wpa2_params(ssid=ssid)
try:
# "WPA-PSK enabled, but PSK or passphrase is not configured."
hostapd.add_ap(apdev[0], params)
raise Exception("AP setup succeeded unexpectedly")
except Exception as e:
if "Failed to enable hostapd" in str(e):
pass
else:
raise
def test_ap_eapol_version(dev, apdev):
"""hostapd eapol_version configuration"""
passphrase = "asdfghjkl"
params = hostapd.wpa2_params(ssid="test1", passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
params = hostapd.wpa2_params(ssid="test2", passphrase=passphrase)
params['eapol_version'] = '1'
hapd2 = hostapd.add_ap(apdev[1], params)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].connect("test1", psk=passphrase, scan_freq="2412",
wait_connect=False)
ev1 = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev1 is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
hapd.request("SET ext_eapol_frame_io 0")
hapd2.request("SET ext_eapol_frame_io 1")
dev[1].connect("test2", psk=passphrase, scan_freq="2412",
wait_connect=False)
ev2 = hapd2.wait_event(["EAPOL-TX"], timeout=15)
if ev2 is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
hapd2.request("SET ext_eapol_frame_io 0")
dev[0].wait_connected()
dev[1].wait_connected()
ver1 = ev1.split(' ')[2][0:2]
ver2 = ev2.split(' ')[2][0:2]
if ver1 != "02":
raise Exception("Unexpected default eapol_version: " + ver1)
if ver2 != "01":
raise Exception("eapol_version did not match configuration: " + ver2)
def test_ap_dtim_period(dev, apdev):
"""DTIM period configuration"""
ssid = "dtim-period"
params = {'ssid': ssid, 'dtim_period': "10"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
for i in range(10):
dev[0].scan(freq="2412")
bss = dev[0].get_bss(bssid)
if 'beacon_ie' in bss:
break
time.sleep(0.2)
if 'beacon_ie' not in bss:
raise Exception("Did not find Beacon IEs")
ie = parse_ie(bss['beacon_ie'])
if 5 not in ie:
raise Exception("TIM element missing")
count, period = struct.unpack('BB', ie[5][0:2])
logger.info("DTIM count %d DTIM period %d" % (count, period))
if period != 10:
raise Exception("Unexpected DTIM period: %d" % period)
if count >= period:
raise Exception("Unexpected DTIM count: %d" % count)
def test_ap_no_probe_resp(dev, apdev):
"""AP with Probe Response frame sending from hostapd disabled"""
ssid = "no-probe-resp"
params = {'ssid': ssid, 'send_probe_response': "0"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412", passive=True)
dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
bss = dev[0].get_bss(bssid)
if 'ie' in bss and 'beacon_ie' in bss and \
len(bss['ie']) != len(bss['beacon_ie']):
raise Exception("Probe Response frames seen")
def test_ap_long_preamble(dev, apdev):
"""AP with long preamble"""
ssid = "long-preamble"
params = {'ssid': ssid, 'preamble': "0",
'hw_mode': 'b', 'ieee80211n': '0',
'supported_rates': '10', 'basic_rates': '10'}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wmm_uapsd(dev, apdev):
"""AP with U-APSD advertisement"""
ssid = "uapsd"
params = {'ssid': ssid, 'uapsd_advertisement_enabled': "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wowlan_triggers(dev, apdev):
"""AP with wowlan_triggers"""
ssid = "wowlan"
params = {'ssid': ssid, 'wowlan_triggers': "any"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_notify_mgmt_frames(dev, apdev):
"""hostapd notify_mgmt_frames configuration enabled"""
ssid = "mgmt_frames"
params = {'ssid': ssid, 'notify_mgmt_frames': "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=5)
if ev is None:
raise Exception("AP-MGMT-FRAME-RECEIVED wait timed out")
if "buf=b0" not in ev:
raise Exception("Expected auth request in AP-MGMT-FRAME-RECEIVED")
def test_ap_notify_mgmt_frames_disabled(dev, apdev):
"""hostapd notify_mgmt_frames configuration disabled"""
ssid = "mgmt_frames"
params = {'ssid': ssid, 'notify_mgmt_frames': "0"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected AP-MGMT-FRAME-RECEIVED")
+
+def test_ap_airtime_policy_static(dev, apdev):
+ """Airtime policy - static"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "1"
+ params['airtime_update_interval'] = "200"
+ params['airtime_sta_weight'] = dev[0].own_addr() + " 512"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_dynamic(dev, apdev):
+ """Airtime policy - per-BSS dynamic"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "2"
+ params['airtime_update_interval'] = "200"
+ params['airtime_bss_weight'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_limit(dev, apdev):
+ """Airtime policy - per-BSS limit"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "3"
+ params['airtime_update_interval'] = "200"
+ params['airtime_bss_weight'] = "2"
+ params['airtime_bss_limit'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+ hapd.set("force_backlog_bytes", "1")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_limit_invalid(dev, apdev):
+ """Airtime policy - per-BSS limit (invalid)"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "3"
+ params['airtime_update_interval'] = "0"
+ params['airtime_bss_weight'] = "2"
+ params['airtime_bss_limit'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid airtime policy configuration accepted")
+ hapd.set("airtime_update_interval", "200")
+ hapd.enable()
+ hapd.set("airtime_update_interval", "0")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
diff --git a/tests/hwsim/test_ap_psk.py b/tests/hwsim/test_ap_psk.py
index be6a88b2a164..b6048be13844 100644
--- a/tests/hwsim/test_ap_psk.py
+++ b/tests/hwsim/test_ap_psk.py
@@ -1,3537 +1,3553 @@
# WPA2-Personal tests
# Copyright (c) 2014, Qualcomm Atheros, Inc.
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import binascii
from Crypto.Cipher import AES
import hashlib
import hmac
import logging
logger = logging.getLogger()
import os
import re
import socket
import struct
import subprocess
import time
import hostapd
from utils import *
import hwsim_utils
from wpasupplicant import WpaSupplicant
from tshark import run_tshark
from wlantest import WlantestCapture, Wlantest
def check_mib(dev, vals):
mib = dev.get_mib()
for v in vals:
if mib[v[0]] != v[1]:
raise Exception("Unexpected {} = {} (expected {})".format(v[0], mib[v[0]], v[1]))
@remote_compatible
def test_ap_wpa2_psk(dev, apdev):
"""WPA2-PSK AP with PSK instead of passphrase"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
hapd = hostapd.add_ap(apdev[0], params)
key_mgmt = hapd.get_config()['key_mgmt']
if key_mgmt.split(' ')[0] != "WPA-PSK":
raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
sig = dev[0].request("SIGNAL_POLL").splitlines()
pkt = dev[0].request("PKTCNT_POLL").splitlines()
if "FREQUENCY=2412" not in sig:
raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
if "TXBAD=0" not in pkt:
raise Exception("Unexpected TXBAD value: " + str(pkt))
def test_ap_wpa2_psk_file(dev, apdev):
"""WPA2-PSK AP with PSK from a file"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_psk_file'] = 'hostapd.wpa_psk'
hostapd.add_ap(apdev[0], params)
dev[1].connect(ssid, psk="very secret", scan_freq="2412", wait_connect=False)
dev[2].connect(ssid, raw_psk=psk, scan_freq="2412")
dev[2].request("REMOVE_NETWORK all")
dev[0].connect(ssid, psk="very secret", scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[2].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
dev[0].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
ev = dev[1].wait_event(["WPA: 4-Way Handshake failed"], timeout=10)
if ev is None:
raise Exception("Timed out while waiting for failure report")
dev[1].request("REMOVE_NETWORK all")
def check_no_keyid(hapd, dev):
addr = dev.own_addr()
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=1)
if ev is None:
raise Exception("No AP-STA-CONNECTED indicated")
if addr not in ev:
raise Exception("AP-STA-CONNECTED for unexpected STA")
if "keyid=" in ev:
raise Exception("Unexpected keyid indication")
def check_keyid(hapd, dev, keyid):
addr = dev.own_addr()
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=1)
if ev is None:
raise Exception("No AP-STA-CONNECTED indicated")
if addr not in ev:
raise Exception("AP-STA-CONNECTED for unexpected STA")
if "keyid=" + keyid not in ev:
raise Exception("Incorrect keyid indication")
sta = hapd.get_sta(addr)
if 'keyid' not in sta or sta['keyid'] != keyid:
raise Exception("Incorrect keyid in STA output")
dev.request("REMOVE_NETWORK all")
def check_disconnect(dev, expected):
for i in range(2):
if expected[i]:
dev[i].wait_disconnected()
dev[i].request("REMOVE_NETWORK all")
else:
ev = dev[i].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected disconnection")
dev[i].request("REMOVE_NETWORK all")
dev[i].wait_disconnected()
def test_ap_wpa2_psk_file_keyid(dev, apdev, params):
"""WPA2-PSK AP with PSK from a file (keyid and reload)"""
psk_file = os.path.join(params['logdir'], 'ap_wpa2_psk_file_keyid.wpa_psk')
with open(psk_file, 'w') as f:
f.write('00:00:00:00:00:00 secret passphrase\n')
f.write('02:00:00:00:00:00 very secret\n')
f.write('00:00:00:00:00:00 another passphrase for all STAs\n')
ssid = "test-wpa2-psk"
params = hostapd.wpa2_params(ssid=ssid, passphrase='qwertyuiop')
params['wpa_psk_file'] = psk_file
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk="very secret", scan_freq="2412")
check_no_keyid(hapd, dev[0])
dev[1].connect(ssid, psk="another passphrase for all STAs",
scan_freq="2412")
check_no_keyid(hapd, dev[1])
dev[2].connect(ssid, psk="qwertyuiop", scan_freq="2412")
check_no_keyid(hapd, dev[2])
with open(psk_file, 'w') as f:
f.write('00:00:00:00:00:00 secret passphrase\n')
f.write('02:00:00:00:00:00 very secret\n')
f.write('00:00:00:00:00:00 changed passphrase\n')
if "OK" not in hapd.request("RELOAD_WPA_PSK"):
raise Exception("RELOAD_WPA_PSK failed")
check_disconnect(dev, [False, True, False])
with open(psk_file, 'w') as f:
f.write('00:00:00:00:00:00 secret passphrase\n')
f.write('keyid=foo 02:00:00:00:00:00 very secret\n')
f.write('keyid=bar 00:00:00:00:00:00 another passphrase for all STAs\n')
if "OK" not in hapd.request("RELOAD_WPA_PSK"):
raise Exception("RELOAD_WPA_PSK failed")
dev[0].connect(ssid, psk="very secret", scan_freq="2412")
check_keyid(hapd, dev[0], "foo")
dev[1].connect(ssid, psk="another passphrase for all STAs",
scan_freq="2412")
check_keyid(hapd, dev[1], "bar")
dev[2].connect(ssid, psk="qwertyuiop", scan_freq="2412")
check_no_keyid(hapd, dev[2])
dev[0].wait_disconnected()
dev[0].connect(ssid, psk="secret passphrase", scan_freq="2412")
check_no_keyid(hapd, dev[0])
with open(psk_file, 'w') as f:
f.write('# empty\n')
if "OK" not in hapd.request("RELOAD_WPA_PSK"):
raise Exception("RELOAD_WPA_PSK failed")
check_disconnect(dev, [True, True, False])
with open(psk_file, 'w') as f:
f.write('broken\n')
if "FAIL" not in hapd.request("RELOAD_WPA_PSK"):
raise Exception("RELOAD_WPA_PSK succeeded with invalid file")
@remote_compatible
def test_ap_wpa2_psk_mem(dev, apdev):
"""WPA2-PSK AP with passphrase only in memory"""
try:
_test_ap_wpa2_psk_mem(dev, apdev)
finally:
dev[0].request("SCAN_INTERVAL 5")
dev[1].request("SCAN_INTERVAL 5")
def _test_ap_wpa2_psk_mem(dev, apdev):
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
dev[0].request("SCAN_INTERVAL 1")
ev = dev[0].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
if ev is None:
raise Exception("Request for PSK/passphrase timed out")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':"' + passphrase + '"')
dev[0].wait_connected(timeout=10)
dev[1].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
dev[1].request("SCAN_INTERVAL 1")
ev = dev[1].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
if ev is None:
raise Exception("Request for PSK/passphrase timed out(2)")
id = ev.split(':')[0].split('-')[-1]
dev[1].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':' + psk)
dev[1].wait_connected(timeout=10)
@remote_compatible
def test_ap_wpa2_ptk_rekey(dev, apdev):
"""WPA2-PSK AP and PTK rekey enforced by station"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
Wlantest.setup(hapd)
wt = Wlantest()
wt.flush()
wt.add_passphrase(passphrase)
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"])
if ev is None:
raise Exception("PTK rekey timed out")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Disconnect instead of rekey")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_ptk_rekey_blocked_ap(dev, apdev):
"""WPA2-PSK AP and PTK rekey enforced by station and AP blocking it"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_deny_ptk0_rekey'] = "2"
hapd = hostapd.add_ap(apdev[0], params)
conf = hapd.request("GET_CONFIG").splitlines()
if "wpa_deny_ptk0_rekey=2" not in conf:
raise Exception("wpa_deny_ptk0_rekey value not in GET_CONFIG")
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"])
if ev is None:
raise Exception("PTK rekey timed out")
if "WPA: Key negotiation completed" in ev:
raise Exception("No disconnect, PTK rekey succeeded")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
if ev is None:
raise Exception("Reconnect too slow")
def test_ap_wpa2_ptk_rekey_blocked_sta(dev, apdev):
"""WPA2-PSK AP and PTK rekey enforced by station while also blocking it"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412",
wpa_deny_ptk0_rekey="2")
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"])
if ev is None:
raise Exception("PTK rekey timed out")
if "WPA: Key negotiation completed" in ev:
raise Exception("No disconnect, PTK rekey succeeded")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
if ev is None:
raise Exception("Reconnect too slow")
def test_ap_wpa2_ptk_rekey_anonce(dev, apdev):
"""WPA2-PSK AP and PTK rekey enforced by station and ANonce change"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
dev[0].dump_monitor()
anonce1 = dev[0].request("GET anonce")
if "OK" not in dev[0].request("KEY_REQUEST 0 1"):
raise Exception("KEY_REQUEST failed")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
anonce2 = dev[0].request("GET anonce")
if anonce1 == anonce2:
raise Exception("AP did not update ANonce in requested PTK rekeying")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_ptk_rekey_ap(dev, apdev):
"""WPA2-PSK AP and PTK rekey enforced by AP"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_ptk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_sha256_ptk_rekey(dev, apdev):
"""WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by station"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
wpa_ptk_rekey="1", scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6")])
@remote_compatible
def test_ap_wpa2_sha256_ptk_rekey_ap(dev, apdev):
"""WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by AP"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
params['wpa_ptk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6")])
@remote_compatible
def test_ap_wpa_ptk_rekey(dev, apdev):
"""WPA-PSK/TKIP AP and PTK rekey enforced by station"""
skip_with_fips(dev[0])
skip_without_tkip(dev[0])
ssid = "test-wpa-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
if "[WPA-PSK-TKIP]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing WPA element info")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa_ptk_rekey_ap(dev, apdev):
"""WPA-PSK/TKIP AP and PTK rekey enforced by AP"""
skip_with_fips(dev[0])
skip_without_tkip(dev[0])
ssid = "test-wpa-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
params['wpa_ptk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa_ccmp(dev, apdev):
"""WPA-PSK/CCMP"""
ssid = "test-wpa-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
params['wpa_pairwise'] = "CCMP"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
check_mib(dev[0], [("dot11RSNAConfigGroupCipherSize", "128"),
("dot11RSNAGroupCipherRequested", "00-50-f2-4"),
("dot11RSNAPairwiseCipherRequested", "00-50-f2-4"),
("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-2"),
("dot11RSNAGroupCipherSelected", "00-50-f2-4"),
("dot11RSNAPairwiseCipherSelected", "00-50-f2-4"),
("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-2"),
("dot1xSuppSuppControlledPortStatus", "Authorized")])
def test_ap_wpa2_psk_file_errors(dev, apdev):
"""WPA2-PSK AP with various PSK file error and success cases"""
addr0 = dev[0].own_addr()
addr1 = dev[1].own_addr()
addr2 = dev[2].own_addr()
ssid = "psk"
pskfile = "/tmp/ap_wpa2_psk_file_errors.psk_file"
try:
os.remove(pskfile)
except:
pass
params = {"ssid": ssid, "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP", "wpa_psk_file": pskfile}
try:
# missing PSK file
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
hapd.request("DISABLE")
# invalid MAC address
with open(pskfile, "w") as f:
f.write("\n")
f.write("foo\n")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
hapd.request("DISABLE")
# no PSK on line
with open(pskfile, "w") as f:
f.write("00:11:22:33:44:55\n")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
hapd.request("DISABLE")
# invalid PSK
with open(pskfile, "w") as f:
f.write("00:11:22:33:44:55 1234567\n")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
hapd.request("DISABLE")
# empty token at the end of the line
with open(pskfile, "w") as f:
f.write("=\n")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE success")
hapd.request("DISABLE")
# valid PSK file
with open(pskfile, "w") as f:
f.write("00:11:22:33:44:55 12345678\n")
f.write(addr0 + " 123456789\n")
f.write(addr1 + " 123456789a\n")
f.write(addr2 + " 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n")
if "FAIL" in hapd.request("ENABLE"):
raise Exception("Unexpected ENABLE failure")
dev[0].connect(ssid, psk="123456789", scan_freq="2412")
dev[1].connect(ssid, psk="123456789a", scan_freq="2412")
dev[2].connect(ssid, raw_psk="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", scan_freq="2412")
finally:
try:
os.remove(pskfile)
except:
pass
@remote_compatible
def test_ap_wpa2_psk_wildcard_ssid(dev, apdev):
"""WPA2-PSK AP and wildcard SSID configuration"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("", bssid=apdev[0]['bssid'], psk=passphrase,
scan_freq="2412")
dev[1].connect("", bssid=apdev[0]['bssid'], raw_psk=psk, scan_freq="2412")
@remote_compatible
def test_ap_wpa2_gtk_rekey(dev, apdev):
"""WPA2-PSK AP and GTK rekey enforced by AP"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_group_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_gtk_rekey_request(dev, apdev):
"""WPA2-PSK AP and GTK rekey by AP request"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
if "OK" not in hapd.request("REKEY_GTK"):
raise Exception("REKEY_GTK failed")
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
+def test_ap_wpa2_gtk_rekey_failure(dev, apdev):
+ """WPA2-PSK AP and GTK rekey failure"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ with fail_test(hapd, 1, "wpa_group_config_group_keys"):
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ wait_fail_trigger(hapd, "GET_FAIL")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ dev[0].wait_disconnected()
+
@remote_compatible
def test_ap_wpa_gtk_rekey(dev, apdev):
"""WPA-PSK/TKIP AP and GTK rekey enforced by AP"""
skip_with_fips(dev[0])
skip_without_tkip(dev[0])
ssid = "test-wpa-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
params['wpa_group_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_gmk_rekey(dev, apdev):
"""WPA2-PSK AP and GMK and GTK rekey enforced by AP"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_group_rekey'] = '1'
params['wpa_gmk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
for i in range(0, 3):
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_strict_rekey(dev, apdev):
"""WPA2-PSK AP and strict GTK rekey enforced by AP"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_strict_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
dev[1].request("DISCONNECT")
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_bridge_fdb(dev, apdev):
"""Bridge FDB entry removal"""
hapd = None
try:
ssid = "test-wpa2-psk"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['bridge'] = 'ap-br0'
hapd = hostapd.add_ap(apdev[0], params)
hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
bssid=apdev[0]['bssid'])
dev[1].connect(ssid, psk=passphrase, scan_freq="2412",
bssid=apdev[0]['bssid'])
hapd.wait_sta()
hapd.wait_sta()
addr0 = dev[0].p2p_interface_addr()
hwsim_utils.test_connectivity_sta(dev[0], dev[1])
err, macs1 = hapd.cmd_execute(['brctl', 'showmacs', 'ap-br0'])
hapd.cmd_execute(['brctl', 'setageing', 'ap-br0', '1'])
dev[0].request("DISCONNECT")
dev[1].request("DISCONNECT")
time.sleep(1)
err, macs2 = hapd.cmd_execute(['brctl', 'showmacs', 'ap-br0'])
addr1 = dev[1].p2p_interface_addr()
if addr0 not in macs1 or addr1 not in macs1:
raise Exception("Bridge FDB entry missing")
if addr0 in macs2 or addr1 in macs2:
raise Exception("Bridge FDB entry was not removed")
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
'down'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0'])
@remote_compatible
def test_ap_wpa2_already_in_bridge(dev, apdev):
"""hostapd behavior with interface already in bridge"""
ifname = apdev[0]['ifname']
br_ifname = 'ext-ap-br0'
try:
ssid = "test-wpa2-psk"
passphrase = "12345678"
hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'up'])
hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', '__ap'])
hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
if hapd.get_driver_status_field('brname') != br_ifname:
raise Exception("Bridge name not identified correctly")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'down'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname])
hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', 'station'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
@remote_compatible
def test_ap_wpa2_in_different_bridge(dev, apdev):
"""hostapd behavior with interface in different bridge"""
ifname = apdev[0]['ifname']
br_ifname = 'ext-ap-br0'
try:
ssid = "test-wpa2-psk"
passphrase = "12345678"
hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'up'])
hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', '__ap'])
hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
time.sleep(0.5)
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['bridge'] = 'ap-br0'
hapd = hostapd.add_ap(apdev[0], params)
hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', 'ap-br0', '0'])
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
'up'])
brname = hapd.get_driver_status_field('brname')
if brname != 'ap-br0':
raise Exception("Incorrect bridge: " + brname)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
if hapd.get_driver_status_field("added_bridge") != "1":
raise Exception("Unexpected added_bridge value")
if hapd.get_driver_status_field("added_if_into_bridge") != "1":
raise Exception("Unexpected added_if_into_bridge value")
dev[0].request("DISCONNECT")
hapd.disable()
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'down'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname,
"2>", "/dev/null"], shell=True)
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
@remote_compatible
def test_ap_wpa2_ext_add_to_bridge(dev, apdev):
"""hostapd behavior with interface added to bridge externally"""
ifname = apdev[0]['ifname']
br_ifname = 'ext-ap-br0'
try:
ssid = "test-wpa2-psk"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'up'])
hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
if hapd.get_driver_status_field('brname') != br_ifname:
raise Exception("Bridge name not identified correctly")
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
'down'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname])
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
def setup_psk_ext(dev, apdev, wpa_ptk_rekey=None):
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = wpa_ptk_rekey
hapd = hostapd.add_ap(apdev, params)
hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
dev.connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
return hapd
def ext_4way_hs(hapd, dev):
bssid = hapd.own_addr()
addr = dev.own_addr()
first = None
last = None
while True:
ev = hapd.wait_event(["EAPOL-TX", "AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
if "AP-STA-CONNECTED" in ev:
dev.wait_connected(timeout=15)
break
if not first:
first = ev.split(' ')[2]
last = ev.split(' ')[2]
res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
ev = dev.wait_event(["EAPOL-TX", "CTRL-EVENT-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
if "CTRL-EVENT-CONNECTED" in ev:
break
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
return first, last
def test_ap_wpa2_psk_ext(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O"""
hapd = setup_psk_ext(dev[0], apdev[0])
ext_4way_hs(hapd, dev[0])
def test_ap_wpa2_psk_unexpected(dev, apdev):
"""WPA2-PSK and supplicant receiving unexpected EAPOL-Key frames"""
hapd = setup_psk_ext(dev[0], apdev[0])
first, last = ext_4way_hs(hapd, dev[0])
# Not associated - Delay processing of received EAPOL frame (state=COMPLETED
# bssid=02:00:00:00:03:00)
other = "02:11:22:33:44:55"
res = dev[0].request("EAPOL_RX " + other + " " + first)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# WPA: EAPOL-Key Replay Counter did not increase - dropping packet
bssid = hapd.own_addr()
res = dev[0].request("EAPOL_RX " + bssid + " " + last)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# WPA: Invalid EAPOL-Key MIC - dropping packet
msg = last[0:18] + '01' + last[20:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=12)
if ev is not None:
raise Exception("Unexpected disconnection")
def test_ap_wpa2_psk_ext_retry_msg_3(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
# Do not send to the AP
dev[0].wait_connected(timeout=15)
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_ext_retry_msg_3b(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (b)"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
# Do not send the first msg 3/4 to the STA yet; wait for retransmission
# from AP.
msg3_1 = ev
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3_2 = ev
# Send the first msg 3/4 to STA
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3_1.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
dev[0].wait_connected(timeout=15)
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
hwsim_utils.test_connectivity(dev[0], hapd)
# Send the second msg 3/4 to STA
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3_2.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
# Do not send the second msg 4/4 to the AP
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_ext_retry_msg_3c(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (c)"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg1 = ev.split(' ')[2]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg4 = ev.split(' ')[2]
# Do not send msg 4/4 to hostapd to trigger retry
# STA believes everything is ready
dev[0].wait_connected()
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
# Send a forged msg 1/4 to STA (update replay counter)
msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
# and replace nonce (this results in "WPA: ANonce from message 1 of
# 4-Way Handshake differs from 3 of 4-Way Handshake - drop packet" when
# wpa_supplicant processed msg 3/4 afterwards)
#msg1b = msg1[0:18] + msg3[18:34] + 32*"ff" + msg1[98:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
if ev is None:
# wpa_supplicant seems to have ignored the forged message. This means
# the attack would fail.
logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
return
# Do not send msg 2/4 to hostapd
# Send previously received msg 3/4 to STA
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_ext_retry_msg_3d(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (d)"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg1 = ev.split(' ')[2]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg4 = ev.split(' ')[2]
# Do not send msg 4/4 to hostapd to trigger retry
# STA believes everything is ready
dev[0].wait_connected()
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
# Send a forged msg 1/4 to STA (update replay counter)
msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
if ev is None:
# wpa_supplicant seems to have ignored the forged message. This means
# the attack would fail.
logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
return
# Do not send msg 2/4 to hostapd
# EAPOL-Key msg 3/4 (retry 2)
# New one needed to get the correct Replay Counter value
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
# Send msg 3/4 to STA
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_ext_retry_msg_3e(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (e)"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg1 = ev.split(' ')[2]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg4 = ev.split(' ')[2]
# Do not send msg 4/4 to hostapd to trigger retry
# STA believes everything is ready
dev[0].wait_connected()
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
# Send a forged msg 1/4 to STA (update replay counter and replace ANonce)
msg1b = msg1[0:18] + msg3[18:34] + 32*"ff" + msg1[98:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
# Do not send msg 2/4 to hostapd
# Send a forged msg 1/4 to STA (back to previously used ANonce)
msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
if ev is None:
# wpa_supplicant seems to have ignored the forged message. This means
# the attack would fail.
logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
return
# Do not send msg 2/4 to hostapd
# EAPOL-Key msg 3/4 (retry 2)
# New one needed to get the correct Replay Counter value
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
# Send msg 3/4 to STA
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_ext_delayed_ptk_rekey(dev, apdev):
"""WPA2-PSK AP using external EAPOL I/O and delayed PTK rekey exchange"""
hapd = setup_psk_ext(dev[0], apdev[0], wpa_ptk_rekey="3")
bssid = apdev[0]['bssid']
addr = dev[0].p2p_interface_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg2 = ev.split(' ')[2]
# Do not send this to the AP
# EAPOL-Key msg 1/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg4 = ev.split(' ')[2]
# Do not send msg 4/4 to AP
# EAPOL-Key msg 3/4 (retry)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
msg4b = ev.split(' ')[2]
# Do not send msg 4/4 to AP
# Send the previous EAPOL-Key msg 4/4 to AP
res = hapd.request("EAPOL_RX " + addr + " " + msg4)
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
# Wait for PTK rekeying to be initialized
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
# EAPOL-Key msg 2/4 from the previous 4-way handshake
# hostapd is expected to ignore this due to unexpected Replay Counter
res = hapd.request("EAPOL_RX " + addr + " " + msg2)
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4 (actually, this ends up being retransmitted 1/4)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
keyinfo = ev.split(' ')[2][10:14]
if keyinfo != "008a":
raise Exception("Unexpected key info when expected msg 1/4:" + keyinfo)
# EAPOL-Key msg 4/4 from the previous 4-way handshake
# hostapd is expected to ignore this due to unexpected Replay Counter
res = hapd.request("EAPOL_RX " + addr + " " + msg4b)
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# Check if any more EAPOL-Key frames are seen. If the second 4-way handshake
# was accepted, there would be no more EAPOL-Key frames. If the Replay
# Counters were rejected, there would be a retransmitted msg 1/4 here.
ev = hapd.wait_event(["EAPOL-TX"], timeout=1.1)
if ev is None:
raise Exception("Did not see EAPOL-TX from hostapd in the end (expected msg 1/4)")
keyinfo = ev.split(' ')[2][10:14]
if keyinfo != "008a":
raise Exception("Unexpected key info when expected msg 1/4:" + keyinfo)
def parse_eapol(data):
(version, type, length) = struct.unpack('>BBH', data[0:4])
payload = data[4:]
if length > len(payload):
raise Exception("Invalid EAPOL length")
if length < len(payload):
payload = payload[0:length]
eapol = {}
eapol['version'] = version
eapol['type'] = type
eapol['length'] = length
eapol['payload'] = payload
if type == 3:
# EAPOL-Key
(eapol['descr_type'],) = struct.unpack('B', payload[0:1])
payload = payload[1:]
if eapol['descr_type'] == 2 or eapol['descr_type'] == 254:
# RSN EAPOL-Key
(key_info, key_len) = struct.unpack('>HH', payload[0:4])
eapol['rsn_key_info'] = key_info
eapol['rsn_key_len'] = key_len
eapol['rsn_replay_counter'] = payload[4:12]
eapol['rsn_key_nonce'] = payload[12:44]
eapol['rsn_key_iv'] = payload[44:60]
eapol['rsn_key_rsc'] = payload[60:68]
eapol['rsn_key_id'] = payload[68:76]
eapol['rsn_key_mic'] = payload[76:92]
payload = payload[92:]
(eapol['rsn_key_data_len'],) = struct.unpack('>H', payload[0:2])
payload = payload[2:]
eapol['rsn_key_data'] = payload
return eapol
def build_eapol(msg):
data = struct.pack(">BBH", msg['version'], msg['type'], msg['length'])
if msg['type'] == 3:
data += struct.pack('>BHH', msg['descr_type'], msg['rsn_key_info'],
msg['rsn_key_len'])
data += msg['rsn_replay_counter']
data += msg['rsn_key_nonce']
data += msg['rsn_key_iv']
data += msg['rsn_key_rsc']
data += msg['rsn_key_id']
data += msg['rsn_key_mic']
data += struct.pack('>H', msg['rsn_key_data_len'])
data += msg['rsn_key_data']
else:
data += msg['payload']
return data
def sha1_prf(key, label, data, outlen):
res = b''
counter = 0
while outlen > 0:
m = hmac.new(key, label.encode(), hashlib.sha1)
m.update(struct.pack('B', 0))
m.update(data)
m.update(struct.pack('B', counter))
counter += 1
hash = m.digest()
if outlen > len(hash):
res += hash
outlen -= len(hash)
else:
res += hash[0:outlen]
outlen = 0
return res
def pmk_to_ptk(pmk, addr1, addr2, nonce1, nonce2):
if addr1 < addr2:
data = binascii.unhexlify(addr1.replace(':', '')) + binascii.unhexlify(addr2.replace(':', ''))
else:
data = binascii.unhexlify(addr2.replace(':', '')) + binascii.unhexlify(addr1.replace(':', ''))
if nonce1 < nonce2:
data += nonce1 + nonce2
else:
data += nonce2 + nonce1
label = "Pairwise key expansion"
ptk = sha1_prf(pmk, label, data, 48)
kck = ptk[0:16]
kek = ptk[16:32]
return (ptk, kck, kek)
def eapol_key_mic(kck, msg):
msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
data = build_eapol(msg)
m = hmac.new(kck, data, hashlib.sha1)
msg['rsn_key_mic'] = m.digest()[0:16]
def rsn_eapol_key_set(msg, key_info, key_len, nonce, data):
msg['rsn_key_info'] = key_info
msg['rsn_key_len'] = key_len
if nonce:
msg['rsn_key_nonce'] = nonce
else:
msg['rsn_key_nonce'] = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')
if data:
msg['rsn_key_data_len'] = len(data)
msg['rsn_key_data'] = data
msg['length'] = 95 + len(data)
else:
msg['rsn_key_data_len'] = 0
msg['rsn_key_data'] = b''
msg['length'] = 95
def recv_eapol(hapd):
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
eapol = binascii.unhexlify(ev.split(' ')[2])
return parse_eapol(eapol)
def send_eapol(hapd, addr, data):
res = hapd.request("EAPOL_RX " + addr + " " + binascii.hexlify(data).decode())
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
def reply_eapol(info, hapd, addr, msg, key_info, nonce, data, kck):
logger.info("Send EAPOL-Key msg " + info)
rsn_eapol_key_set(msg, key_info, 0, nonce, data)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
def eapol_test(apdev, dev, wpa2=True, ieee80211w=0):
bssid = apdev['bssid']
if wpa2:
ssid = "test-wpa2-psk"
else:
ssid = "test-wpa-psk"
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
pmk = binascii.unhexlify(psk)
if wpa2:
params = hostapd.wpa2_params(ssid=ssid)
else:
params = hostapd.wpa_params(ssid=ssid)
params['wpa_psk'] = psk
params['ieee80211w'] = str(ieee80211w)
hapd = hostapd.add_ap(apdev, params)
hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
dev.connect(ssid, raw_psk=psk, scan_freq="2412", wait_connect=False,
ieee80211w=str(ieee80211w))
addr = dev.p2p_interface_addr()
if wpa2:
if ieee80211w == 2:
rsne = binascii.unhexlify('30140100000fac040100000fac040100000fac02cc00')
else:
rsne = binascii.unhexlify('30140100000fac040100000fac040100000fac020000')
else:
rsne = binascii.unhexlify('dd160050f20101000050f20201000050f20201000050f202')
snonce = binascii.unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
return (bssid, ssid, hapd, snonce, pmk, addr, rsne)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol(dev, apdev):
"""WPA2-PSK AP using external EAPOL supplicant"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg = recv_eapol(hapd)
anonce = msg['rsn_key_nonce']
logger.info("Replay same data back")
send_eapol(hapd, addr, build_eapol(msg))
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.info("Truncated Key Data in EAPOL-Key msg 2/4")
rsn_eapol_key_set(msg, 0x0101, 0, snonce, rsne)
msg['length'] = 95 + 22 - 1
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
logger.info("Replay same data back")
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_retry1(dev, apdev):
"""WPA2 4-way handshake with EAPOL-Key 1/4 retransmitted"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg1 = recv_eapol(hapd)
anonce = msg1['rsn_key_nonce']
msg2 = recv_eapol(hapd)
if anonce != msg2['rsn_key_nonce']:
raise Exception("ANonce changed")
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.info("Send EAPOL-Key msg 2/4")
msg = msg2
rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_retry1b(dev, apdev):
"""WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg1 = recv_eapol(hapd)
anonce = msg1['rsn_key_nonce']
msg2 = recv_eapol(hapd)
if anonce != msg2['rsn_key_nonce']:
raise Exception("ANonce changed")
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce, rsne, kck)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_retry1c(dev, apdev):
"""WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg1 = recv_eapol(hapd)
anonce = msg1['rsn_key_nonce']
msg2 = recv_eapol(hapd)
if anonce != msg2['rsn_key_nonce']:
raise Exception("ANonce changed")
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_retry1d(dev, apdev):
"""WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing and older used"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg1 = recv_eapol(hapd)
anonce = msg1['rsn_key_nonce']
msg2 = recv_eapol(hapd)
if anonce != msg2['rsn_key_nonce']:
raise Exception("ANonce changed")
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
(ptk2, kck2, kek2) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck2)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_type_diff(dev, apdev):
"""WPA2 4-way handshake using external EAPOL supplicant"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg = recv_eapol(hapd)
anonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
# Incorrect descriptor type (frame dropped)
msg['descr_type'] = 253
rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
# Incorrect descriptor type, but with a workaround (frame processed)
msg['descr_type'] = 254
rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
logger.info("Replay same data back")
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa_psk_ext_eapol(dev, apdev):
"""WPA2-PSK AP using external EAPOL supplicant"""
skip_without_tkip(dev[0])
(bssid, ssid, hapd, snonce, pmk, addr, wpae) = eapol_test(apdev[0], dev[0],
wpa2=False)
msg = recv_eapol(hapd)
anonce = msg['rsn_key_nonce']
logger.info("Replay same data back")
send_eapol(hapd, addr, build_eapol(msg))
logger.info("Too short data")
send_eapol(hapd, addr, build_eapol(msg)[0:98])
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
msg['descr_type'] = 2
reply_eapol("2/4(invalid type)", hapd, addr, msg, 0x010a, snonce, wpae, kck)
msg['descr_type'] = 254
reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, wpae, kck)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
logger.info("Replay same data back")
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
@remote_compatible
def test_ap_wpa2_psk_ext_eapol_key_info(dev, apdev):
"""WPA2-PSK 4-way handshake with strange key info values"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
msg = recv_eapol(hapd)
anonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
rsn_eapol_key_set(msg, 0x0000, 0, snonce, rsne)
send_eapol(hapd, addr, build_eapol(msg))
rsn_eapol_key_set(msg, 0xffff, 0, snonce, rsne)
send_eapol(hapd, addr, build_eapol(msg))
# SMK M1
rsn_eapol_key_set(msg, 0x2802, 0, snonce, rsne)
send_eapol(hapd, addr, build_eapol(msg))
# SMK M3
rsn_eapol_key_set(msg, 0x2002, 0, snonce, rsne)
send_eapol(hapd, addr, build_eapol(msg))
# Request
rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
send_eapol(hapd, addr, build_eapol(msg))
# Request
rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
tmp_kck = binascii.unhexlify('00000000000000000000000000000000')
eapol_key_mic(tmp_kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
msg = recv_eapol(hapd)
if anonce != msg['rsn_key_nonce']:
raise Exception("ANonce changed")
# Request (valic MIC)
rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
# Request (valid MIC, replayed counter)
rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
eapol_key_mic(kck, msg)
send_eapol(hapd, addr, build_eapol(msg))
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
hapd.wait_sta(timeout=15)
def build_eapol_key_1_4(anonce, replay_counter=1, key_data=b'', key_len=16):
msg = {}
msg['version'] = 2
msg['type'] = 3
msg['length'] = 95 + len(key_data)
msg['descr_type'] = 2
msg['rsn_key_info'] = 0x8a
msg['rsn_key_len'] = key_len
msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
msg['rsn_key_nonce'] = anonce
msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
msg['rsn_key_data_len'] = len(key_data)
msg['rsn_key_data'] = key_data
return msg
def build_eapol_key_3_4(anonce, kck, key_data, replay_counter=2,
key_info=0x13ca, extra_len=0, descr_type=2, key_len=16):
msg = {}
msg['version'] = 2
msg['type'] = 3
msg['length'] = 95 + len(key_data) + extra_len
msg['descr_type'] = descr_type
msg['rsn_key_info'] = key_info
msg['rsn_key_len'] = key_len
msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
msg['rsn_key_nonce'] = anonce
msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_data_len'] = len(key_data)
msg['rsn_key_data'] = key_data
eapol_key_mic(kck, msg)
return msg
def aes_wrap(kek, plain):
n = len(plain) // 8
a = 0xa6a6a6a6a6a6a6a6
enc = AES.new(kek).encrypt
r = [plain[i * 8:(i + 1) * 8] for i in range(0, n)]
for j in range(6):
for i in range(1, n + 1):
b = enc(struct.pack('>Q', a) + r[i - 1])
a = struct.unpack('>Q', b[:8])[0] ^ (n * j + i)
r[i - 1] = b[8:]
return struct.pack('>Q', a) + b''.join(r)
def pad_key_data(plain):
pad_len = len(plain) % 8
if pad_len:
pad_len = 8 - pad_len
plain += b'\xdd'
pad_len -= 1
plain += pad_len * b'\x00'
return plain
def test_ap_wpa2_psk_supp_proto(dev, apdev):
"""WPA2-PSK 4-way handshake protocol testing for supplicant"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Invalid AES wrap data length 0")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'', replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 0"])
if ev is None:
raise Exception("Unsupported AES-WRAP len 0 not reported")
logger.debug("Invalid AES wrap data length 1")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'1', replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 1"])
if ev is None:
raise Exception("Unsupported AES-WRAP len 1 not reported")
logger.debug("Invalid AES wrap data length 9")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'123456789', replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 9"])
if ev is None:
raise Exception("Unsupported AES-WRAP len 9 not reported")
logger.debug("Invalid AES wrap data payload")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter)
# do not increment counter to test replay protection
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: AES unwrap failed"])
if ev is None:
raise Exception("AES unwrap failure not reported")
logger.debug("Replay Count not increasing")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: EAPOL-Key Replay Counter did not increase"])
if ev is None:
raise Exception("Replay Counter replay not reported")
logger.debug("Missing Ack bit in key info")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
key_info=0x134a)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: No Ack bit in key_info"])
if ev is None:
raise Exception("Missing Ack bit not reported")
logger.debug("Unexpected Request bit in key info")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
key_info=0x1bca)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: EAPOL-Key with Request bit"])
if ev is None:
raise Exception("Request bit not reported")
logger.debug("Unsupported key descriptor version 0")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13c8)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 0"])
if ev is None:
raise Exception("Unsupported EAPOL-Key descriptor version 0 not reported")
logger.debug("Key descriptor version 1 not allowed with CCMP")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13c9)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (1) is not 2"])
if ev is None:
raise Exception("Not allowed EAPOL-Key descriptor version not reported")
logger.debug("Invalid AES wrap payload with key descriptor version 2")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13ca)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: AES unwrap failed"])
if ev is None:
raise Exception("AES unwrap failure not reported")
logger.debug("Key descriptor version 3 workaround")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13cb)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (3) is not 2"])
if ev is None:
raise Exception("CCMP key descriptor mismatch not reported")
ev = dev[0].wait_event(["WPA: Interoperability workaround"])
if ev is None:
raise Exception("AES-128-CMAC workaround not reported")
ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key MIC - dropping packet"])
if ev is None:
raise Exception("MIC failure with AES-128-CMAC workaround not reported")
logger.debug("Unsupported key descriptor version 4")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13cc)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 4"])
if ev is None:
raise Exception("Unsupported EAPOL-Key descriptor version 4 not reported")
logger.debug("Unsupported key descriptor version 7")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
replay_counter=counter, key_info=0x13cf)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 7"])
if ev is None:
raise Exception("Unsupported EAPOL-Key descriptor version 7 not reported")
logger.debug("Too short EAPOL header length")
dev[0].dump_monitor()
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
extra_len=-1)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key frame - key_data overflow (8 > 7)"])
if ev is None:
raise Exception("Key data overflow not reported")
logger.debug("Too long EAPOL header length")
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
extra_len=1)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
logger.debug("Unsupported descriptor type 0")
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
descr_type=0)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
logger.debug("WPA descriptor type 0")
msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
descr_type=254)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
logger.debug("Non-zero key index for pairwise key")
dev[0].dump_monitor()
wrapped = aes_wrap(kek, 16*b'z')
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_info=0x13ea)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index"])
if ev is None:
raise Exception("Non-zero key index not reported")
logger.debug("Invalid Key Data plaintext payload --> disconnect")
dev[0].dump_monitor()
wrapped = aes_wrap(kek, 16*b'z')
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_no_ie(dev, apdev):
"""WPA2-PSK supplicant protocol testing: IE not included"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("No IEs in msg 3/4 --> disconnect")
dev[0].dump_monitor()
wrapped = aes_wrap(kek, 16*b'\x00')
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_ie_mismatch(dev, apdev):
"""WPA2-PSK supplicant protocol testing: IE mismatch"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Msg 3/4 with mismatching IE")
dev[0].dump_monitor()
wrapped = aes_wrap(kek, pad_key_data(binascii.unhexlify('30060100000fac04dd16000fac010100dc11188831bf4aa4a8678d2b41498618')))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_ok(dev, apdev):
"""WPA2-PSK supplicant protocol testing: success"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_connected(timeout=1)
def test_ap_wpa2_psk_supp_proto_no_gtk(dev, apdev):
"""WPA2-PSK supplicant protocol testing: no GTK"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("EAPOL-Key msg 3/4 without GTK KDE")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected connection completion reported")
def test_ap_wpa2_psk_supp_proto_anonce_change(dev, apdev):
"""WPA2-PSK supplicant protocol testing: ANonce change"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
anonce2 = binascii.unhexlify('3333333333333333333333333333333333333333333333333333333333333333')
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce2, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake"])
if ev is None:
raise Exception("ANonce change not reported")
def test_ap_wpa2_psk_supp_proto_unexpected_group_msg(dev, apdev):
"""WPA2-PSK supplicant protocol testing: unexpected group message"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Group key 1/2 instead of msg 3/4")
dev[0].dump_monitor()
wrapped = aes_wrap(kek, binascii.unhexlify('dd16000fac010100dc11188831bf4aa4a8678d2b41498618'))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_info=0x13c2)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Group Key Handshake started prior to completion of 4-way handshake"])
if ev is None:
raise Exception("Unexpected group key message not reported")
dev[0].wait_disconnected(timeout=1)
@remote_compatible
def test_ap_wpa2_psk_supp_proto_msg_1_invalid_kde(dev, apdev):
"""WPA2-PSK supplicant protocol testing: invalid KDE in msg 1/4"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4 with invalid KDE
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter,
key_data=binascii.unhexlify('5555'))
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_wrong_pairwise_key_len(dev, apdev):
"""WPA2-PSK supplicant protocol testing: wrong pairwise key length"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_len=15)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Invalid CCMP key length 15"])
if ev is None:
raise Exception("Invalid CCMP key length not reported")
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_wrong_group_key_len(dev, apdev):
"""WPA2-PSK supplicant protocol testing: wrong group key length"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd15000fac010100dc11188831bf4aa4a8678d2b414986')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 15"])
if ev is None:
raise Exception("Invalid CCMP key length not reported")
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_gtk_tx_bit_workaround(dev, apdev):
"""WPA2-PSK supplicant protocol testing: GTK TX bit workaround"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010500dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Tx bit set for GTK, but pairwise keys are used - ignore Tx bit"])
if ev is None:
raise Exception("GTK Tx bit workaround not reported")
dev[0].wait_connected(timeout=1)
def test_ap_wpa2_psk_supp_proto_gtk_keyidx_0_and_3(dev, apdev):
"""WPA2-PSK supplicant protocol testing: GTK key index 0 and 3"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_connected(timeout=1)
logger.debug("Valid EAPOL-Key group msg 1/2 (GTK keyidx 3)")
dev[0].dump_monitor()
plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_info=0x13c2)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
ev = dev[0].wait_event(["WPA: Group rekeying completed"])
if ev is None:
raise Exception("GTK rekeing not reported")
logger.debug("Unencrypted GTK KDE in group msg 1/2")
dev[0].dump_monitor()
plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
key_info=0x03c2)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
if ev is None:
raise Exception("Unencrypted GTK KDE not reported")
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_no_gtk_in_group_msg(dev, apdev):
"""WPA2-PSK supplicant protocol testing: GTK KDE missing from group msg"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_connected(timeout=1)
logger.debug("No GTK KDE in EAPOL-Key group msg 1/2")
dev[0].dump_monitor()
plain = binascii.unhexlify('dd00dd00dd00dd00dd00dd00dd00dd00')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_info=0x13c2)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: No GTK IE in Group Key msg 1/2"])
if ev is None:
raise Exception("Missing GTK KDE not reported")
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_too_long_gtk_in_group_msg(dev, apdev):
"""WPA2-PSK supplicant protocol testing: too long GTK KDE in group msg"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_connected(timeout=1)
logger.debug("EAPOL-Key group msg 1/2 with too long GTK KDE")
dev[0].dump_monitor()
plain = binascii.unhexlify('dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
key_info=0x13c2)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 33",
"RSN: Too long GTK in GTK KDE (len=33)"])
if ev is None:
raise Exception("Too long GTK KDE not reported")
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_too_long_gtk_kde(dev, apdev):
"""WPA2-PSK supplicant protocol testing: too long GTK KDE"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("EAPOL-Key msg 3/4 with too short GTK KDE")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
dev[0].wait_disconnected(timeout=1)
def test_ap_wpa2_psk_supp_proto_gtk_not_encrypted(dev, apdev):
"""WPA2-PSK supplicant protocol testing: GTK KDE not encrypted"""
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("Valid EAPOL-Key msg 3/4")
dev[0].dump_monitor()
plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
key_info=0x03ca)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
if ev is None:
raise Exception("Unencrypted GTK KDE not reported")
dev[0].wait_disconnected(timeout=1)
def run_psk_supp_proto_pmf2(dev, apdev, igtk_kde=None, fail=False):
(bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0],
ieee80211w=2)
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
msg = recv_eapol(hapd)
dev[0].dump_monitor()
# Build own EAPOL-Key msg 1/4
anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
counter = 1
msg = build_eapol_key_1_4(anonce, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
msg = recv_eapol(dev[0])
snonce = msg['rsn_key_nonce']
(ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
logger.debug("EAPOL-Key msg 3/4")
dev[0].dump_monitor()
gtk_kde = binascii.unhexlify('dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
plain = rsne + gtk_kde
if igtk_kde:
plain += igtk_kde
wrapped = aes_wrap(kek, pad_key_data(plain))
msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
counter += 1
send_eapol(dev[0], bssid, build_eapol(msg))
if fail:
dev[0].wait_disconnected(timeout=1)
return
dev[0].wait_connected(timeout=1)
# Verify that an unprotected broadcast Deauthentication frame is ignored
bssid = binascii.unhexlify(hapd.own_addr().replace(':', ''))
sock = start_monitor(apdev[1]["ifname"])
radiotap = radiotap_build()
frame = binascii.unhexlify("c0003a01")
frame += 6*b'\xff' + bssid + bssid
frame += binascii.unhexlify("1000" + "0300")
sock.send(radiotap + frame)
# And same with incorrect BIP protection
for keyid in ["0400", "0500", "0600", "0004", "0005", "0006", "ffff"]:
frame2 = frame + binascii.unhexlify("4c10" + keyid + "010000000000c0e5ca5f2b3b4de9")
sock.send(radiotap + frame2)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected disconnection")
def run_psk_supp_proto_pmf(dev, apdev, igtk_kde=None, fail=False):
try:
run_psk_supp_proto_pmf2(dev, apdev, igtk_kde=igtk_kde, fail=fail)
finally:
stop_monitor(apdev[1]["ifname"])
def test_ap_wpa2_psk_supp_proto_no_igtk(dev, apdev):
"""WPA2-PSK supplicant protocol testing: no IGTK KDE"""
run_psk_supp_proto_pmf(dev, apdev, igtk_kde=None)
def test_ap_wpa2_psk_supp_proto_igtk_ok(dev, apdev):
"""WPA2-PSK supplicant protocol testing: valid IGTK KDE"""
igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0400' + 6*'00' + 16*'77')
run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde)
def test_ap_wpa2_psk_supp_proto_igtk_keyid_swap(dev, apdev):
"""WPA2-PSK supplicant protocol testing: swapped IGTK KeyID"""
igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0004' + 6*'00' + 16*'77')
run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde)
def test_ap_wpa2_psk_supp_proto_igtk_keyid_too_large(dev, apdev):
"""WPA2-PSK supplicant protocol testing: too large IGTK KeyID"""
igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + 'ffff' + 6*'00' + 16*'77')
run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde, fail=True)
def test_ap_wpa2_psk_supp_proto_igtk_keyid_unexpected(dev, apdev):
"""WPA2-PSK supplicant protocol testing: unexpected IGTK KeyID"""
igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0006' + 6*'00' + 16*'77')
run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde, fail=True)
def find_wpas_process(dev):
ifname = dev.ifname
err, data = dev.cmd_execute(['ps', 'ax'])
for l in data.splitlines():
if "wpa_supplicant" not in l:
continue
if "-i" + ifname not in l:
continue
return int(l.strip().split(' ')[0])
raise Exception("Could not find wpa_supplicant process")
def read_process_memory(pid, key=None):
buf = bytes()
logger.info("Reading process memory (pid=%d)" % pid)
with open('/proc/%d/maps' % pid, 'r') as maps, \
open('/proc/%d/mem' % pid, 'rb') as mem:
for l in maps.readlines():
m = re.match(r'([0-9a-f]+)-([0-9a-f]+) ([-r][-w][-x][-p])', l)
if not m:
continue
start = int(m.group(1), 16)
end = int(m.group(2), 16)
perm = m.group(3)
if start > 0xffffffffffff:
continue
if end < start:
continue
if not perm.startswith('rw'):
continue
for name in ["[heap]", "[stack]"]:
if name in l:
logger.info("%s 0x%x-0x%x is at %d-%d" % (name, start, end, len(buf), len(buf) + (end - start)))
mem.seek(start)
data = mem.read(end - start)
buf += data
if key and key in data:
logger.info("Key found in " + l)
logger.info("Total process memory read: %d bytes" % len(buf))
return buf
def verify_not_present(buf, key, fname, keyname):
pos = buf.find(key)
if pos < 0:
return
prefix = 2048 if pos > 2048 else pos
with open(fname + keyname, 'wb') as f:
f.write(buf[pos - prefix:pos + 2048])
raise Exception(keyname + " found after disassociation")
def get_key_locations(buf, key, keyname):
count = 0
pos = 0
while True:
pos = buf.find(key, pos)
if pos < 0:
break
logger.info("Found %s at %d" % (keyname, pos))
context = 128
start = pos - context if pos > context else 0
before = binascii.hexlify(buf[start:pos])
context += len(key)
end = pos + context if pos < len(buf) - context else len(buf) - context
after = binascii.hexlify(buf[pos + len(key):end])
logger.debug("Memory context %d-%d: %s|%s|%s" % (start, end, before, binascii.hexlify(key), after))
count += 1
pos += len(key)
return count
def test_wpa2_psk_key_lifetime_in_memory(dev, apdev, params):
"""WPA2-PSK and PSK/PTK lifetime in memory"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
pmk = binascii.unhexlify(psk)
p = hostapd.wpa2_params(ssid=ssid)
p['wpa_psk'] = psk
hapd = hostapd.add_ap(apdev[0], p)
pid = find_wpas_process(dev[0])
id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
only_add_network=True)
logger.info("Checking keys in memory after network profile configuration")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
dev[0].request("REMOVE_NETWORK all")
logger.info("Checking keys in memory after network profile removal")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
only_add_network=True)
logger.info("Checking keys in memory before connection")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
dev[0].connect_network(id, timeout=20)
# The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
# event has been delivered, so verify that wpa_supplicant has returned to
# eloop before reading process memory.
time.sleep(1)
dev[0].ping()
buf = read_process_memory(pid, pmk)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].relog()
ptk = None
gtk = None
with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
for l in f.readlines():
if "WPA: PTK - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
ptk = binascii.unhexlify(val)
if "WPA: Group Key - hexdump" in l:
val = l.strip().split(':')[3].replace(' ', '')
gtk = binascii.unhexlify(val)
if not pmk or not ptk or not gtk:
raise Exception("Could not find keys from debug log")
if len(gtk) != 16:
raise Exception("Unexpected GTK length")
kck = ptk[0:16]
kek = ptk[16:32]
tk = ptk[32:48]
logger.info("Checking keys in memory while associated")
get_key_locations(buf, pmk, "PMK")
if pmk not in buf:
raise HwsimSkip("PMK not found while associated")
if kck not in buf:
raise Exception("KCK not found while associated")
if kek not in buf:
raise Exception("KEK not found while associated")
#if tk in buf:
# raise Exception("TK found from memory")
logger.info("Checking keys in memory after disassociation")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
# Note: PMK/PSK is still present in network configuration
fname = os.path.join(params['logdir'],
'wpa2_psk_key_lifetime_in_memory.memctx-')
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
if gtk in buf:
get_key_locations(buf, gtk, "GTK")
verify_not_present(buf, gtk, fname, "GTK")
dev[0].request("REMOVE_NETWORK all")
logger.info("Checking keys in memory after network profile removal")
buf = read_process_memory(pid, pmk)
get_key_locations(buf, pmk, "PMK")
verify_not_present(buf, pmk, fname, "PMK")
verify_not_present(buf, kck, fname, "KCK")
verify_not_present(buf, kek, fname, "KEK")
verify_not_present(buf, tk, fname, "TK")
verify_not_present(buf, gtk, fname, "GTK")
@remote_compatible
def test_ap_wpa2_psk_wep(dev, apdev):
"""WPA2-PSK AP and WEP enabled"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
try:
hapd.set('wep_key0', '"hello"')
raise Exception("WEP key accepted to WPA2 network")
except Exception:
pass
def test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
"""WPA2-PSK AP and wpas interface in a bridge"""
br_ifname = 'sta-br0'
ifname = 'wlan5'
try:
_test_ap_wpa2_psk_wpas_in_bridge(dev, apdev)
finally:
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
subprocess.call(['brctl', 'delif', br_ifname, ifname])
subprocess.call(['brctl', 'delbr', br_ifname])
subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
def _test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
br_ifname = 'sta-br0'
ifname = 'wlan5'
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
subprocess.call(['brctl', 'addbr', br_ifname])
subprocess.call(['brctl', 'setfd', br_ifname, '0'])
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
wpas.interface_add(ifname, br_ifname=br_ifname)
wpas.dump_monitor()
wpas.connect(ssid, psk=passphrase, scan_freq="2412")
wpas.dump_monitor()
@remote_compatible
def test_ap_wpa2_psk_ifdown(dev, apdev):
"""AP with open mode and external ifconfig down"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=10)
if ev is None:
raise Exception("No INTERFACE-DISABLED event")
# this wait tests beacon loss detection in mac80211
dev[0].wait_disconnected()
hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
if ev is None:
raise Exception("No INTERFACE-ENABLED event")
dev[0].wait_connected()
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_drop_first_msg_4(dev, apdev):
"""WPA2-PSK and first EAPOL-Key msg 4/4 dropped"""
hapd = setup_psk_ext(dev[0], apdev[0])
bssid = apdev[0]['bssid']
addr = dev[0].own_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
logger.info("Drop the first EAPOL-Key msg 4/4")
# wpa_supplicant believes now that 4-way handshake succeeded; hostapd
# doesn't. Use normal EAPOL TX/RX to handle retries.
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
dev[0].wait_connected()
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
if ev is not None:
logger.info("Disconnection detected")
# The EAPOL-Key retries are supposed to allow the connection to be
# established without having to reassociate. However, this does not
# currently work since mac80211 ends up encrypting EAPOL-Key msg 4/4
# after the pairwise key has been configured and AP will drop those and
# disconnect the station after reaching retransmission limit. Connection
# is then established after reassociation. Once that behavior has been
# optimized to prevent EAPOL-Key frame encryption for retransmission
# case, this exception can be uncommented here.
#raise Exception("Unexpected disconnection")
@remote_compatible
def test_ap_wpa2_psk_disable_enable(dev, apdev):
"""WPA2-PSK AP getting disabled and re-enabled"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
for i in range(2):
hapd.request("DISABLE")
dev[0].wait_disconnected()
hapd.request("ENABLE")
dev[0].wait_connected()
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
@remote_compatible
def test_ap_wpa2_psk_incorrect_passphrase(dev, apdev):
"""WPA2-PSK AP and station using incorrect passphrase"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk="incorrect passphrase", scan_freq="2412",
wait_connect=False)
ev = hapd.wait_event(["AP-STA-POSSIBLE-PSK-MISMATCH"], timeout=10)
if ev is None:
raise Exception("No AP-STA-POSSIBLE-PSK-MISMATCH reported")
dev[0].dump_monitor()
hapd.disable()
hapd.set("wpa_passphrase", "incorrect passphrase")
hapd.enable()
dev[0].wait_connected(timeout=20)
@remote_compatible
def test_ap_wpa_ie_parsing(dev, apdev):
"""WPA IE parsing"""
skip_with_fips(dev[0])
skip_without_tkip(dev[0])
ssid = "test-wpa-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
only_add_network=True)
tests = ["dd040050f201",
"dd050050f20101",
"dd060050f2010100",
"dd060050f2010001",
"dd070050f201010000",
"dd080050f20101000050",
"dd090050f20101000050f2",
"dd0a0050f20101000050f202",
"dd0b0050f20101000050f20201",
"dd0c0050f20101000050f2020100",
"dd0c0050f20101000050f2020000",
"dd0c0050f20101000050f202ffff",
"dd0d0050f20101000050f202010000",
"dd0e0050f20101000050f20201000050",
"dd0f0050f20101000050f20201000050f2",
"dd100050f20101000050f20201000050f202",
"dd110050f20101000050f20201000050f20201",
"dd120050f20101000050f20201000050f2020100",
"dd120050f20101000050f20201000050f2020000",
"dd120050f20101000050f20201000050f202ffff",
"dd130050f20101000050f20201000050f202010000",
"dd140050f20101000050f20201000050f20201000050",
"dd150050f20101000050f20201000050f20201000050f2"]
for t in tests:
try:
if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
raise Exception("VENDOR_ELEM_ADD failed")
dev[0].select_network(id)
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
if ev is None:
raise Exception("Association rejection not reported")
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
finally:
dev[0].request("VENDOR_ELEM_REMOVE 13 *")
tests = ["dd170050f20101000050f20201000050f20201000050f202ff",
"dd180050f20101000050f20201000050f20201000050f202ffff",
"dd190050f20101000050f20201000050f20201000050f202ffffff"]
for t in tests:
try:
if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
raise Exception("VENDOR_ELEM_ADD failed")
dev[0].select_network(id)
ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED',
'WPA: 4-Way Handshake failed'], timeout=10)
if ev is None:
raise Exception("Association failed unexpectedly")
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
finally:
dev[0].request("VENDOR_ELEM_REMOVE 13 *")
@remote_compatible
def test_ap_wpa2_psk_no_random(dev, apdev):
"""WPA2-PSK AP and no random numbers available"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
hapd = hostapd.add_ap(apdev[0], params)
with fail_test(hapd, 1, "wpa_gmk_to_gtk"):
id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=15)
if ev is None:
raise Exception("Disconnection event not reported")
dev[0].request("DISCONNECT")
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
@remote_compatible
def test_rsn_ie_proto_psk_sta(dev, apdev):
"""RSN element protocol testing for PSK cases on STA side"""
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
# This is the RSN element used normally by hostapd
params['own_ie_override'] = '30140100000fac040100000fac040100000fac020c00'
hapd = hostapd.add_ap(apdev[0], params)
if "FAIL" not in hapd.request("SET own_ie_override qwerty"):
raise Exception("Invalid own_ie_override value accepted")
id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
tests = [('No RSN Capabilities field',
'30120100000fac040100000fac040100000fac02'),
('Reserved RSN Capabilities bits set',
'30140100000fac040100000fac040100000fac023cff'),
('Truncated RSN Capabilities field',
'30130100000fac040100000fac040100000fac023c'),
('Extra pairwise cipher suite (unsupported)',
'30180100000fac040200ffffffff000fac040100000fac020c00'),
('Extra AKM suite (unsupported)',
'30180100000fac040100000fac040200ffffffff000fac020c00'),
('PMKIDCount field included',
'30160100000fac040100000fac040100000fac020c000000'),
('Truncated PMKIDCount field',
'30150100000fac040100000fac040100000fac020c0000'),
('Unexpected Group Management Cipher Suite with PMF disabled',
'301a0100000fac040100000fac040100000fac020c000000000fac06'),
('Extra octet after defined fields (future extensibility)',
'301b0100000fac040100000fac040100000fac020c000000000fac0600')]
for txt, ie in tests:
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].request("NOTE " + txt)
logger.info(txt)
hapd.disable()
hapd.set('own_ie_override', ie)
hapd.enable()
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
@remote_compatible
def test_ap_cli_order(dev, apdev):
"""hostapd configuration parameter SET ordering"""
ssid = "test-rsn-setup"
passphrase = 'zzzzzzzz'
hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
hapd.set('ssid', ssid)
hapd.set('wpa_passphrase', passphrase)
hapd.set('rsn_pairwise', 'CCMP')
hapd.set('wpa_key_mgmt', 'WPA-PSK')
hapd.set('wpa', '2')
hapd.enable()
cfg = hapd.get_config()
if cfg['group_cipher'] != 'CCMP':
raise Exception("Unexpected group_cipher: " + cfg['group_cipher'])
if cfg['rsn_pairwise_cipher'] != 'CCMP':
raise Exception("Unexpected rsn_pairwise_cipher: " + cfg['rsn_pairwise_cipher'])
ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
if ev is None:
raise Exception("AP startup timed out")
if "AP-ENABLED" not in ev:
raise Exception("AP startup failed")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
def set_test_assoc_ie(dev, ie):
if "OK" not in dev.request("TEST_ASSOC_IE " + ie):
raise Exception("Could not set TEST_ASSOC_IE")
@remote_compatible
def test_ap_wpa2_psk_assoc_rsn(dev, apdev):
"""WPA2-PSK AP and association request RSN IE differences"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
tests = [("Normal wpa_supplicant assoc req RSN IE",
"30140100000fac040100000fac040100000fac020000"),
("RSN IE without RSN Capabilities",
"30120100000fac040100000fac040100000fac02")]
for title, ie in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [("WPA IE instead of RSN IE and only RSN enabled on AP",
"dd160050f20101000050f20201000050f20201000050f202", 40),
("Empty RSN IE", "3000", 40),
("RSN IE with truncated Version", "300101", 40),
("RSN IE with only Version", "30020100", 43)]
for title, ie, status in tests:
logger.info(title)
set_test_assoc_ie(dev[0], ie)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
if ev is None:
raise Exception("Association rejection not reported")
if "status_code=" + str(status) not in ev:
raise Exception("Unexpected status code: " + ev)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_ap_wpa2_psk_ft_workaround(dev, apdev):
"""WPA2-PSK+FT AP and workaround for incorrect STA behavior"""
ssid = "test-wpa2-psk-ft"
passphrase = 'qwertyuiop'
params = {"wpa": "2",
"wpa_key_mgmt": "FT-PSK WPA-PSK",
"rsn_pairwise": "CCMP",
"ssid": ssid,
"wpa_passphrase": passphrase}
params["mobility_domain"] = "a1b2"
params["r0_key_lifetime"] = "10000"
params["pmk_r1_push"] = "1"
params["reassociation_deadline"] = "1000"
params['nas_identifier'] = "nas1.w1.fi"
params['r1_key_holder'] = "000102030405"
hapd = hostapd.add_ap(apdev[0], params)
# Include both WPA-PSK and FT-PSK AKMs in Association Request frame
set_test_assoc_ie(dev[0],
"30180100000fac040100000fac040200000fac02000fac040000")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa2_psk_assoc_rsn_pmkid(dev, apdev):
"""WPA2-PSK AP and association request RSN IE with PMKID"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
set_test_assoc_ie(dev[0], "30260100000fac040100000fac040100000fac0200000100" + 16*'00')
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wpa_psk_rsn_pairwise(dev, apdev):
"""WPA-PSK AP and only rsn_pairwise set"""
skip_without_tkip(dev[0])
params = {"ssid": "wpapsk", "wpa": "1", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "TKIP", "wpa_passphrase": "1234567890"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("wpapsk", psk="1234567890", proto="WPA", pairwise="TKIP",
scan_freq="2412")
def test_ap_wpa2_eapol_retry_limit(dev, apdev):
"""WPA2-PSK EAPOL-Key retry limit configuration"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_ptk_rekey'] = '2'
params['wpa_group_update_count'] = '1'
params['wpa_pairwise_update_count'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
if "FAIL" not in hapd.request("SET wpa_group_update_count 0"):
raise Exception("Invalid wpa_group_update_count value accepted")
if "FAIL" not in hapd.request("SET wpa_pairwise_update_count 0"):
raise Exception("Invalid wpa_pairwise_update_count value accepted")
def test_ap_wpa2_disable_eapol_retry(dev, apdev):
"""WPA2-PSK disable EAPOL-Key retry"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_disable_eapol_key_retries'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
logger.info("Verify working 4-way handshake without retries")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
addr = dev[0].own_addr()
logger.info("Verify no retransmission of message 3/4")
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("Timeout on EAPOL-TX (M1) from hostapd")
ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("Timeout on EAPOL-TX (M1 retry) from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX (M1) to wpa_supplicant failed")
ev = dev[0].wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("Timeout on EAPOL-TX (M2) from wpa_supplicant")
dev[0].dump_monitor()
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX (M2) to hostapd failed")
ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("Timeout on EAPOL-TX (M3) from hostapd")
ev = hapd.wait_event(["EAPOL-TX"], timeout=2)
if ev is not None:
raise Exception("Unexpected EAPOL-TX M3 retry from hostapd")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
if ev is None:
raise Exception("Disconnection not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_ap_wpa2_disable_eapol_retry_group(dev, apdev):
"""WPA2-PSK disable EAPOL-Key retry for group handshake"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_disable_eapol_key_retries'] = '1'
params['wpa_strict_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
id = dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
dev[0].dump_monitor()
addr = dev[0].own_addr()
dev[1].request("DISCONNECT")
dev[1].wait_disconnected()
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
dev[1].request("RECONNECT")
dev[1].wait_connected()
hapd.wait_sta()
dev[0].dump_monitor()
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
dev[1].request("DISCONNECT")
ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("Timeout on EAPOL-TX (group M1) from hostapd")
ev = hapd.wait_event(["EAPOL-TX"], timeout=2)
if ev is not None:
raise Exception("Unexpected EAPOL-TX group M1 retry from hostapd")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
if ev is None:
raise Exception("Disconnection not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_ap_wpa2_psk_mic_0(dev, apdev):
"""WPA2-PSK/TKIP and MIC=0 in EAPOL-Key msg 3/4"""
skip_without_tkip(dev[0])
bssid = apdev[0]['bssid']
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['rsn_pairwise'] = "TKIP"
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
addr = dev[0].own_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
dev[0].dump_monitor()
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
msg3 = ev.split(' ')[2]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 4/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
# Do not send to the AP
# EAPOL-Key msg 3/4 with MIC=0 and modifications
eapol_hdr = msg3[0:8]
key_type = msg3[8:10]
key_info = msg3[10:14]
key_length = msg3[14:18]
replay_counter = msg3[18:34]
key_nonce = msg3[34:98]
key_iv = msg3[98:130]
key_rsc = msg3[130:146]
key_id = msg3[146:162]
key_mic = msg3[162:194]
key_data_len = msg3[194:198]
key_data = msg3[198:]
msg3b = eapol_hdr + key_type
msg3b += "12c9" # Clear MIC bit from key_info (originally 13c9)
msg3b += key_length
msg3b += '0000000000000003'
msg3b += key_nonce + key_iv + key_rsc + key_id
msg3b += 32*'0' # Clear MIC value
msg3b += key_data_len + key_data
dev[0].dump_monitor()
res = dev[0].request("EAPOL_RX " + bssid + " " + msg3b)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
ev = dev[0].wait_event(["EAPOL-TX", "WPA: Ignore EAPOL-Key"], timeout=2)
if ev is None:
raise Exception("No event from wpa_supplicant")
if "EAPOL-TX" in ev:
raise Exception("Unexpected EAPOL-Key message from wpa_supplicant")
dev[0].request("DISCONNECT")
def test_ap_wpa2_psk_local_error(dev, apdev):
"""WPA2-PSK and local error cases on supplicant"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "WPA-PSK WPA-PSK-SHA256"
hapd = hostapd.add_ap(apdev[0], params)
with fail_test(dev[0], 1, "sha1_prf;wpa_pmk_to_ptk"):
id = dev[0].connect(ssid, key_mgmt="WPA-PSK", psk=passphrase,
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
if ev is None:
raise Exception("Disconnection event not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
with fail_test(dev[0], 1, "sha256_prf;wpa_pmk_to_ptk"):
id = dev[0].connect(ssid, key_mgmt="WPA-PSK-SHA256", psk=passphrase,
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
if ev is None:
raise Exception("Disconnection event not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_ap_wpa2_psk_inject_assoc(dev, apdev, params):
"""WPA2-PSK AP and Authentication and Association Request frame injection"""
prefix = "ap_wpa2_psk_inject_assoc"
ifname = apdev[0]["ifname"]
cap = os.path.join(params['logdir'], prefix + "." + ifname + ".pcap")
ssid = "test"
params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
params["wpa_key_mgmt"] = "WPA-PSK"
hapd = hostapd.add_ap(apdev[0], params)
wt = WlantestCapture(ifname, cap)
time.sleep(1)
bssid = hapd.own_addr().replace(':', '')
hapd.request("SET ext_mgmt_frame_handling 1")
addr = "021122334455"
auth = "b0003a01" + bssid + addr + bssid + '1000000001000000'
res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth)
if "OK" not in res:
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
ev = ev.replace("ok=0", "ok=1")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
assoc = "00003a01" + bssid + addr + bssid + '2000' + '31040500' + '000474657374' + '010802040b160c121824' + '30140100000fac040100000fac040100000fac020000'
res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc)
if "OK" not in res:
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
ev = ev.replace("ok=0", "ok=1")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
hapd.request("SET ext_mgmt_frame_handling 0")
dev[0].connect(ssid, psk="12345678", scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
time.sleep(1)
hwsim_utils.test_connectivity(dev[0], hapd)
time.sleep(0.5)
wt.close()
time.sleep(0.5)
# Check for Layer 2 Update frame and unexpected frames from the station
# that did not fully complete authentication.
res = run_tshark(cap, "basicxid.llc.xid.format == 0x81",
["eth.src"], wait=False)
real_sta_seen = False
unexpected_sta_seen = False
real_addr = dev[0].own_addr()
for l in res.splitlines():
if l == real_addr:
real_sta_seen = True
else:
unexpected_sta_seen = True
if unexpected_sta_seen:
raise Exception("Layer 2 Update frame from unexpected STA seen")
if not real_sta_seen:
raise Exception("Layer 2 Update frame from real STA not seen")
res = run_tshark(cap, "eth.src == 02:11:22:33:44:55", ["eth.src"],
wait=False)
if len(res) > 0:
raise Exception("Unexpected frame from unauthorized STA seen")
def test_ap_wpa2_psk_no_control_port(dev, apdev):
"""WPA2-PSK AP without nl80211 control port"""
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['driver_params'] = "control_port=0"
hapd = hostapd.add_ap(apdev[0], params)
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="control_port=0")
wpas.connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(wpas, hapd)
if "OK" not in wpas.request("KEY_REQUEST 0 1"):
raise Exception("KEY_REQUEST failed")
ev = wpas.wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hapd.wait_ptkinitdone(wpas.own_addr())
hwsim_utils.test_connectivity(wpas, hapd)
wpas.request("DISCONNECT")
wpas.wait_disconnected()
wpas.dump_monitor()
def test_ap_wpa2_psk_ap_control_port(dev, apdev):
"""WPA2-PSK AP with nl80211 control port in AP mode"""
run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val=1)
def test_ap_wpa2_psk_ap_control_port_disabled(dev, apdev):
"""WPA2-PSK AP with nl80211 control port in AP mode disabled"""
run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val=0)
def run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val):
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['driver_params'] = "control_port_ap=%d" % ctrl_val
hapd = hostapd.add_ap(apdev[0], params)
flags = hapd.request("DRIVER_FLAGS").splitlines()[1:]
flags2 = hapd.request("DRIVER_FLAGS2").splitlines()[1:]
logger.info("AP driver flags: " + str(flags))
logger.info("AP driver flags2: " + str(flags2))
if 'CONTROL_PORT' not in flags or 'CONTROL_PORT_RX' not in flags2:
raise HwsimSkip("No AP driver support for CONTROL_PORT")
flags = dev[0].request("DRIVER_FLAGS").splitlines()[1:]
flags2 = dev[0].request("DRIVER_FLAGS2").splitlines()[1:]
logger.info("STA driver flags: " + str(flags))
logger.info("STA driver flags2: " + str(flags2))
if 'CONTROL_PORT' not in flags or 'CONTROL_PORT_RX' not in flags2:
raise HwsimSkip("No STA driver support for CONTROL_PORT")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
if "OK" not in dev[0].request("KEY_REQUEST 0 1"):
raise Exception("KEY_REQUEST failed")
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
hapd.wait_ptkinitdone(dev[0].own_addr())
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev):
"""RSNE mismatch in EAPOL-Key msg 3/4"""
ie = "30140100000fac040100000fac040100000fac020c80"
run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, ie)
def test_ap_wpa2_psk_rsne_mismatch_ap2(dev, apdev):
"""RSNE mismatch in EAPOL-Key msg 3/4"""
ie = "30150100000fac040100000fac040100000fac020c0000"
run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, ie)
def test_ap_wpa2_psk_rsne_mismatch_ap3(dev, apdev):
"""RSNE mismatch in EAPOL-Key msg 3/4"""
run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, "")
def run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, rsne):
params = hostapd.wpa2_params(ssid="psk", passphrase="12345678")
params['rsne_override_eapol'] = rsne
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("psk", psk="12345678", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["Associated with"], timeout=10)
if ev is None:
raise Exception("No indication of association seen")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=5)
dev[0].request("REMOVE_NETWORK all")
if ev is None:
raise Exception("No disconnection seen")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Unexpected connection")
if "reason=17 locally_generated=1" not in ev:
raise Exception("Unexpected disconnection reason: " + ev)
def test_ap_wpa2_psk_rsnxe_mismatch_ap(dev, apdev):
"""RSNXE mismatch in EAPOL-Key msg 3/4"""
params = hostapd.wpa2_params(ssid="psk", passphrase="12345678")
params['rsnxe_override_eapol'] = "F40100"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("psk", psk="12345678", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["Associated with"], timeout=10)
if ev is None:
raise Exception("No indication of association seen")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=5)
dev[0].request("REMOVE_NETWORK all")
if ev is None:
raise Exception("No disconnection seen")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Unexpected connection")
if "reason=17 locally_generated=1" not in ev:
raise Exception("Unexpected disconnection reason: " + ev)
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap0(dev, apdev):
"""WPA2-PSK AP and PTK rekey by AP (disabled on STA)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 1, 0)
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap1(dev, apdev):
"""WPA2-PSK AP and PTK rekey by AP (start with Key ID 0)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 1, 1)
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap2(dev, apdev):
"""WPA2-PSK AP and PTK rekey by AP (start with Key ID 1)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 2, 1)
def run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, ap_ext_key_id,
sta_ext_key_id):
check_ext_key_id_capa(dev[0])
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['wpa_ptk_rekey'] = '2'
params['extended_key_id'] = str(ap_ext_key_id)
hapd = hostapd.add_ap(apdev[0], params)
check_ext_key_id_capa(hapd)
try:
dev[0].set("extended_key_id", str(sta_ext_key_id))
dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
idx = int(dev[0].request("GET last_tk_key_idx"))
expect_idx = 1 if ap_ext_key_id == 2 and sta_ext_key_id else 0
if idx != expect_idx:
raise Exception("Unexpected Key ID for the first TK: %d (expected %d)" % (idx, expect_idx))
ev = dev[0].wait_event(["WPA: Key negotiation completed"])
if ev is None:
raise Exception("PTK rekey timed out")
idx = int(dev[0].request("GET last_tk_key_idx"))
expect_idx = 1 if ap_ext_key_id == 1 and sta_ext_key_id else 0
if idx != expect_idx:
raise Exception("Unexpected Key ID for the second TK: %d (expected %d)" % (idx, expect_idx))
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].set("extended_key_id", "0")
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta0(dev, apdev):
"""Extended Key ID and PTK rekey by station (Ext Key ID disabled on AP)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 0)
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta1(dev, apdev):
"""Extended Key ID and PTK rekey by station (start with Key ID 0)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 1)
def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta2(dev, apdev):
"""Extended Key ID and PTK rekey by station (start with Key ID 1)"""
run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 2)
def run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, ext_key_id):
check_ext_key_id_capa(dev[0])
ssid = "test-wpa2-psk"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params['extended_key_id'] = str(ext_key_id)
hapd = hostapd.add_ap(apdev[0], params)
check_ext_key_id_capa(hapd)
Wlantest.setup(hapd)
wt = Wlantest()
wt.flush()
wt.add_passphrase(passphrase)
try:
dev[0].set("extended_key_id", "1")
dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1",
scan_freq="2412")
idx = int(dev[0].request("GET last_tk_key_idx"))
expect_idx = 1 if ext_key_id == 2 else 0
if idx != expect_idx:
raise Exception("Unexpected Key ID for the first TK: %d (expected %d)" % (idx, expect_idx))
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"])
if ev is None:
raise Exception("PTK rekey timed out")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Disconnect instead of rekey")
idx = int(dev[0].request("GET last_tk_key_idx"))
expect_idx = 1 if ext_key_id == 1 else 0
if idx != expect_idx:
raise Exception("Unexpected Key ID for the second TK: %d (expected %d)" % (idx, expect_idx))
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].set("extended_key_id", "0")
diff --git a/tests/hwsim/test_ap_track.py b/tests/hwsim/test_ap_track.py
index 9d9e2cd460ee..ba8f3eb252cd 100644
--- a/tests/hwsim/test_ap_track.py
+++ b/tests/hwsim/test_ap_track.py
@@ -1,405 +1,437 @@
# Test cases for hostapd tracking unconnected stations
# 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 logging
logger = logging.getLogger()
import subprocess
import time
import hostapd
from wpasupplicant import WpaSupplicant
from utils import parse_ie, disable_hapd, clear_regdom_dev
def test_ap_track_sta(dev, apdev):
"""Dualband AP tracking unconnected stations"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"track_sta_max_num": "2"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"track_sta_max_num": "100",
"track_sta_max_age": "1"}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta(dev, hapd, apdev[0]['bssid'], hapd2,
apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev, 3)
def _test_ap_track_sta(dev, hapd, bssid, hapd2, bssid2):
for i in range(2):
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[2].scan_for_bss(bssid2, freq=5200, force_scan=True)
addr0 = dev[0].own_addr()
addr1 = dev[1].own_addr()
addr2 = dev[2].own_addr()
track = hapd.request("TRACK_STA_LIST")
if addr0 not in track or addr1 not in track:
raise Exception("Station missing from 2.4 GHz tracking")
if addr2 in track:
raise Exception("Unexpected station included in 2.4 GHz tracking")
track = hapd2.request("TRACK_STA_LIST")
if addr0 not in track or addr2 not in track:
raise Exception("Station missing from 5 GHz tracking")
if addr1 in track:
raise Exception("Unexpected station included in 5 GHz tracking")
# Test expiration
time.sleep(1.1)
track = hapd.request("TRACK_STA_LIST")
if addr0 not in track or addr1 not in track:
raise Exception("Station missing from 2.4 GHz tracking (expiration)")
track = hapd2.request("TRACK_STA_LIST")
if addr0 in track or addr2 in track:
raise Exception("Station not expired from 5 GHz tracking")
# Test maximum list length
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[2].scan_for_bss(bssid, freq=2437, force_scan=True)
track = hapd.request("TRACK_STA_LIST")
if len(track.splitlines()) != 2:
raise Exception("Unexpected number of entries: %d" % len(track.splitlines()))
if addr1 not in track or addr2 not in track:
raise Exception("Station missing from 2.4 GHz tracking (max limit)")
def test_ap_track_sta_no_probe_resp(dev, apdev):
"""Dualband AP not replying to probes from dualband STA on 2.4 GHz"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"beacon_int": "10000",
"no_probe_resp_if_seen_on": apdev[1]['ifname']}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"track_sta_max_num": "100"}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta_no_probe_resp(dev, apdev[0]['bssid'],
apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev, 2)
def _test_ap_track_sta_no_probe_resp(dev, bssid, bssid2):
dev[0].flush_scan_cache()
dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[0].scan(freq=2437, type="ONLY")
dev[0].scan(freq=2437, type="ONLY")
bss = dev[0].get_bss(bssid)
if bss:
ie = parse_ie(bss['ie'])
# Check whether this is from a Beacon frame (TIM element included) since
# it is possible that a Beacon frame was received during the active
# scan. This test should fail only if a Probe Response frame was
# received.
if 5 not in ie:
raise Exception("2.4 GHz AP found unexpectedly")
def test_ap_track_sta_no_auth(dev, apdev):
"""Dualband AP rejecting authentication from dualband STA on 2.4 GHz"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"track_sta_max_num": "100",
"no_auth_if_seen_on": apdev[1]['ifname']}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"track_sta_max_num": "100"}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta_no_auth(dev, apdev[0]['bssid'], apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev, 2)
def _test_ap_track_sta_no_auth(dev, bssid, bssid2):
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[1].connect("track", key_mgmt="NONE", scan_freq="2437")
dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
freq_list="2437", wait_connect=False)
dev[1].request("DISCONNECT")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-AUTH-REJECT"], timeout=10)
if ev is None:
raise Exception("Unknown connection result")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "status_code=82" not in ev:
raise Exception("Unexpected rejection reason: " + ev)
if "ie=34" not in ev:
raise Exception("No Neighbor Report element: " + ev)
dev[0].request("DISCONNECT")
def test_ap_track_sta_no_auth_passive(dev, apdev):
"""AP rejecting authentication from dualband STA on 2.4 GHz (passive)"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"no_auth_if_seen_on": apdev[1]['ifname']}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"interworking": "1",
"venue_name": "eng:Venue",
"track_sta_max_num": "100"}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta_no_auth_passive(dev, apdev[0]['bssid'],
apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev)
def _test_ap_track_sta_no_auth_passive(dev, bssid, bssid2):
dev[0].flush_scan_cache()
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
for i in range(10):
dev[0].request("SCAN freq=5200 passive=1")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
if ev is None:
raise Exception("Scan did not complete")
if dev[0].get_bss(bssid2):
break
if i == 9:
raise Exception("AP not found with passive scans")
if "OK" not in dev[0].request("ANQP_GET " + bssid2 + " 258"):
raise Exception("ANQP_GET command failed")
ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
if ev is None or "Venue Name" not in ev:
raise Exception("Did not receive Venue Name")
dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
freq_list="2437", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-AUTH-REJECT"], timeout=10)
if ev is None:
raise Exception("Unknown connection result")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "status_code=82" not in ev:
raise Exception("Unexpected rejection reason: " + ev)
dev[0].request("DISCONNECT")
def test_ap_track_sta_force_5ghz(dev, apdev):
"""Dualband AP forcing dualband STA to connect on 5 GHz"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"no_probe_resp_if_seen_on": apdev[1]['ifname'],
"no_auth_if_seen_on": apdev[1]['ifname']}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"track_sta_max_num": "100"}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta_force_5ghz(dev, apdev[0]['bssid'], apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev)
def _test_ap_track_sta_force_5ghz(dev, bssid, bssid2):
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
freq = dev[0].get_status_field('freq')
if freq != '5200':
raise Exception("Unexpected operating channel")
dev[0].request("DISCONNECT")
def test_ap_track_sta_force_2ghz(dev, apdev):
"""Dualband AP forcing dualband STA to connect on 2.4 GHz"""
try:
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"track_sta_max_num": "100"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "a",
"channel": "40",
"no_probe_resp_if_seen_on": apdev[0]['ifname'],
"no_auth_if_seen_on": apdev[0]['ifname']}
hapd2 = hostapd.add_ap(apdev[1], params)
_test_ap_track_sta_force_2ghz(dev, apdev[0]['bssid'], apdev[1]['bssid'])
finally:
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev)
def _test_ap_track_sta_force_2ghz(dev, bssid, bssid2):
dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
freq = dev[0].get_status_field('freq')
if freq != '2437':
raise Exception("Unexpected operating channel")
dev[0].request("DISCONNECT")
def test_ap_track_taxonomy(dev, apdev):
"""AP tracking STA taxonomy"""
try:
_test_ap_track_taxonomy(dev, apdev)
finally:
dev[1].request("SET p2p_disabled 0")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def _test_ap_track_taxonomy(dev, apdev):
params = {"ssid": "track",
"country_code": "US",
"hw_mode": "g",
"channel": "6",
"track_sta_max_num": "2"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
addr0 = dev[0].own_addr()
dev[0].connect("track", key_mgmt="NONE", scan_freq="2437")
dev[1].request("SET p2p_disabled 1")
dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
addr1 = dev[1].own_addr()
dev[1].connect("track", key_mgmt="NONE", scan_freq="2437")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.request("SET model_name track test")
wpas.scan_for_bss(bssid, freq=2437, force_scan=True)
addr = wpas.own_addr()
wpas.connect("track", key_mgmt="NONE", scan_freq="2437")
if "FAIL" not in hapd.request("SIGNATURE abc"):
raise Exception("SIGNATURE failure not reported (1)")
if "FAIL" not in hapd.request("SIGNATURE 22:33:44:55:66:77"):
raise Exception("SIGNATURE failure not reported (2)")
res = hapd.request("SIGNATURE " + addr0)
logger.info("sta0: " + res)
if not res.startswith("wifi4|probe:"):
raise Exception("Unexpected SIGNATURE prefix")
if "|assoc:" not in res:
raise Exception("Missing assoc info in SIGNATURE")
if "wps:track_test" in res:
raise Exception("Unexpected WPS model name")
res = hapd.request("SIGNATURE " + addr1)
logger.info("sta1: " + res)
if not res.startswith("wifi4|probe:"):
raise Exception("Unexpected SIGNATURE prefix")
if "|assoc:" not in res:
raise Exception("Missing assoc info in SIGNATURE")
if "wps:" in res:
raise Exception("Unexpected WPS info")
if ",221(0050f2,4)," in res:
raise Exception("Unexpected WPS IE info")
if ",221(506f9a,9)," in res:
raise Exception("Unexpected P2P IE info")
res = hapd.request("SIGNATURE " + addr)
logger.info("sta: " + res)
if not res.startswith("wifi4|probe:"):
raise Exception("Unexpected SIGNATURE prefix")
if "|assoc:" not in res:
raise Exception("Missing assoc info in SIGNATURE")
if "wps:track_test" not in res:
raise Exception("Missing WPS model name")
if ",221(0050f2,4)," not in res:
raise Exception("Missing WPS IE info")
if ",221(506f9a,9)," not in res:
raise Exception("Missing P2P IE info")
addr2 = dev[2].own_addr()
res = hapd.request("SIGNATURE " + addr2)
if "FAIL" not in res:
raise Exception("Unexpected SIGNATURE success for sta2 (1)")
for i in range(10):
dev[2].request("SCAN freq=2437 passive=1")
ev = dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("Scan did not complete")
if dev[2].get_bss(bssid):
break
res = hapd.request("SIGNATURE " + addr2)
if "FAIL" not in res:
raise Exception("Unexpected SIGNATURE success for sta2 (2)")
dev[2].connect("track", key_mgmt="NONE", scan_freq="2437")
res = hapd.request("SIGNATURE " + addr2)
if "FAIL" not in res and len(res) > 0:
raise Exception("Unexpected SIGNATURE success for sta2 (3)")
dev[2].scan_for_bss(bssid, freq=2437, force_scan=True)
res = hapd.request("SIGNATURE " + addr2)
logger.info("sta2: " + res)
if not res.startswith("wifi4|probe:"):
raise Exception("Unexpected SIGNATURE prefix")
if "|assoc:" not in res:
raise Exception("Missing assoc info in SIGNATURE")
+
+def test_ap_track_taxonomy_5g(dev, apdev):
+ """AP tracking STA taxonomy (5 GHz)"""
+ try:
+ _test_ap_track_taxonomy_5g(dev, apdev)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def _test_ap_track_taxonomy_5g(dev, apdev):
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=5200, force_scan=True)
+ addr0 = dev[0].own_addr()
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="5200")
+
+ res = hapd.request("SIGNATURE " + addr0)
+ logger.info("sta0: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+ if ",htcap:" not in res:
+ raise Exception("Missing HT info in SIGNATURE")
+ if ",vhtcap:" not in res:
+ raise Exception("Missing VHT info in SIGNATURE")
diff --git a/tests/hwsim/test_ap_wps.py b/tests/hwsim/test_ap_wps.py
index 08be5f41f230..a07ed60b8218 100644
--- a/tests/hwsim/test_ap_wps.py
+++ b/tests/hwsim/test_ap_wps.py
@@ -1,10466 +1,10568 @@
# WPS tests
# Copyright (c) 2013-2017, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
from tshark import run_tshark
import base64
import binascii
from Crypto.Cipher import AES
import hashlib
import hmac
import os
import time
import sys
import stat
import subprocess
import logging
logger = logging.getLogger()
import re
import socket
import struct
try:
from http.client import HTTPConnection
from urllib.request import urlopen
from urllib.parse import urlparse, urljoin
from urllib.error import HTTPError
from io import StringIO
from socketserver import StreamRequestHandler, TCPServer
except ImportError:
from httplib import HTTPConnection
from urllib import urlopen
from urlparse import urlparse, urljoin
from urllib2 import build_opener, ProxyHandler, HTTPError
from StringIO import StringIO
from SocketServer import StreamRequestHandler, TCPServer
import urllib
import xml.etree.ElementTree as ET
import hwsim_utils
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
from test_ap_eap import int_eap_server_params
-def wps_start_ap(apdev, ssid="test-wps-conf"):
+def wps_start_ap(apdev, ssid="test-wps-conf", extra_cred=None):
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ if extra_cred:
+ params['extra_cred'] = extra_cred
return hostapd.add_ap(apdev, params)
@remote_compatible
def test_ap_wps_init(dev, apdev):
"""Initial AP configuration with first WPS Enrollee"""
skip_without_tkip(dev[0])
ssid = "test-wps"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1"})
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "home")
dev[0].set_network_quoted(id, "psk", "12345678")
dev[0].request("ENABLE_NETWORK %s no-connect" % id)
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "home2")
dev[0].set_network(id, "bssid", "00:11:22:33:44:55")
dev[0].set_network(id, "key_mgmt", "NONE")
dev[0].request("ENABLE_NETWORK %s no-connect" % id)
dev[0].request("WPS_PBC")
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
status = hapd.request("WPS_GET_STATUS")
if "PBC Status: Disabled" not in status:
raise Exception("PBC status not shown correctly")
if "Last WPS result: Success" not in status:
raise Exception("Last WPS result not shown correctly")
if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
raise Exception("Peer address not shown correctly")
conf = hapd.request("GET_CONFIG")
if "wps_state=configured" not in conf:
raise Exception("AP not in WPS configured state")
if "wpa=2" in conf:
if "rsn_pairwise_cipher=CCMP" not in conf:
raise Exception("Unexpected rsn_pairwise_cipher")
if "group_cipher=CCMP" not in conf:
raise Exception("Unexpected group_cipher")
else:
if "wpa=3" not in conf:
raise Exception("AP not in WPA+WPA2 configuration")
if "rsn_pairwise_cipher=CCMP TKIP" not in conf:
raise Exception("Unexpected rsn_pairwise_cipher")
if "wpa_pairwise_cipher=CCMP TKIP" not in conf:
raise Exception("Unexpected wpa_pairwise_cipher")
if "group_cipher=TKIP" not in conf:
raise Exception("Unexpected group_cipher")
if len(dev[0].list_networks()) != 3:
raise Exception("Unexpected number of network blocks")
def test_ap_wps_init_2ap_pbc(dev, apdev):
"""Initial two-radio AP configuration with first WPS PBC Enrollee"""
skip_without_tkip(dev[0])
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hostapd.add_ap(apdev[1], params)
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-PBC]" not in bss['flags']:
raise Exception("WPS-PBC flag missing from AP1")
bss = dev[0].get_bss(apdev[1]['bssid'])
if "[WPS-PBC]" not in bss['flags']:
raise Exception("WPS-PBC flag missing from AP2")
dev[0].dump_monitor()
dev[0].request("SET wps_cred_processing 2")
dev[0].request("WPS_PBC")
ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=30)
dev[0].request("SET wps_cred_processing 0")
if ev is None:
raise Exception("WPS cred event not seen")
if "100e" not in ev:
raise Exception("WPS attributes not included in the cred event")
dev[0].wait_connected(timeout=30)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
bss = dev[1].get_bss(apdev[0]['bssid'])
if "[WPS-PBC]" in bss['flags']:
raise Exception("WPS-PBC flag not cleared from AP1")
bss = dev[1].get_bss(apdev[1]['bssid'])
if "[WPS-PBC]" in bss['flags']:
raise Exception("WPS-PBC flag not cleared from AP2")
def test_ap_wps_init_2ap_pin(dev, apdev):
"""Initial two-radio AP configuration with first WPS PIN Enrollee"""
skip_without_tkip(dev[0])
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hostapd.add_ap(apdev[1], params)
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" not in bss['flags']:
raise Exception("WPS-AUTH flag missing from AP1")
bss = dev[0].get_bss(apdev[1]['bssid'])
if "[WPS-AUTH]" not in bss['flags']:
raise Exception("WPS-AUTH flag missing from AP2")
dev[0].dump_monitor()
dev[0].request("WPS_PIN any " + pin)
dev[0].wait_connected(timeout=30)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
bss = dev[1].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" in bss['flags']:
raise Exception("WPS-AUTH flag not cleared from AP1")
bss = dev[1].get_bss(apdev[1]['bssid'])
if "[WPS-AUTH]" in bss['flags']:
raise Exception("WPS-AUTH flag not cleared from AP2")
@remote_compatible
def test_ap_wps_init_through_wps_config(dev, apdev):
"""Initial AP configuration using wps_config command"""
ssid = "test-wps-init-config"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1"})
if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"12345678").decode()):
raise Exception("WPS_CONFIG command failed")
ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
if ev is None:
raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
# It takes some time for the AP to update Beacon and Probe Response frames,
# so wait here before requesting the scan to be started to avoid adding
# extra five second wait to the test due to fetching obsolete scan results.
hapd.ping()
time.sleep(0.2)
dev[0].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
pairwise="CCMP", group="CCMP")
if "FAIL" not in hapd.request("WPS_CONFIG foo"):
raise Exception("Invalid WPS_CONFIG accepted")
@remote_compatible
def test_ap_wps_init_through_wps_config_2(dev, apdev):
"""AP configuration using wps_config and wps_cred_processing=2"""
ssid = "test-wps-init-config"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"wps_cred_processing": "2"})
if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"12345678").decode()):
raise Exception("WPS_CONFIG command failed")
ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
if ev is None:
raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
if "100e" not in ev:
raise Exception("WPS-NEW-AP-SETTINGS did not include Credential")
@remote_compatible
def test_ap_wps_invalid_wps_config_passphrase(dev, apdev):
"""AP configuration using wps_config command with invalid passphrase"""
ssid = "test-wps-init-config"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1"})
if "FAIL" not in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"1234567").decode()):
raise Exception("Invalid WPS_CONFIG command accepted")
def test_ap_wps_conf(dev, apdev):
"""WPS PBC provisioning with configured AP"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].set("device_name", "Device A")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED':
raise Exception("Not fully connected")
if status['bssid'] != apdev[0]['bssid']:
raise Exception("Unexpected BSSID")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
sta = hapd.get_sta(dev[0].p2p_interface_addr())
if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
raise Exception("Device name not available in STA command")
def test_ap_wps_conf_5ghz(dev, apdev):
"""WPS PBC provisioning with configured AP on 5 GHz band"""
try:
hapd = None
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"country_code": "FI", "hw_mode": "a", "channel": "36"}
hapd = hostapd.add_ap(apdev[0], params)
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].set("device_name", "Device A")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="5180")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
sta = hapd.get_sta(dev[0].p2p_interface_addr())
if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
raise Exception("Device name not available in STA command")
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def test_ap_wps_conf_chan14(dev, apdev):
"""WPS PBC provisioning with configured AP on channel 14"""
try:
hapd = None
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"country_code": "JP", "hw_mode": "b", "channel": "14"}
hapd = hostapd.add_ap(apdev[0], params)
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].set("device_name", "Device A")
dev[0].request("WPS_PBC")
dev[0].wait_connected(timeout=30)
sta = hapd.get_sta(dev[0].p2p_interface_addr())
if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
raise Exception("Device name not available in STA command")
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
@remote_compatible
def test_ap_wps_twice(dev, apdev):
"""WPS provisioning with twice to change passphrase"""
ssid = "test-wps-twice"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
hapd = hostapd.add_ap(apdev[0], params)
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
dev[0].request("DISCONNECT")
logger.info("Restart AP with different passphrase and re-run WPS")
hostapd.remove_bss(apdev[0])
params['wpa_passphrase'] = 'another passphrase'
hapd = hostapd.add_ap(apdev[0], params)
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
networks = dev[0].list_networks()
if len(networks) > 1:
raise Exception("Unexpected duplicated network block present")
@remote_compatible
def test_ap_wps_incorrect_pin(dev, apdev):
"""WPS PIN provisioning with incorrect PIN"""
ssid = "test-wps-incorrect-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning attempt 1")
hapd.request("WPS_PIN any 12345670")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s 55554444" % apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
if ev is None:
raise Exception("WPS operation timed out")
if "config_error=18" not in ev:
raise Exception("Incorrect config_error reported")
if "msg=8" not in ev:
raise Exception("PIN error detected on incorrect message")
dev[0].wait_disconnected(timeout=10)
dev[0].request("WPS_CANCEL")
# if a scan was in progress, wait for it to complete before trying WPS again
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
status = hapd.request("WPS_GET_STATUS")
if "Last WPS result: Failed" not in status:
raise Exception("WPS failure result not shown correctly")
logger.info("WPS provisioning attempt 2")
hapd.request("WPS_PIN any 12345670")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s 12344444" % apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
if ev is None:
raise Exception("WPS operation timed out")
if "config_error=18" not in ev:
raise Exception("Incorrect config_error reported")
if "msg=10" not in ev:
raise Exception("PIN error detected on incorrect message")
dev[0].wait_disconnected(timeout=10)
@remote_compatible
def test_ap_wps_conf_pin(dev, apdev):
"""WPS PIN provisioning with configured AP"""
ssid = "test-wps-conf-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
bss = dev[1].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" in bss['flags']:
raise Exception("WPS-AUTH flag not cleared")
logger.info("Try to connect from another station using the same PIN")
pin = dev[1].request("WPS_PIN " + apdev[0]['bssid'])
ev = dev[1].wait_event(["WPS-M2D", "CTRL-EVENT-CONNECTED"], timeout=30)
if ev is None:
raise Exception("Operation timed out")
if "WPS-M2D" not in ev:
raise Exception("Unexpected WPS operation started")
hapd.request("WPS_PIN any " + pin)
dev[1].wait_connected(timeout=30)
def test_ap_wps_conf_pin_mixed_mode(dev, apdev):
"""WPS PIN provisioning with configured AP (WPA+WPA2)"""
skip_without_tkip(dev[0])
ssid = "test-wps-conf-pin-mixed"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "3",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wpa_pairwise": "TKIP"})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
logger.info("WPS provisioning step (auth_types=0x1b)")
if "OK" not in dev[0].request("SET wps_force_auth_types 0x1b"):
raise Exception("Failed to set wps_force_auth_types 0x1b")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
logger.info("WPS provisioning step (auth_types=0 encr_types=0)")
if "OK" not in dev[0].request("SET wps_force_auth_types 0"):
raise Exception("Failed to set wps_force_auth_types 0")
if "OK" not in dev[0].request("SET wps_force_encr_types 0"):
raise Exception("Failed to set wps_force_encr_types 0")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
dev[0].request("SET wps_force_auth_types ")
dev[0].request("SET wps_force_encr_types ")
@remote_compatible
def test_ap_wps_conf_pin_v1(dev, apdev):
"""WPS PIN provisioning with configured WPS v1.0 AP"""
ssid = "test-wps-conf-pin-v1"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("SET wps_version_number 0x10")
hapd.request("WPS_PIN any " + pin)
found = False
for i in range(0, 10):
dev[0].scan(freq="2412")
if "[WPS-PIN]" in dev[0].request("SCAN_RESULTS"):
found = True
break
if not found:
hapd.request("SET wps_version_number 0x20")
raise Exception("WPS-PIN flag not seen in scan results")
dev[0].dump_monitor()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
hapd.request("SET wps_version_number 0x20")
@remote_compatible
def test_ap_wps_conf_pin_2sta(dev, apdev):
"""Two stations trying to use WPS PIN at the same time"""
ssid = "test-wps-conf-pin2"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
pin = "12345670"
pin2 = "55554444"
hapd.request("WPS_PIN " + dev[0].get_status_field("uuid") + " " + pin)
hapd.request("WPS_PIN " + dev[1].get_status_field("uuid") + " " + pin)
dev[0].dump_monitor()
dev[1].dump_monitor()
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
dev[1].wait_connected(timeout=30)
@remote_compatible
def test_ap_wps_conf_pin_timeout(dev, apdev):
"""WPS PIN provisioning with configured AP timing out PIN"""
ssid = "test-wps-conf-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
addr = dev[0].p2p_interface_addr()
pin = dev[0].wps_read_pin()
if "FAIL" not in hapd.request("WPS_PIN "):
raise Exception("Unexpected success on invalid WPS_PIN")
hapd.request("WPS_PIN any " + pin + " 1")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
time.sleep(1.1)
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=20)
if ev is None:
raise Exception("WPS-PIN-NEEDED event timed out")
ev = dev[0].wait_event(["WPS-M2D"])
if ev is None:
raise Exception("M2D not reported")
dev[0].request("WPS_CANCEL")
hapd.request("WPS_PIN any " + pin + " 20 " + addr)
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
def test_ap_wps_reg_connect(dev, apdev):
"""WPS registrar using AP PIN to connect"""
ssid = "test-wps-reg-ap-pin"
appin = "12345670"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin})
logger.info("WPS provisioning step")
dev[0].dump_monitor()
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
def test_ap_wps_reg_connect_zero_len_ap_pin(dev, apdev):
"""hostapd with zero length ap_pin parameter"""
ssid = "test-wps-reg-ap-pin"
appin = ""
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin})
logger.info("WPS provisioning step")
dev[0].dump_monitor()
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin, no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("No WPS-FAIL reported")
if "msg=5 config_error=15" not in ev:
raise Exception("Unexpected WPS-FAIL: " + ev)
def test_ap_wps_reg_connect_mixed_mode(dev, apdev):
"""WPS registrar using AP PIN to connect (WPA+WPA2)"""
skip_without_tkip(dev[0])
ssid = "test-wps-reg-ap-pin"
appin = "12345670"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "3",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wpa_pairwise": "TKIP", "ap_pin": appin})
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
def test_ap_wps_reg_override_ap_settings(dev, apdev):
"""WPS registrar and ap_settings override"""
ap_settings = "/tmp/ap_wps_reg_override_ap_settings"
try:
os.remove(ap_settings)
except:
pass
# Override AP Settings with values that point to another AP
data = build_wsc_attr(ATTR_NETWORK_INDEX, b'\x01')
data += build_wsc_attr(ATTR_SSID, b"test")
data += build_wsc_attr(ATTR_AUTH_TYPE, b'\x00\x01')
data += build_wsc_attr(ATTR_ENCR_TYPE, b'\x00\x01')
data += build_wsc_attr(ATTR_NETWORK_KEY, b'')
data += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[1]['bssid'].replace(':', '')))
with open(ap_settings, "wb") as f:
f.write(data)
ssid = "test-wps-reg-ap-pin"
appin = "12345670"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin, "ap_settings": ap_settings})
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "test"})
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
ev = hapd2.wait_event(['AP-STA-CONNECTED'], timeout=10)
os.remove(ap_settings)
if ev is None:
raise Exception("No connection with the other AP")
def check_wps_reg_failure(dev, ap, appin):
dev.request("WPS_REG " + ap['bssid'] + " " + appin)
ev = dev.wait_event(["WPS-SUCCESS", "WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS operation timed out")
if "WPS-SUCCESS" in ev:
raise Exception("WPS operation succeeded unexpectedly")
if "config_error=15" not in ev:
raise Exception("WPS setup locked state was not reported correctly")
def test_ap_wps_random_ap_pin(dev, apdev):
"""WPS registrar using random AP PIN"""
ssid = "test-wps-reg-random-ap-pin"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
appin = hapd.request("WPS_AP_PIN random")
if "FAIL" in appin:
raise Exception("Could not generate random AP PIN")
if appin not in hapd.request("WPS_AP_PIN get"):
raise Exception("Could not fetch current AP PIN")
logger.info("WPS provisioning step")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
hapd.request("WPS_AP_PIN disable")
logger.info("WPS provisioning step with AP PIN disabled")
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
check_wps_reg_failure(dev[1], apdev[0], appin)
logger.info("WPS provisioning step with AP PIN reset")
appin = "12345670"
hapd.request("WPS_AP_PIN set " + appin)
dev[1].wps_reg(apdev[0]['bssid'], appin)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=10)
dev[1].wait_disconnected(timeout=10)
logger.info("WPS provisioning step after AP PIN timeout")
hapd.request("WPS_AP_PIN disable")
appin = hapd.request("WPS_AP_PIN random 1")
time.sleep(1.1)
if "FAIL" not in hapd.request("WPS_AP_PIN get"):
raise Exception("AP PIN unexpectedly still enabled")
check_wps_reg_failure(dev[0], apdev[0], appin)
logger.info("WPS provisioning step after AP PIN timeout(2)")
hapd.request("WPS_AP_PIN disable")
appin = "12345670"
hapd.request("WPS_AP_PIN set " + appin + " 1")
time.sleep(1.1)
if "FAIL" not in hapd.request("WPS_AP_PIN get"):
raise Exception("AP PIN unexpectedly still enabled")
check_wps_reg_failure(dev[1], apdev[0], appin)
with fail_test(hapd, 1, "os_get_random;wps_generate_pin"):
hapd.request("WPS_AP_PIN random 1")
hapd.request("WPS_AP_PIN disable")
with alloc_fail(hapd, 1, "upnp_wps_set_ap_pin"):
hapd.request("WPS_AP_PIN set 12345670")
hapd.request("WPS_AP_PIN disable")
if "FAIL" not in hapd.request("WPS_AP_PIN set"):
raise Exception("Invalid WPS_AP_PIN accepted")
if "FAIL" not in hapd.request("WPS_AP_PIN foo"):
raise Exception("Invalid WPS_AP_PIN accepted")
if "FAIL" not in hapd.request("WPS_AP_PIN set " + 9*'1'):
raise Exception("Invalid WPS_AP_PIN accepted")
def test_ap_wps_reg_config(dev, apdev):
"""WPS registrar configuring an AP using AP PIN"""
ssid = "test-wps-init-ap-pin"
appin = "12345670"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"ap_pin": appin})
logger.info("WPS configuration step")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
new_ssid = "wps-new-ssid"
new_passphrase = "1234567890"
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
new_passphrase)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != new_ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
logger.info("Re-configure back to open")
dev[0].request("REMOVE_NETWORK all")
dev[0].flush_scan_cache()
dev[0].dump_monitor()
dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-open", "OPEN", "NONE", "")
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != "wps-open":
raise Exception("Unexpected SSID")
if status['key_mgmt'] != 'NONE':
raise Exception("Unexpected key_mgmt")
def test_ap_wps_reg_config_ext_processing(dev, apdev):
"""WPS registrar configuring an AP with external config processing"""
ssid = "test-wps-init-ap-pin"
appin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wps_cred_processing": "1", "ap_pin": appin}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
new_ssid = "wps-new-ssid"
new_passphrase = "1234567890"
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
new_passphrase, no_wait=True)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS registrar operation timed out")
ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("WPS configuration timed out")
if "1026" not in ev:
raise Exception("AP Settings missing from event")
hapd.request("SET wps_cred_processing 0")
if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(new_ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(new_passphrase.encode()).decode()):
raise Exception("WPS_CONFIG command failed")
dev[0].wait_connected(timeout=15)
def test_ap_wps_reg_config_tkip(dev, apdev):
"""WPS registrar configuring AP to use TKIP and AP upgrading to TKIP+CCMP"""
skip_with_fips(dev[0])
skip_without_tkip(dev[0])
ssid = "test-wps-init-ap"
appin = "12345670"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"ap_pin": appin})
logger.info("WPS configuration step")
dev[0].flush_scan_cache()
dev[0].request("SET wps_version_number 0x10")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
new_ssid = "wps-new-ssid-with-tkip"
new_passphrase = "1234567890"
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPAPSK", "TKIP",
new_passphrase)
logger.info("Re-connect to verify WPA2 mixed mode")
dev[0].request("DISCONNECT")
id = 0
dev[0].set_network(id, "pairwise", "CCMP")
dev[0].set_network(id, "proto", "RSN")
dev[0].connect_network(id)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected: wpa_state={} bssid={}".format(status['wpa_state'], status['bssid']))
if status['ssid'] != new_ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['group_cipher'] != 'TKIP':
conf = hapd.request("GET_CONFIG")
if "group_cipher=CCMP" not in conf or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
def test_ap_wps_setup_locked(dev, apdev):
"""WPS registrar locking up AP setup on AP PIN failures"""
ssid = "test-wps-incorrect-ap-pin"
appin = "12345670"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin})
new_ssid = "wps-new-ssid-test"
new_passphrase = "1234567890"
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
ap_setup_locked = False
for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
dev[0].dump_monitor()
logger.info("Try incorrect AP PIN - attempt " + pin)
dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
"CCMP", new_passphrase, no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
if ev is None:
raise Exception("Timeout on receiving WPS operation failure event")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "config_error=15" in ev:
logger.info("AP Setup Locked")
ap_setup_locked = True
elif "config_error=18" not in ev:
raise Exception("config_error=18 not reported")
dev[0].wait_disconnected(timeout=10)
time.sleep(0.1)
if not ap_setup_locked:
raise Exception("AP setup was not locked")
dev[0].request("WPS_CANCEL")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True,
only_new=True)
bss = dev[0].get_bss(apdev[0]['bssid'])
if 'wps_ap_setup_locked' not in bss or bss['wps_ap_setup_locked'] != '1':
logger.info("BSS: " + str(bss))
raise Exception("AP Setup Locked not indicated in scan results")
status = hapd.request("WPS_GET_STATUS")
if "Last WPS result: Failed" not in status:
raise Exception("WPS failure result not shown correctly")
if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
raise Exception("Peer address not shown correctly")
time.sleep(0.5)
dev[0].dump_monitor()
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("WPS success was not reported")
dev[0].wait_connected(timeout=30)
appin = hapd.request("WPS_AP_PIN random")
if "FAIL" in appin:
raise Exception("Could not generate random AP PIN")
ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=10)
if ev is None:
raise Exception("Failed to unlock AP PIN")
def test_ap_wps_setup_locked_timeout(dev, apdev):
"""WPS re-enabling AP PIN after timeout"""
ssid = "test-wps-incorrect-ap-pin"
appin = "12345670"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin})
new_ssid = "wps-new-ssid-test"
new_passphrase = "1234567890"
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
ap_setup_locked = False
for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
dev[0].dump_monitor()
logger.info("Try incorrect AP PIN - attempt " + pin)
dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
"CCMP", new_passphrase, no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Timeout on receiving WPS operation failure event")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "config_error=15" in ev:
logger.info("AP Setup Locked")
ap_setup_locked = True
break
elif "config_error=18" not in ev:
raise Exception("config_error=18 not reported")
dev[0].wait_disconnected(timeout=10)
time.sleep(0.1)
if not ap_setup_locked:
raise Exception("AP setup was not locked")
ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=80)
if ev is None:
raise Exception("AP PIN did not get unlocked on 60 second timeout")
def test_ap_wps_setup_locked_2(dev, apdev):
"""WPS AP configured for special ap_setup_locked=2 mode"""
ssid = "test-wps-ap-pin"
appin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin, "ap_setup_locked": "2"}
hapd = hostapd.add_ap(apdev[0], params)
new_ssid = "wps-new-ssid-test"
new_passphrase = "1234567890"
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
hapd.dump_monitor()
dev[0].dump_monitor()
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK",
"CCMP", new_passphrase, no_wait=True)
ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("hostapd did not report WPS failure")
if "msg=12 config_error=15" not in ev:
raise Exception("Unexpected failure reason (AP): " + ev)
ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
if ev is None:
raise Exception("Timeout on receiving WPS operation failure event")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "config_error=15" not in ev:
raise Exception("Unexpected failure reason (STA): " + ev)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
def setup_ap_wps_pbc_overlap_2ap(apdev):
params = {"ssid": "wps1", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "wps2", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "123456789", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"}
hapd2 = hostapd.add_ap(apdev[1], params)
hapd.request("WPS_PBC")
hapd2.request("WPS_PBC")
return hapd, hapd2
@remote_compatible
def test_ap_wps_pbc_overlap_2ap(dev, apdev):
"""WPS PBC session overlap with two active APs"""
hapd, hapd2 = setup_ap_wps_pbc_overlap_2ap(apdev)
logger.info("WPS provisioning step")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].request("WPS_PBC")
ev = dev[0].wait_event(["WPS-OVERLAP-DETECTED"], timeout=15)
hapd.request("DISABLE")
hapd2.request("DISABLE")
dev[0].flush_scan_cache()
if ev is None:
raise Exception("PBC session overlap not detected")
@remote_compatible
def test_ap_wps_pbc_overlap_2ap_specific_bssid(dev, apdev):
"""WPS PBC session overlap with two active APs (specific BSSID selected)"""
hapd, hapd2 = setup_ap_wps_pbc_overlap_2ap(apdev)
logger.info("WPS provisioning step")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-OVERLAP-DETECTED",
"CTRL-EVENT-CONNECTED"], timeout=15)
dev[0].request("DISCONNECT")
hapd.request("DISABLE")
hapd2.request("DISABLE")
dev[0].flush_scan_cache()
if ev is None:
raise Exception("PBC session overlap result not reported")
if "CTRL-EVENT-CONNECTED" not in ev:
raise Exception("Connection did not complete")
@remote_compatible
def test_ap_wps_pbc_overlap_2sta(dev, apdev):
"""WPS PBC session overlap with two active STAs"""
ssid = "test-wps-pbc-overlap"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[1].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-M2D"], timeout=15)
if ev is None:
raise Exception("PBC session overlap not detected (dev0)")
if "config_error=12" not in ev:
raise Exception("PBC session overlap not correctly reported (dev0)")
dev[0].request("WPS_CANCEL")
dev[0].request("DISCONNECT")
ev = dev[1].wait_event(["WPS-M2D"], timeout=15)
if ev is None:
raise Exception("PBC session overlap not detected (dev1)")
if "config_error=12" not in ev:
raise Exception("PBC session overlap not correctly reported (dev1)")
dev[1].request("WPS_CANCEL")
dev[1].request("DISCONNECT")
ev = hapd.wait_event(["WPS-OVERLAP-DETECTED"], timeout=1)
if ev is None:
raise Exception("PBC session overlap not detected (AP)")
if "PBC Status: Overlap" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
hapd.request("WPS_CANCEL")
ret = hapd.request("WPS_PBC")
if "FAIL" not in ret:
raise Exception("PBC mode allowed to be started while PBC overlap still active")
hapd.request("DISABLE")
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
+def test_ap_wps_pbc_session_workaround(dev, apdev):
+ """WPS PBC session overlap workaround"""
+ ssid = "test-wps-pbc-overlap"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ bssid = hapd.own_addr()
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("WPS_PBC " + bssid)
+ dev[0].wait_connected(timeout=30)
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=30)
+ dev[0].dump_monitor()
+ # Trigger AP/Registrar to ignore PBC activation immediately after
+ # successfully completed provisioning
+ dev[0].request("WPS_PBC " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("No scan results reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].dump_monitor()
+
+ # Verify that PBC session overlap does not prevent connection
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].request("WPS_PBC " + bssid)
+ dev[1].wait_connected()
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+ hapd.request("DISABLE")
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
@remote_compatible
def test_ap_wps_cancel(dev, apdev):
"""WPS AP cancelling enabled config method"""
ssid = "test-wps-ap-cancel"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
bssid = apdev[0]['bssid']
logger.info("Verify PBC enable/cancel")
hapd.request("WPS_PBC")
dev[0].scan(freq="2412")
dev[0].scan(freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-PBC]" not in bss['flags']:
raise Exception("WPS-PBC flag missing")
if "FAIL" in hapd.request("WPS_CANCEL"):
raise Exception("WPS_CANCEL failed")
dev[0].scan(freq="2412")
dev[0].scan(freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-PBC]" in bss['flags']:
raise Exception("WPS-PBC flag not cleared")
logger.info("Verify PIN enable/cancel")
hapd.request("WPS_PIN any 12345670")
dev[0].scan(freq="2412")
dev[0].scan(freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" not in bss['flags']:
raise Exception("WPS-AUTH flag missing")
if "FAIL" in hapd.request("WPS_CANCEL"):
raise Exception("WPS_CANCEL failed")
dev[0].scan(freq="2412")
dev[0].scan(freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" in bss['flags']:
raise Exception("WPS-AUTH flag not cleared")
def test_ap_wps_er_add_enrollee(dev, apdev):
"""WPS ER configuring AP and adding a new enrollee using PIN"""
try:
_test_ap_wps_er_add_enrollee(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_add_enrollee(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
'friendly_name': "WPS AP - <>&'\" - TEST",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
logger.info("WPS configuration step")
new_passphrase = "1234567890"
dev[0].dump_monitor()
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin, ssid, "WPA2PSK", "CCMP",
new_passphrase)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != ssid:
raise Exception("Unexpected SSID")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
logger.info("Start ER")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
if "|WPS AP - &lt;&gt;&amp;&apos;&quot; - TEST|Company|" not in ev:
raise Exception("Expected friendly name not found")
logger.info("Learn AP configuration through UPnP")
dev[0].dump_monitor()
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not in settings")
if "ssid=" + ssid not in ev:
raise Exception("Expected SSID not in settings")
if "key=" + new_passphrase not in ev:
raise Exception("Expected passphrase not in settings")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
logger.info("Add Enrollee using ER")
pin = dev[1].wps_read_pin()
dev[0].dump_monitor()
dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].dump_monitor()
dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("Enrollee did not report success")
dev[1].wait_connected(timeout=15)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
hwsim_utils.test_connectivity_sta(dev[0], dev[1])
logger.info("Add a specific Enrollee using ER")
pin = dev[2].wps_read_pin()
addr2 = dev[2].p2p_interface_addr()
dev[0].dump_monitor()
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].dump_monitor()
dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
if ev is None:
raise Exception("Enrollee not seen")
if addr2 not in ev:
raise Exception("Unexpected Enrollee MAC address")
dev[0].request("WPS_ER_PIN " + addr2 + " " + pin + " " + addr2)
dev[2].wait_connected(timeout=30)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
logger.info("Verify registrar selection behavior")
dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
dev[1].request("DISCONNECT")
dev[1].wait_disconnected(timeout=10)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[1].scan(freq="2412")
bss = dev[1].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" not in bss['flags']:
# It is possible for scan to miss an update especially when running
# tests under load with multiple VMs, so allow another attempt.
dev[1].scan(freq="2412")
bss = dev[1].get_bss(apdev[0]['bssid'])
if "[WPS-AUTH]" not in bss['flags']:
raise Exception("WPS-AUTH flag missing")
logger.info("Stop ER")
dev[0].dump_monitor()
dev[0].request("WPS_ER_STOP")
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"])
if ev is None:
raise Exception("WPS ER unsubscription timed out")
# It takes some time for the UPnP UNSUBSCRIBE command to go through, so wait
# a bit before verifying that the scan results have changed.
time.sleep(0.2)
for i in range(0, 10):
dev[1].request("BSS_FLUSH 0")
dev[1].scan(freq="2412", only_new=True)
bss = dev[1].get_bss(apdev[0]['bssid'])
if bss and 'flags' in bss and "[WPS-AUTH]" not in bss['flags']:
break
logger.debug("WPS-AUTH flag was still in place - wait a bit longer")
time.sleep(0.1)
if "[WPS-AUTH]" in bss['flags']:
raise Exception("WPS-AUTH flag not removed")
def test_ap_wps_er_add_enrollee_uuid(dev, apdev):
"""WPS ER adding a new enrollee identified by UUID"""
try:
_test_ap_wps_er_add_enrollee_uuid(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_add_enrollee_uuid(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
logger.info("WPS configuration step")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
logger.info("Start ER")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
logger.info("Learn AP configuration through UPnP")
dev[0].dump_monitor()
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not in settings")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
logger.info("Add a specific Enrollee using ER (PBC/UUID)")
addr1 = dev[1].p2p_interface_addr()
dev[0].dump_monitor()
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].dump_monitor()
dev[1].request("WPS_PBC %s" % apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
if ev is None:
raise Exception("Enrollee not seen")
if addr1 not in ev:
raise Exception("Unexpected Enrollee MAC address")
uuid = ev.split(' ')[1]
dev[0].request("WPS_ER_PBC " + uuid)
dev[1].wait_connected(timeout=30)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
logger.info("Add a specific Enrollee using ER (PIN/UUID)")
pin = dev[2].wps_read_pin()
addr2 = dev[2].p2p_interface_addr()
dev[0].dump_monitor()
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].dump_monitor()
dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
if ev is None:
raise Exception("Enrollee not seen")
if addr2 not in ev:
raise Exception("Unexpected Enrollee MAC address")
uuid = ev.split(' ')[1]
dev[0].request("WPS_ER_PIN " + uuid + " " + pin)
dev[2].wait_connected(timeout=30)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-REMOVE"], timeout=15)
if ev is None:
raise Exception("No Enrollee STA entry timeout seen")
logger.info("Stop ER")
dev[0].dump_monitor()
dev[0].request("WPS_ER_STOP")
def test_ap_wps_er_multi_add_enrollee(dev, apdev):
"""Multiple WPS ERs adding a new enrollee using PIN"""
try:
_test_ap_wps_er_multi_add_enrollee(dev, apdev)
finally:
for i in range(2):
dev[i].request("WPS_ER_STOP")
def _test_ap_wps_er_multi_add_enrollee(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
'friendly_name': "WPS AP",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
for i in range(2):
dev[i].flush_scan_cache()
dev[i].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[i].wps_reg(apdev[0]['bssid'], ap_pin)
for i in range(2):
dev[i].request("WPS_ER_START ifname=lo")
for i in range(2):
ev = dev[i].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
dev[i].dump_monitor()
for i in range(2):
dev[i].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
for i in range(2):
ev = dev[i].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
ev = dev[i].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
pin = dev[2].wps_read_pin()
addr = dev[2].own_addr()
dev[0].dump_monitor()
dev[0].request("WPS_ER_PIN any " + pin + " " + addr)
dev[1].dump_monitor()
dev[1].request("WPS_ER_PIN any " + pin + " " + addr)
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].dump_monitor()
dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[2].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("Enrollee did not report success")
dev[2].wait_connected(timeout=15)
def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
"""WPS ER connected to AP and adding a new enrollee using PBC"""
try:
_test_ap_wps_er_add_enrollee_pbc(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_add_enrollee_pbc(dev, apdev):
ssid = "wps-er-add-enrollee-pbc"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
logger.info("Learn AP configuration")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
logger.info("Start ER")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
enrollee = dev[1].p2p_interface_addr()
if "FAIL-UNKNOWN-UUID" not in dev[0].request("WPS_ER_PBC " + enrollee):
raise Exception("Unknown UUID not reported")
logger.info("Add Enrollee using ER and PBC")
dev[0].dump_monitor()
dev[1].dump_monitor()
dev[1].request("WPS_PBC")
for i in range(0, 2):
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
if ev is None:
raise Exception("Enrollee discovery timed out")
if enrollee in ev:
break
if i == 1:
raise Exception("Expected Enrollee not found")
if "FAIL-NO-AP-SETTINGS" not in dev[0].request("WPS_ER_PBC " + enrollee):
raise Exception("Unknown UUID not reported")
logger.info("Use learned network configuration on ER")
dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
if "OK" not in dev[0].request("WPS_ER_PBC " + enrollee):
raise Exception("WPS_ER_PBC failed")
ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Enrollee did not report success")
dev[1].wait_connected(timeout=15)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
hwsim_utils.test_connectivity_sta(dev[0], dev[1])
def test_ap_wps_er_pbc_overlap(dev, apdev):
"""WPS ER connected to AP and PBC session overlap"""
try:
_test_ap_wps_er_pbc_overlap(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_pbc_overlap(dev, apdev):
ssid = "wps-er-add-enrollee-pbc"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
# avoid leaving dev 1 or 2 as the last Probe Request to the AP
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True)
dev[0].dump_monitor()
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
# verify BSSID selection of the AP instead of UUID
if "FAIL" in dev[0].request("WPS_ER_SET_CONFIG " + apdev[0]['bssid'] + " 0"):
raise Exception("Could not select AP based on BSSID")
dev[0].dump_monitor()
dev[1].request("WPS_PBC " + apdev[0]['bssid'])
dev[2].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("PBC scan failed")
ev = dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("PBC scan failed")
found1 = False
found2 = False
addr1 = dev[1].own_addr()
addr2 = dev[2].own_addr()
for i in range(3):
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
if ev is None:
raise Exception("Enrollee discovery timed out")
if addr1 in ev:
found1 = True
if found2:
break
if addr2 in ev:
found2 = True
if found1:
break
if dev[0].request("WPS_ER_PBC " + ap_uuid) != "FAIL-PBC-OVERLAP\n":
raise Exception("PBC overlap not reported")
dev[1].request("WPS_CANCEL")
dev[2].request("WPS_CANCEL")
if dev[0].request("WPS_ER_PBC foo") != "FAIL\n":
raise Exception("Invalid WPS_ER_PBC accepted")
def test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
"""WPS v1.0 ER connected to AP and adding a new enrollee using PIN"""
try:
_test_ap_wps_er_v10_add_enrollee_pin(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
ssid = "wps-er-add-enrollee-pbc"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
logger.info("Learn AP configuration")
dev[0].request("SET wps_version_number 0x10")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
logger.info("Start ER")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
logger.info("Use learned network configuration on ER")
dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
logger.info("Add Enrollee using ER and PIN")
enrollee = dev[1].p2p_interface_addr()
pin = dev[1].wps_read_pin()
dev[0].dump_monitor()
dev[0].request("WPS_ER_PIN any " + pin + " " + enrollee)
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].dump_monitor()
dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[1].wait_connected(timeout=30)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
@remote_compatible
def test_ap_wps_er_config_ap(dev, apdev):
"""WPS ER configuring AP over UPnP"""
try:
_test_ap_wps_er_config_ap(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_config_ap(dev, apdev):
ssid = "wps-er-ap-config"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
logger.info("Connect ER to the AP")
dev[0].connect(ssid, psk="12345678", scan_freq="2412")
logger.info("WPS configuration step")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
new_passphrase = "1234567890"
dev[0].request("WPS_ER_CONFIG " + apdev[0]['bssid'] + " " + ap_pin + " " +
binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " +
binascii.hexlify(new_passphrase.encode()).decode())
ev = dev[0].wait_event(["WPS-SUCCESS"])
if ev is None:
raise Exception("WPS ER configuration operation timed out")
dev[0].wait_disconnected(timeout=10)
dev[0].connect(ssid, psk="1234567890", scan_freq="2412")
logger.info("WPS ER restart")
dev[0].request("WPS_ER_START")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out on ER restart")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found on ER restart")
if "OK" not in dev[0].request("WPS_ER_STOP"):
raise Exception("WPS_ER_STOP failed")
if "OK" not in dev[0].request("WPS_ER_STOP"):
raise Exception("WPS_ER_STOP failed")
@remote_compatible
def test_ap_wps_er_cache_ap_settings(dev, apdev):
"""WPS ER caching AP settings"""
try:
_test_ap_wps_er_cache_ap_settings(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_cache_ap_settings(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
id = int(dev[0].list_networks()[0]['id'])
dev[0].set_network(id, "scan_freq", "2412")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
dev[0].dump_monitor()
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
hapd.disable()
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE", "CTRL-EVENT-DISCONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP removal or disconnection timed out")
hapd = hostapd.add_ap(apdev[0], params)
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP discovery or connection timed out")
pin = dev[1].wps_read_pin()
dev[0].dump_monitor()
dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
time.sleep(0.2)
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].dump_monitor()
dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("Enrollee did not report success")
dev[1].wait_connected(timeout=15)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
dev[0].dump_monitor()
dev[0].request("WPS_ER_STOP")
def test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
"""WPS ER caching AP settings (OOM)"""
try:
_test_ap_wps_er_cache_ap_settings_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
id = int(dev[0].list_networks()[0]['id'])
dev[0].set_network(id, "scan_freq", "2412")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
dev[0].dump_monitor()
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
with alloc_fail(dev[0], 1, "=wps_er_ap_use_cached_settings"):
hapd.disable()
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE",
"CTRL-EVENT-DISCONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP removal or disconnection timed out")
hapd = hostapd.add_ap(apdev[0], params)
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP discovery or connection timed out")
dev[0].request("WPS_ER_STOP")
def test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
"""WPS ER caching AP settings (OOM 2)"""
try:
_test_ap_wps_er_cache_ap_settings_oom2(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
id = int(dev[0].list_networks()[0]['id'])
dev[0].set_network(id, "scan_freq", "2412")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
dev[0].dump_monitor()
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
with alloc_fail(dev[0], 1, "=wps_er_ap_cache_settings"):
hapd.disable()
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE",
"CTRL-EVENT-DISCONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP removal or disconnection timed out")
hapd = hostapd.add_ap(apdev[0], params)
for i in range(2):
ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
timeout=15)
if ev is None:
raise Exception("AP discovery or connection timed out")
dev[0].request("WPS_ER_STOP")
def test_ap_wps_er_subscribe_oom(dev, apdev):
"""WPS ER subscribe OOM"""
try:
_test_ap_wps_er_subscribe_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_subscribe_oom(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
id = int(dev[0].list_networks()[0]['id'])
dev[0].set_network(id, "scan_freq", "2412")
with alloc_fail(dev[0], 1, "http_client_addr;wps_er_subscribe"):
dev[0].request("WPS_ER_START ifname=lo")
for i in range(50):
res = dev[0].request("GET_ALLOC_FAIL")
if res.startswith("0:"):
break
time.sleep(0.1)
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=0)
if ev:
raise Exception("Unexpected AP discovery during OOM")
dev[0].request("WPS_ER_STOP")
def test_ap_wps_er_set_sel_reg_oom(dev, apdev):
"""WPS ER SetSelectedRegistrar OOM"""
try:
_test_ap_wps_er_set_sel_reg_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_set_sel_reg_oom(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
if ev is None:
raise Exception("AP not discovered")
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL timed out")
time.sleep(0.1)
for func in ["http_client_url_parse;wps_er_send_set_sel_reg",
"wps_er_soap_hdr;wps_er_send_set_sel_reg",
"http_client_addr;wps_er_send_set_sel_reg",
"wpabuf_alloc;wps_er_set_sel_reg"]:
with alloc_fail(dev[0], 1, func):
if "OK" not in dev[0].request("WPS_ER_PBC " + ap_uuid):
raise Exception("WPS_ER_PBC failed")
ev = dev[0].wait_event(["WPS-PBC-ACTIVE"], timeout=3)
if ev is None:
raise Exception("WPS-PBC-ACTIVE not seen")
dev[0].request("WPS_ER_STOP")
@remote_compatible
def test_ap_wps_er_learn_oom(dev, apdev):
"""WPS ER learn OOM"""
try:
_test_ap_wps_er_learn_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_learn_oom(dev, apdev):
ssid = "wps-er-add-enrollee"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
if ev is None:
raise Exception("AP not discovered")
for func in ["wps_er_http_put_message_cb",
"xml_get_base64_item;wps_er_http_put_message_cb",
"http_client_url_parse;wps_er_ap_put_message",
"wps_er_soap_hdr;wps_er_ap_put_message",
"http_client_addr;wps_er_ap_put_message"]:
with alloc_fail(dev[0], 1, func):
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=1)
if ev is not None:
raise Exception("AP learn succeeded during OOM")
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=10)
if ev is None:
raise Exception("AP learn did not succeed")
if "FAIL" not in dev[0].request("WPS_ER_LEARN 00000000-9e5c-4e73-bd82-f89cbcd10d7e " + ap_pin):
raise Exception("WPS_ER_LEARN for unknown AP accepted")
dev[0].request("WPS_ER_STOP")
def test_ap_wps_fragmentation(dev, apdev):
"""WPS with fragmentation in EAP-WSC and mixed mode WPA+WPA2"""
skip_without_tkip(dev[0])
ssid = "test-wps-fragmentation"
appin = "12345670"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "3",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wpa_pairwise": "TKIP", "ap_pin": appin,
"fragment_size": "50"})
logger.info("WPS provisioning step (PBC)")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
dev[0].request("SET wps_fragment_size 50")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED':
raise Exception("Not fully connected")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
logger.info("WPS provisioning step (PIN)")
pin = dev[1].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].request("SET wps_fragment_size 50")
dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[1].wait_connected(timeout=30)
status = dev[1].get_status()
if status['wpa_state'] != 'COMPLETED':
raise Exception("Not fully connected")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
logger.info("WPS connection as registrar")
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].request("SET wps_fragment_size 50")
dev[2].wps_reg(apdev[0]['bssid'], appin)
status = dev[2].get_status()
if status['wpa_state'] != 'COMPLETED':
raise Exception("Not fully connected")
if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
raise Exception("Unexpected encryption configuration")
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
@remote_compatible
def test_ap_wps_new_version_sta(dev, apdev):
"""WPS compatibility with new version number on the station"""
ssid = "test-wps-ver"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("SET wps_version_number 0x43")
dev[0].request("SET wps_vendor_ext_m1 000137100100020001")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
@remote_compatible
def test_ap_wps_new_version_ap(dev, apdev):
"""WPS compatibility with new version number on the AP"""
ssid = "test-wps-ver"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
if "FAIL" in hapd.request("SET wps_version_number 0x43"):
raise Exception("Failed to enable test functionality")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
hapd.request("SET wps_version_number 0x20")
@remote_compatible
def test_ap_wps_check_pin(dev, apdev):
"""Verify PIN checking through control interface"""
hapd = hostapd.add_ap(apdev[0],
{"ssid": "wps", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
for t in [("12345670", "12345670"),
("12345678", "FAIL-CHECKSUM"),
("12345", "FAIL"),
("123456789", "FAIL"),
("1234-5670", "12345670"),
("1234 5670", "12345670"),
("1-2.3:4 5670", "12345670")]:
res = hapd.request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
res2 = dev[0].request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
if res != res2:
raise Exception("Unexpected difference in WPS_CHECK_PIN responses")
if res != t[1]:
raise Exception("Incorrect WPS_CHECK_PIN response {} (expected {})".format(res, t[1]))
if "FAIL" not in hapd.request("WPS_CHECK_PIN 12345"):
raise Exception("Unexpected WPS_CHECK_PIN success")
if "FAIL" not in hapd.request("WPS_CHECK_PIN 123456789"):
raise Exception("Unexpected WPS_CHECK_PIN success")
for i in range(0, 10):
pin = dev[0].request("WPS_PIN get")
rpin = dev[0].request("WPS_CHECK_PIN " + pin).rstrip('\n')
if pin != rpin:
raise Exception("Random PIN validation failed for " + pin)
def test_ap_wps_pin_get_failure(dev, apdev):
"""PIN generation failure"""
with fail_test(dev[0], 1,
"os_get_random;wpa_supplicant_ctrl_iface_wps_pin"):
if "FAIL" not in dev[0].request("WPS_PIN get"):
raise Exception("WPS_PIN did not report failure")
def test_ap_wps_wep_config(dev, apdev):
"""WPS 2.0 AP rejecting WEP configuration"""
ssid = "test-wps-config"
appin = "12345670"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"ap_pin": appin})
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-new-ssid-wep", "OPEN", "WEP",
"hello", no_wait=True)
ev = hapd.wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL timed out")
if "reason=2" not in ev:
raise Exception("Unexpected reason code in WPS-FAIL")
status = hapd.request("WPS_GET_STATUS")
if "Last WPS result: Failed" not in status:
raise Exception("WPS failure result not shown correctly")
if "Failure Reason: WEP Prohibited" not in status:
raise Exception("Failure reason not reported correctly")
if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
raise Exception("Peer address not shown correctly")
def test_ap_wps_wep_enroll(dev, apdev):
"""WPS 2.0 STA rejecting WEP configuration"""
ssid = "test-wps-wep"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"skip_cred_build": "1", "extra_cred": "wps-wep-cred"}
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL event timed out")
if "msg=12" not in ev or "reason=2 (WEP Prohibited)" not in ev:
raise Exception("Unexpected WPS-FAIL event: " + ev)
@remote_compatible
def test_ap_wps_ie_fragmentation(dev, apdev):
"""WPS AP using fragmented WPS IE"""
ssid = "test-wps-ie-fragmentation"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "1234567890abcdef1234567890abcdef",
"manufacturer": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"model_name": "1234567890abcdef1234567890abcdef",
"model_number": "1234567890abcdef1234567890abcdef",
"serial_number": "1234567890abcdef1234567890abcdef"}
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
bss = dev[0].get_bss(apdev[0]['bssid'])
if "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
logger.info("Device Name not received correctly")
logger.info(bss)
# This can fail if Probe Response frame is missed and Beacon frame was
# used to fill in the BSS entry. This can happen, e.g., during heavy
# load every now and then and is not really an error, so try to
# workaround by runnign another scan.
dev[0].scan(freq="2412", only_new=True)
bss = dev[0].get_bss(apdev[0]['bssid'])
if not bss or "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
logger.info(bss)
raise Exception("Device Name not received correctly")
if len(re.findall("dd..0050f204", bss['ie'])) != 2:
raise Exception("Unexpected number of WPS IEs")
def get_psk(pskfile):
psks = {}
with open(pskfile, "r") as f:
lines = f.read().splitlines()
for l in lines:
if l == "# WPA PSKs":
continue
vals = l.split(' ')
if len(vals) != 3 or vals[0] != "wps=1":
continue
addr = vals[1]
psk = vals[2]
psks[addr] = psk
return psks
def test_ap_wps_per_station_psk(dev, apdev):
"""WPS PBC provisioning with per-station PSK"""
addr0 = dev[0].own_addr()
addr1 = dev[1].own_addr()
addr2 = dev[2].own_addr()
ssid = "wps"
appin = "12345670"
pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
try:
os.remove(pskfile)
except:
pass
hapd = None
try:
with open(pskfile, "w") as f:
f.write("# WPA PSKs\n")
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa": "2", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP", "ap_pin": appin,
"wpa_psk_file": pskfile}
hapd = hostapd.add_ap(apdev[0], params)
logger.info("First enrollee")
hapd.request("WPS_PBC")
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
logger.info("Second enrollee")
hapd.request("WPS_PBC")
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].request("WPS_PBC " + apdev[0]['bssid'])
dev[1].wait_connected(timeout=30)
logger.info("External registrar")
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].wps_reg(apdev[0]['bssid'], appin)
logger.info("Verifying PSK results")
psks = get_psk(pskfile)
if addr0 not in psks:
raise Exception("No PSK recorded for sta0")
if addr1 not in psks:
raise Exception("No PSK recorded for sta1")
if addr2 not in psks:
raise Exception("No PSK recorded for sta2")
if psks[addr0] == psks[addr1]:
raise Exception("Same PSK recorded for sta0 and sta1")
if psks[addr0] == psks[addr2]:
raise Exception("Same PSK recorded for sta0 and sta2")
if psks[addr1] == psks[addr2]:
raise Exception("Same PSK recorded for sta1 and sta2")
dev[0].request("REMOVE_NETWORK all")
logger.info("Second external registrar")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].wps_reg(apdev[0]['bssid'], appin)
psks2 = get_psk(pskfile)
if addr0 not in psks2:
raise Exception("No PSK recorded for sta0(reg)")
if psks[addr0] == psks2[addr0]:
raise Exception("Same PSK recorded for sta0(enrollee) and sta0(reg)")
finally:
os.remove(pskfile)
if hapd:
dev[0].request("DISCONNECT")
dev[1].request("DISCONNECT")
dev[2].request("DISCONNECT")
hapd.disable()
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def test_ap_wps_per_station_psk_preset(dev, apdev):
"""WPS PIN provisioning with per-station PSK preset"""
addr0 = dev[0].own_addr()
addr1 = dev[1].own_addr()
addr2 = dev[2].own_addr()
ssid = "wps"
appin = "12345670"
pskfile = "/tmp/ap_wps_per_enrollee_psk_preset.psk_file"
try:
os.remove(pskfile)
except:
pass
hapd = None
try:
with open(pskfile, "w") as f:
f.write("# WPA PSKs\n")
f.write("wps=1 " + addr0 + " preset-passphrase-0\n")
f.write("wps=1 " + addr2 + " preset-passphrase-2\n")
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa": "2", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP", "ap_pin": appin,
"wpa_psk_file": pskfile}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
logger.info("First enrollee")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("WPS_PIN %s %s" % (bssid, pin))
dev[0].wait_connected(timeout=30)
logger.info("Second enrollee")
pin = dev[1].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[1].scan_for_bss(bssid, freq=2412)
dev[1].request("WPS_PIN %s %s" % (bssid, pin))
dev[1].wait_connected(timeout=30)
logger.info("External registrar")
dev[2].scan_for_bss(bssid, freq=2412)
dev[2].wps_reg(bssid, appin)
logger.info("Verifying PSK results")
psks = get_psk(pskfile)
if addr0 not in psks:
raise Exception("No PSK recorded for sta0")
if addr1 not in psks:
raise Exception("No PSK recorded for sta1")
if addr2 not in psks:
raise Exception("No PSK recorded for sta2")
logger.info("PSK[0]: " + psks[addr0])
logger.info("PSK[1]: " + psks[addr1])
logger.info("PSK[2]: " + psks[addr2])
if psks[addr0] == psks[addr1]:
raise Exception("Same PSK recorded for sta0 and sta1")
if psks[addr0] == psks[addr2]:
raise Exception("Same PSK recorded for sta0 and sta2")
if psks[addr1] == psks[addr2]:
raise Exception("Same PSK recorded for sta1 and sta2")
pmk0 = hapd.request("GET_PMK " + addr0)
pmk1 = hapd.request("GET_PMK " + addr1)
pmk2 = hapd.request("GET_PMK " + addr2)
logger.info("PMK[0]: " + pmk0)
logger.info("PMK[1]: " + pmk1)
logger.info("PMK[2]: " + pmk2)
if pmk0 != "565faec21ff04702d9d17c464e1301efd36c8a3ea46bb866b4bec7fed4384579":
raise Exception("PSK[0] mismatch")
if psks[addr1] != pmk1:
raise Exception("PSK[1] mismatch")
if psks[addr2] != pmk2:
raise Exception("PSK[2] mismatch")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
logger.info("First enrollee again")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("WPS_PIN %s %s" % (bssid, pin))
dev[0].wait_connected(timeout=30)
psks2 = get_psk(pskfile)
if addr0 not in psks2:
raise Exception("No PSK recorded for sta0 (2)")
if psks[addr0] != psks2[addr0]:
raise Exception("Different PSK recorded for sta0(enrollee) and sta0(enrollee 2)")
finally:
os.remove(pskfile)
def test_ap_wps_per_station_psk_failure(dev, apdev):
"""WPS PBC provisioning with per-station PSK (file not writable)"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
addr2 = dev[2].p2p_dev_addr()
ssid = "wps"
appin = "12345670"
pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
try:
os.remove(pskfile)
except:
pass
hapd = None
try:
with open(pskfile, "w") as f:
f.write("# WPA PSKs\n")
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa": "2", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP", "ap_pin": appin,
"wpa_psk_file": pskfile}
hapd = hostapd.add_ap(apdev[0], params)
if "FAIL" in hapd.request("SET wpa_psk_file /tmp/does/not/exists/ap_wps_per_enrollee_psk_failure.psk_file"):
raise Exception("Failed to set wpa_psk_file")
logger.info("First enrollee")
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
logger.info("Second enrollee")
hapd.request("WPS_PBC")
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[1].request("WPS_PBC " + apdev[0]['bssid'])
dev[1].wait_connected(timeout=30)
logger.info("External registrar")
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].wps_reg(apdev[0]['bssid'], appin)
logger.info("Verifying PSK results")
psks = get_psk(pskfile)
if len(psks) > 0:
raise Exception("PSK recorded unexpectedly")
finally:
if hapd:
for i in range(3):
dev[i].request("DISCONNECT")
hapd.disable()
for i in range(3):
dev[i].flush_scan_cache()
os.remove(pskfile)
def test_ap_wps_pin_request_file(dev, apdev):
"""WPS PIN provisioning with configured AP"""
ssid = "wps"
pinfile = "/tmp/ap_wps_pin_request_file.log"
if os.path.exists(pinfile):
os.remove(pinfile)
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wps_pin_requests": pinfile,
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
uuid = dev[0].get_status_field("uuid")
pin = dev[0].wps_read_pin()
try:
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=15)
if ev is None:
raise Exception("PIN needed event not shown")
if uuid not in ev:
raise Exception("UUID mismatch")
dev[0].request("WPS_CANCEL")
success = False
with open(pinfile, "r") as f:
lines = f.readlines()
for l in lines:
if uuid in l:
success = True
break
if not success:
raise Exception("PIN request entry not in the log file")
finally:
try:
os.remove(pinfile)
except:
pass
def test_ap_wps_auto_setup_with_config_file(dev, apdev):
"""WPS auto-setup with configuration file"""
skip_without_tkip(dev[0])
conffile = "/tmp/ap_wps_auto_setup_with_config_file.conf"
ifname = apdev[0]['ifname']
try:
with open(conffile, "w") as f:
f.write("driver=nl80211\n")
f.write("hw_mode=g\n")
f.write("channel=1\n")
f.write("ieee80211n=1\n")
f.write("interface=%s\n" % ifname)
f.write("ctrl_interface=/var/run/hostapd\n")
f.write("ssid=wps\n")
f.write("eap_server=1\n")
f.write("wps_state=1\n")
hapd = hostapd.add_bss(apdev[0], ifname, conffile)
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
with open(conffile, "r") as f:
lines = f.read().splitlines()
vals = dict()
for l in lines:
try:
[name, value] = l.split('=', 1)
vals[name] = value
except ValueError as e:
if "# WPS configuration" in l:
pass
else:
raise Exception("Unexpected configuration line: " + l)
if vals['ieee80211n'] != '1' or vals['wps_state'] != '2' or "WPA-PSK" not in vals['wpa_key_mgmt']:
raise Exception("Incorrect configuration: " + str(vals))
finally:
try:
os.remove(conffile)
except:
pass
@long_duration_test
def test_ap_wps_pbc_timeout(dev, apdev):
"""wpa_supplicant PBC walk time and WPS ER SelReg timeout"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
urls = upnp_get_urls(location)
eventurl = urlparse(urls['event_sub_url'])
ctrlurl = urlparse(urls['control_url'])
url = urlparse(location)
conn = HTTPConnection(url.netloc)
class WPSERHTTPServer(StreamRequestHandler):
def handle(self):
data = self.rfile.readline().strip()
logger.debug(data)
self.wfile.write(gen_wps_event())
server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
server.timeout = 1
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
msg = '''<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
<NewMessage>EEoAARAQQQABARASAAIAABBTAAIxSBBJAA4ANyoAASABBv///////xBIABA2LbR7pTpRkYj7
VFi5hrLk
</NewMessage>
</u:SetSelectedRegistrar>
</s:Body>
</s:Envelope>'''
headers = {"Content-type": 'text/xml; charset="utf-8"'}
headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
conn.request("POST", ctrlurl.path, msg, headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
server.handle_request()
logger.info("Start WPS_PBC and wait for PBC walk time expiration")
if "OK" not in dev[0].request("WPS_PBC"):
raise Exception("WPS_PBC failed")
start = os.times()[4]
server.handle_request()
dev[1].request("BSS_FLUSH 0")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
only_new=True)
bss = dev[1].get_bss(apdev[0]['bssid'])
logger.debug("BSS: " + str(bss))
if '[WPS-AUTH]' not in bss['flags']:
raise Exception("WPS not indicated authorized")
server.handle_request()
wps_timeout_seen = False
while True:
hapd.dump_monitor()
dev[1].dump_monitor()
if not wps_timeout_seen:
ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=0)
if ev is not None:
logger.info("PBC timeout seen")
wps_timeout_seen = True
else:
dev[0].dump_monitor()
now = os.times()[4]
if now - start > 130:
raise Exception("Selected registration information not removed")
dev[1].request("BSS_FLUSH 0")
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
only_new=True)
bss = dev[1].get_bss(apdev[0]['bssid'])
logger.debug("BSS: " + str(bss))
if '[WPS-AUTH]' not in bss['flags']:
break
server.handle_request()
server.server_close()
if wps_timeout_seen:
return
now = os.times()[4]
if now < start + 150:
dur = start + 150 - now
else:
dur = 1
logger.info("Continue waiting for PBC timeout (%d sec)" % dur)
ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=dur)
if ev is None:
raise Exception("WPS-TIMEOUT not reported")
def add_ssdp_ap(ap, ap_uuid):
ssid = "wps-ssdp"
ap_pin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo",
"friendly_name": "WPS Access Point",
"manufacturer_url": "http://www.example.com/",
"model_description": "Wireless Access Point",
"model_url": "http://www.example.com/model/",
"upc": "123456789012"}
return hostapd.add_ap(ap, params)
def ssdp_send(msg, no_recv=False):
socket.setdefaulttimeout(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.bind(("127.0.0.1", 0))
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
if no_recv:
return None
return sock.recv(1000).decode()
def ssdp_send_msearch(st, no_recv=False):
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX: 1',
'MAN: "ssdp:discover"',
'ST: ' + st,
'', ''])
return ssdp_send(msg, no_recv=no_recv)
def test_ap_wps_ssdp_msearch(dev, apdev):
"""WPS AP and SSDP M-SEARCH messages"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'Host: 239.255.255.250:1900',
'Mx: 1',
'Man: "ssdp:discover"',
'St: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
ssdp_send(msg)
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'host:\t239.255.255.250:1900\t\t\t\t \t\t',
'mx: \t1\t\t ',
'man: \t \t "ssdp:discover" ',
'st: urn:schemas-wifialliance-org:device:WFADevice:1\t\t',
'', ''])
ssdp_send(msg)
ssdp_send_msearch("ssdp:all")
ssdp_send_msearch("upnp:rootdevice")
ssdp_send_msearch("uuid:" + ap_uuid)
ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1")
ssdp_send_msearch("urn:schemas-wifialliance-org:device:WFADevice:1")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST:\t239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 130',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
ssdp_send(msg, no_recv=True)
def test_ap_wps_ssdp_invalid_msearch(dev, apdev):
"""WPS AP and invalid SSDP M-SEARCH messages"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
socket.setdefaulttimeout(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.bind(("127.0.0.1", 0))
logger.debug("Missing MX")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Negative MX")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX: -1',
'MAN: "ssdp:discover"',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Invalid MX")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX; 1',
'MAN: "ssdp:discover"',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Missing MAN")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Invalid MAN")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX: 1',
'MAN: foo',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MX: 1',
'MAN; "ssdp:discover"',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Missing HOST")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Missing ST")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Mismatching ST")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: uuid:16d5f8a9-4ee4-4f5e-81f9-cc6e2f47f42d',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: foo:bar',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: foobar',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Invalid ST")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST; urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Invalid M-SEARCH")
msg = '\r\n'.join([
'M+SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
msg = '\r\n'.join([
'M-SEARCH-* HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
logger.debug("Invalid message format")
sock.sendto(b"NOTIFY * HTTP/1.1", ("239.255.255.250", 1900))
msg = '\r'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
try:
r = sock.recv(1000)
raise Exception("Unexpected M-SEARCH response: " + r)
except socket.timeout:
pass
logger.debug("Valid M-SEARCH")
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
try:
r = sock.recv(1000)
pass
except socket.timeout:
raise Exception("No SSDP response")
def test_ap_wps_ssdp_burst(dev, apdev):
"""WPS AP and SSDP burst"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
msg = '\r\n'.join([
'M-SEARCH * HTTP/1.1',
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
'MX: 1',
'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
'', ''])
socket.setdefaulttimeout(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.bind(("127.0.0.1", 0))
for i in range(0, 25):
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
resp = 0
while True:
try:
r = sock.recv(1000).decode()
if not r.startswith("HTTP/1.1 200 OK\r\n"):
raise Exception("Unexpected message: " + r)
resp += 1
except socket.timeout:
break
if resp < 20:
raise Exception("Too few SSDP responses")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
sock.bind(("127.0.0.1", 0))
for i in range(0, 25):
sock.sendto(msg.encode(), ("239.255.255.250", 1900))
while True:
try:
r = sock.recv(1000).decode()
if ap_uuid in r:
break
except socket.timeout:
raise Exception("No SSDP response")
def ssdp_get_location(uuid):
res = ssdp_send_msearch("uuid:" + uuid)
location = None
for l in res.splitlines():
if l.lower().startswith("location:"):
location = l.split(':', 1)[1].strip()
break
if location is None:
raise Exception("No UPnP location found")
return location
def upnp_get_urls(location):
if sys.version_info[0] > 2:
conn = urlopen(location)
else:
conn = urlopen(location, proxies={})
tree = ET.parse(conn)
root = tree.getroot()
urn = '{urn:schemas-upnp-org:device-1-0}'
service = root.find("./" + urn + "device/" + urn + "serviceList/" + urn + "service")
res = {}
res['scpd_url'] = urljoin(location, service.find(urn + 'SCPDURL').text)
res['control_url'] = urljoin(location,
service.find(urn + 'controlURL').text)
res['event_sub_url'] = urljoin(location,
service.find(urn + 'eventSubURL').text)
return res
def upnp_soap_action(conn, path, action, include_soap_action=True,
soap_action_override=None, newmsg=None, neweventtype=None,
neweventmac=None):
soapns = 'http://schemas.xmlsoap.org/soap/envelope/'
wpsns = 'urn:schemas-wifialliance-org:service:WFAWLANConfig:1'
ET.register_namespace('soapenv', soapns)
ET.register_namespace('wfa', wpsns)
attrib = {}
attrib['{%s}encodingStyle' % soapns] = 'http://schemas.xmlsoap.org/soap/encoding/'
root = ET.Element("{%s}Envelope" % soapns, attrib=attrib)
body = ET.SubElement(root, "{%s}Body" % soapns)
act = ET.SubElement(body, "{%s}%s" % (wpsns, action))
if newmsg:
msg = ET.SubElement(act, "NewMessage")
msg.text = base64.b64encode(newmsg.encode()).decode()
if neweventtype:
msg = ET.SubElement(act, "NewWLANEventType")
msg.text = neweventtype
if neweventmac:
msg = ET.SubElement(act, "NewWLANEventMAC")
msg.text = neweventmac
headers = {"Content-type": 'text/xml; charset="utf-8"'}
if include_soap_action:
headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % action
elif soap_action_override:
headers["SOAPAction"] = soap_action_override
decl = b'<?xml version=\'1.0\' encoding=\'utf8\'?>\n'
conn.request("POST", path, decl + ET.tostring(root), headers)
return conn.getresponse()
def test_ap_wps_upnp(dev, apdev):
"""WPS AP and UPnP operations"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
urls = upnp_get_urls(location)
if sys.version_info[0] > 2:
conn = urlopen(urls['scpd_url'])
else:
conn = urlopen(urls['scpd_url'], proxies={})
scpd = conn.read()
if sys.version_info[0] > 2:
try:
conn = urlopen(urljoin(location, "unknown.html"))
raise Exception("Unexpected HTTP response to GET unknown URL")
except HTTPError as e:
if e.code != 404:
raise Exception("Unexpected HTTP response to GET unknown URL")
else:
conn = urlopen(urljoin(location, "unknown.html"), proxies={})
if conn.getcode() != 404:
raise Exception("Unexpected HTTP response to GET unknown URL")
url = urlparse(location)
conn = HTTPConnection(url.netloc)
#conn.set_debuglevel(1)
headers = {"Content-type": 'text/xml; charset="utf-8"',
"SOAPAction": '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo"'}
conn.request("POST", "hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn.request("UNKNOWN", "hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"Content-type": 'text/xml; charset="utf-8"',
"SOAPAction": '"urn:some-unknown-action#GetDeviceInfo"'}
ctrlurl = urlparse(urls['control_url'])
conn.request("POST", ctrlurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 401:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("GetDeviceInfo without SOAPAction header")
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
include_soap_action=False)
if resp.status != 401:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("GetDeviceInfo with invalid SOAPAction header")
for act in ["foo",
"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo",
'"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"',
'"urn:schemas-wifialliance-org:service:WFAWLANConfig:123#GetDevice']:
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
include_soap_action=False,
soap_action_override=act)
if resp.status != 401:
raise Exception("Unexpected HTTP response: %d" % resp.status)
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
dev = resp.read().decode()
if "NewDeviceInfo" not in dev:
raise Exception("Unexpected GetDeviceInfo response")
logger.debug("PutMessage without required parameters")
resp = upnp_soap_action(conn, ctrlurl.path, "PutMessage")
if resp.status != 600:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("PutWLANResponse without required parameters")
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse")
if resp.status != 600:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("SetSelectedRegistrar from unregistered ER")
resp = upnp_soap_action(conn, ctrlurl.path, "SetSelectedRegistrar")
if resp.status != 501:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Unknown action")
resp = upnp_soap_action(conn, ctrlurl.path, "Unknown")
if resp.status != 401:
raise Exception("Unexpected HTTP response: %d" % resp.status)
def test_ap_wps_upnp_subscribe(dev, apdev):
"""WPS AP and UPnP event subscription"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
urls = upnp_get_urls(location)
eventurl = urlparse(urls['event_sub_url'])
url = urlparse(location)
conn = HTTPConnection(url.netloc)
#conn.set_debuglevel(1)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", "hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:foobar",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Valid subscription")
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
logger.debug("Invalid re-subscription")
headers = {"NT": "upnp:event",
"sid": "123456734567854",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid re-subscription")
headers = {"NT": "upnp:event",
"sid": "uuid:123456734567854",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid re-subscription")
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"sid": sid,
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("SID mismatch in re-subscription")
headers = {"NT": "upnp:event",
"sid": "uuid:4c2bca79-1ff4-4e43-85d4-952a2b8a51fb",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Valid re-subscription")
headers = {"NT": "upnp:event",
"sid": sid,
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid2 = resp.getheader("sid")
logger.debug("Subscription SID " + sid2)
if sid != sid2:
raise Exception("Unexpected SID change")
logger.debug("Valid re-subscription")
headers = {"NT": "upnp:event",
"sid": "uuid: \t \t" + sid.split(':')[1],
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid unsubscription")
headers = {"sid": sid}
conn.request("UNSUBSCRIBE", "/hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"foo": "bar"}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Valid unsubscription")
headers = {"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Unsubscription for not existing SID")
headers = {"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 412:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid unsubscription")
headers = {"sid": " \t \tfoo"}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid unsubscription")
headers = {"sid": "uuid:\t \tfoo"}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Invalid unsubscription")
headers = {"NT": "upnp:event",
"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 400:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.debug("Valid subscription with multiple callbacks")
headers = {"callback": '<http://127.0.0.1:12345/event> <http://127.0.0.1:12345/event>\t<http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
# Force subscription to be deleted due to errors
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
with alloc_fail(hapd, 1, "event_build_message"):
for i in range(10):
dev[1].dump_monitor()
dev[2].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[2].request("WPS_CANCEL")
if i % 4 == 1:
time.sleep(1)
else:
time.sleep(0.1)
time.sleep(0.2)
headers = {"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "", headers)
resp = conn.getresponse()
if resp.status != 200 and resp.status != 412:
raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
with alloc_fail(hapd, 1, "http_client_addr;event_send_start"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response for SUBSCRIBE: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
headers = {"sid": sid}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
with alloc_fail(hapd, 1, "=wps_upnp_event_add"):
for i in range(2):
dev[1].dump_monitor()
dev[2].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[2].request("WPS_CANCEL")
if i == 0:
time.sleep(1)
else:
time.sleep(0.1)
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "wpabuf_dup;wps_upnp_event_add"):
dev[1].dump_monitor()
dev[2].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[2].request("WPS_CANCEL")
time.sleep(0.1)
with fail_test(hapd, 1, "os_get_random;uuid_make;subscription_start"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "=subscription_start"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"callback": '',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"callback": ' <',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
with alloc_fail(hapd, 1, "wpabuf_alloc;subscription_first_event"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "wps_upnp_event_add;subscription_first_event"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "subscr_addr_add_url"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 2, "subscr_addr_add_url"):
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
for i in range(6):
headers = {"callback": '<http://127.0.0.1:%d/event>' % (12345 + i),
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "=upnp_wps_device_send_wlan_event"):
dev[1].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
time.sleep(0.1)
with alloc_fail(hapd, 1, "wpabuf_alloc;upnp_wps_device_send_event"):
dev[1].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
time.sleep(0.1)
with alloc_fail(hapd, 1,
"base64_gen_encode;?base64_encode;upnp_wps_device_send_wlan_event"):
dev[1].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
time.sleep(0.1)
hapd.disable()
with alloc_fail(hapd, 1, "get_netif_info"):
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("ENABLE succeeded during OOM")
def test_ap_wps_upnp_subscribe_events(dev, apdev):
"""WPS AP and UPnP event subscription and many events"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
urls = upnp_get_urls(location)
eventurl = urlparse(urls['event_sub_url'])
class WPSERHTTPServer(StreamRequestHandler):
def handle(self):
data = self.rfile.readline().strip()
logger.debug(data)
self.wfile.write(gen_wps_event())
server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
server.timeout = 1
url = urlparse(location)
conn = HTTPConnection(url.netloc)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
# Fetch the first event message
server.handle_request()
# Force subscription event queue to reach the maximum length by generating
# new proxied events without the ER fetching any of the pending events.
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
for i in range(16):
dev[1].dump_monitor()
dev[2].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[1].request("WPS_CANCEL")
dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
dev[2].request("WPS_CANCEL")
if i % 4 == 1:
time.sleep(1)
else:
time.sleep(0.1)
hapd.request("WPS_PIN any 12345670")
dev[1].dump_monitor()
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=10)
if ev is None:
raise Exception("WPS success not reported")
# Close the WPS ER HTTP server without fetching all the pending events.
# This tests hostapd code path that clears subscription and the remaining
# event queue when the interface is deinitialized.
server.handle_request()
server.server_close()
dev[1].wait_connected()
def test_ap_wps_upnp_http_proto(dev, apdev):
"""WPS AP and UPnP/HTTP protocol testing"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
url = urlparse(location)
conn = HTTPConnection(url.netloc, timeout=0.2)
#conn.set_debuglevel(1)
conn.request("HEAD", "hello")
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected response to HEAD: " + str(resp.status))
conn.close()
for cmd in ["PUT", "DELETE", "TRACE", "CONNECT", "M-SEARCH", "M-POST"]:
try:
conn.request(cmd, "hello")
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
headers = {"Content-Length": 'abc'}
conn.request("HEAD", "hello", "\r\n\r\n", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
headers = {"Content-Length": '-10'}
conn.request("HEAD", "hello", "\r\n\r\n", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
headers = {"Content-Length": '10000000000000'}
conn.request("HEAD", "hello", "\r\n\r\nhello", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
headers = {"Transfer-Encoding": 'abc'}
conn.request("HEAD", "hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected response to HEAD: " + str(resp.status))
conn.close()
headers = {"Transfer-Encoding": 'chunked'}
conn.request("HEAD", "hello", "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected response to HEAD: " + str(resp.status))
conn.close()
# Too long a header
conn.request("HEAD", 5000 * 'A')
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
# Long URL but within header length limits
conn.request("HEAD", 3000 * 'A')
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected response to HEAD: " + str(resp.status))
conn.close()
headers = {"Content-Length": '20'}
conn.request("POST", "hello", 10 * 'A' + "\r\n\r\n", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
conn.request("POST", "hello", 5000 * 'A' + "\r\n\r\n")
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn.close()
conn.request("POST", "hello", 60000 * 'A' + "\r\n\r\n")
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
def test_ap_wps_upnp_http_proto_chunked(dev, apdev):
"""WPS AP and UPnP/HTTP protocol testing for chunked encoding"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
url = urlparse(location)
conn = HTTPConnection(url.netloc)
#conn.set_debuglevel(1)
headers = {"Transfer-Encoding": 'chunked'}
conn.request("POST", "hello",
"a\r\nabcdefghij\r\n" + "2\r\nkl\r\n" + "0\r\n\r\n",
headers)
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn.close()
conn.putrequest("POST", "hello")
conn.putheader('Transfer-Encoding', 'chunked')
conn.endheaders()
conn.send(b"a\r\nabcdefghij\r\n")
time.sleep(0.1)
conn.send(b"2\r\nkl\r\n")
conn.send(b"0\r\n\r\n")
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn.close()
conn.putrequest("POST", "hello")
conn.putheader('Transfer-Encoding', 'chunked')
conn.endheaders()
completed = False
try:
for i in range(20000):
conn.send(b"1\r\nZ\r\n")
conn.send(b"0\r\n\r\n")
resp = conn.getresponse()
completed = True
except Exception as e:
pass
conn.close()
if completed:
raise Exception("Too long chunked request did not result in connection reset")
headers = {"Transfer-Encoding": 'chunked'}
conn.request("POST", "hello", "80000000\r\na", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
conn.request("POST", "hello", "10000000\r\na", headers)
try:
resp = conn.getresponse()
except Exception as e:
pass
conn.close()
@remote_compatible
def test_ap_wps_disabled(dev, apdev):
"""WPS operations while WPS is disabled"""
ssid = "test-wps-disabled"
hapd = hostapd.add_ap(apdev[0], {"ssid": ssid})
if "FAIL" not in hapd.request("WPS_PBC"):
raise Exception("WPS_PBC succeeded unexpectedly")
if "FAIL" not in hapd.request("WPS_CANCEL"):
raise Exception("WPS_CANCEL succeeded unexpectedly")
def test_ap_wps_mixed_cred(dev, apdev):
"""WPS 2.0 STA merging mixed mode WPA/WPA2 credentials"""
skip_without_tkip(dev[0])
ssid = "test-wps-wep"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"skip_cred_build": "1", "extra_cred": "wps-mixed-cred"}
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("WPS-SUCCESS event timed out")
nets = dev[0].list_networks()
if len(nets) != 1:
raise Exception("Unexpected number of network blocks")
id = nets[0]['id']
proto = dev[0].get_network(id, "proto")
if proto != "WPA RSN":
raise Exception("Unexpected merged proto field value: " + proto)
pairwise = dev[0].get_network(id, "pairwise")
p = pairwise.split()
if "CCMP" not in p or "TKIP" not in p:
raise Exception("Unexpected merged pairwise field value: " + pairwise)
@remote_compatible
def test_ap_wps_while_connected(dev, apdev):
"""WPS PBC provisioning while connected to another AP"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
hostapd.add_ap(apdev[1], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['bssid'] != apdev[0]['bssid']:
raise Exception("Unexpected BSSID")
@remote_compatible
def test_ap_wps_while_connected_no_autoconnect(dev, apdev):
"""WPS PBC provisioning while connected to another AP and STA_AUTOCONNECT disabled"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
hostapd.add_ap(apdev[1], {"ssid": "open"})
try:
dev[0].request("STA_AUTOCONNECT 0")
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
logger.info("WPS provisioning step")
hapd.request("WPS_PBC")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['bssid'] != apdev[0]['bssid']:
raise Exception("Unexpected BSSID")
finally:
dev[0].request("STA_AUTOCONNECT 1")
@remote_compatible
def test_ap_wps_from_event(dev, apdev):
"""WPS PBC event on AP to enable PBC"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
hapd.dump_monitor()
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = hapd.wait_event(['WPS-ENROLLEE-SEEN'], timeout=15)
if ev is None:
raise Exception("No WPS-ENROLLEE-SEEN event on AP")
vals = ev.split(' ')
if vals[1] != dev[0].p2p_interface_addr():
raise Exception("Unexpected enrollee address: " + vals[1])
if vals[5] != '4':
raise Exception("Unexpected Device Password Id: " + vals[5])
hapd.request("WPS_PBC")
dev[0].wait_connected(timeout=30)
def test_ap_wps_ap_scan_2(dev, apdev):
"""AP_SCAN 2 for WPS"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
hapd.request("WPS_PBC")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.dump_monitor()
if "OK" not in wpas.request("AP_SCAN 2"):
raise Exception("Failed to set AP_SCAN 2")
wpas.flush_scan_cache()
wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
wpas.dump_monitor()
wpas.request("WPS_PBC " + apdev[0]['bssid'])
ev = wpas.wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS-SUCCESS event timed out")
wpas.wait_connected(timeout=30)
wpas.dump_monitor()
wpas.request("DISCONNECT")
wpas.wait_disconnected()
id = wpas.list_networks()[0]['id']
pairwise = wpas.get_network(id, "pairwise")
if "CCMP" not in pairwise.split():
raise Exception("Unexpected pairwise parameter value: " + pairwise)
group = wpas.get_network(id, "group")
if "CCMP" not in group.split():
raise Exception("Unexpected group parameter value: " + group)
# Need to select a single cipher for ap_scan=2 testing
wpas.set_network(id, "pairwise", "CCMP")
wpas.set_network(id, "group", "CCMP")
wpas.request("BSS_FLUSH 0")
wpas.dump_monitor()
wpas.request("REASSOCIATE")
wpas.wait_connected(timeout=30)
wpas.dump_monitor()
wpas.request("DISCONNECT")
wpas.wait_disconnected()
wpas.flush_scan_cache()
@remote_compatible
def test_ap_wps_eapol_workaround(dev, apdev):
"""EAPOL workaround code path for 802.1X header length mismatch"""
ssid = "test-wps"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1"})
bssid = apdev[0]['bssid']
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
hapd.request("WPS_PBC")
dev[0].request("WPS_PBC")
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " 020000040193000501FFFF")
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
def test_ap_wps_iteration(dev, apdev):
"""WPS PIN and iterate through APs without selected registrar"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
ssid2 = "test-wps-conf2"
hapd2 = hostapd.add_ap(apdev[1],
{"ssid": ssid2, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].dump_monitor()
pin = dev[0].request("WPS_PIN any")
# Wait for iteration through all WPS APs to happen before enabling any
# Registrar.
for i in range(2):
ev = dev[0].wait_event(["Associated with"], timeout=30)
if ev is None:
raise Exception("No association seen")
ev = dev[0].wait_event(["WPS-M2D"], timeout=10)
if ev is None:
raise Exception("No M2D from AP")
dev[0].wait_disconnected()
# Verify that each AP requested PIN
ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=1)
if ev is None:
raise Exception("No WPS-PIN-NEEDED event from AP")
ev = hapd2.wait_event(["WPS-PIN-NEEDED"], timeout=1)
if ev is None:
raise Exception("No WPS-PIN-NEEDED event from AP2")
# Provide PIN to one of the APs and verify that connection gets formed
hapd.request("WPS_PIN any " + pin)
dev[0].wait_connected(timeout=30)
def test_ap_wps_iteration_error(dev, apdev):
"""WPS AP iteration on no Selected Registrar and error case with an AP"""
ssid = "test-wps-conf-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"})
hapd.request("SET ext_eapol_frame_io 1")
bssid = apdev[0]['bssid']
pin = dev[0].wps_read_pin()
dev[0].request("WPS_PIN any " + pin)
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("No EAPOL-TX (EAP-Request/Identity) from hostapd")
dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("No EAPOL-TX (EAP-WSC/Start) from hostapd")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("No CTRL-EVENT-EAP-STARTED")
# Do not forward any more EAPOL frames to test wpa_supplicant behavior for
# a case with an incorrectly behaving WPS AP.
# Start the real target AP and activate registrar on it.
hapd2 = hostapd.add_ap(apdev[1],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"})
hapd2.request("WPS_PIN any " + pin)
dev[0].wait_disconnected(timeout=15)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
if ev is None:
raise Exception("No CTRL-EVENT-EAP-STARTED for the second AP")
ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=15)
if ev is None:
raise Exception("No WPS-CRED-RECEIVED for the second AP")
dev[0].wait_connected(timeout=15)
@remote_compatible
def test_ap_wps_priority(dev, apdev):
"""WPS PIN provisioning with configured AP and wps_priority"""
ssid = "test-wps-conf-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
try:
dev[0].request("SET wps_priority 6")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
netw = dev[0].list_networks()
prio = dev[0].get_network(netw[0]['id'], 'priority')
if prio != '6':
raise Exception("Unexpected network priority: " + prio)
finally:
dev[0].request("SET wps_priority 0")
@remote_compatible
def test_ap_wps_and_non_wps(dev, apdev):
"""WPS and non-WPS AP in single hostapd process"""
params = {"ssid": "wps", "eap_server": "1", "wps_state": "1"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "no wps"}
hapd2 = hostapd.add_ap(apdev[1], params)
appin = hapd.request("WPS_AP_PIN random")
if "FAIL" in appin:
raise Exception("Could not generate random AP PIN")
if appin not in hapd.request("WPS_AP_PIN get"):
raise Exception("Could not fetch current AP PIN")
if "FAIL" in hapd.request("WPS_PBC"):
raise Exception("WPS_PBC failed")
if "FAIL" in hapd.request("WPS_CANCEL"):
raise Exception("WPS_CANCEL failed")
def test_ap_wps_init_oom(dev, apdev):
"""Initial AP configuration and OOM during PSK generation"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(hapd, 1, "base64_gen_encode;?base64_encode;wps_build_cred"):
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_disconnected()
hapd.request("WPS_PIN any " + pin)
dev[0].wait_connected(timeout=30)
@remote_compatible
def test_ap_wps_er_oom(dev, apdev):
"""WPS ER OOM in XML processing"""
try:
_test_ap_wps_er_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
dev[1].request("WPS_CANCEL")
dev[0].request("DISCONNECT")
def _test_ap_wps_er_oom(dev, apdev):
ssid = "wps-er-ap-config"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
dev[0].connect(ssid, psk="12345678", scan_freq="2412")
with alloc_fail(dev[0], 1,
"base64_gen_decode;?base64_decode;xml_get_base64_item"):
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=3)
if ev is not None:
raise Exception("Unexpected AP discovery")
dev[0].request("WPS_ER_STOP")
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
if ev is None:
raise Exception("AP discovery timed out")
dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
with alloc_fail(dev[0], 1,
"base64_gen_decode;?base64_decode;xml_get_base64_item"):
dev[1].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("PBC scan failed")
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
if ev is None:
raise Exception("Enrollee discovery timed out")
@remote_compatible
def test_ap_wps_er_init_oom(dev, apdev):
"""WPS ER and OOM during init"""
try:
_test_ap_wps_er_init_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_init_oom(dev, apdev):
with alloc_fail(dev[0], 1, "wps_er_init"):
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
raise Exception("WPS_ER_START succeeded during OOM")
with alloc_fail(dev[0], 1, "http_server_init"):
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
raise Exception("WPS_ER_START succeeded during OOM")
with alloc_fail(dev[0], 2, "http_server_init"):
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
raise Exception("WPS_ER_START succeeded during OOM")
with alloc_fail(dev[0], 1, "eloop_sock_table_add_sock;?eloop_register_sock;wps_er_ssdp_init"):
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
raise Exception("WPS_ER_START succeeded during OOM")
with fail_test(dev[0], 1, "os_get_random;wps_er_init"):
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
raise Exception("WPS_ER_START succeeded during os_get_random failure")
@remote_compatible
def test_ap_wps_er_init_fail(dev, apdev):
"""WPS ER init failure"""
if "FAIL" not in dev[0].request("WPS_ER_START ifname=does-not-exist"):
dev[0].request("WPS_ER_STOP")
raise Exception("WPS_ER_START with non-existing ifname succeeded")
def test_ap_wps_wpa_cli_action(dev, apdev, test_params):
"""WPS events and wpa_cli action script"""
logdir = os.path.abspath(test_params['logdir'])
pidfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.pid')
logfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.res')
actionfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.action.sh')
with open(actionfile, 'w') as f:
f.write('#!/bin/sh\n')
f.write('echo $* >> %s\n' % logfile)
# Kill the process and wait some time before returning to allow all the
# pending events to be processed with some of this happening after the
# eloop SIGALRM signal has been scheduled.
f.write('if [ $2 = "WPS-SUCCESS" -a -r %s ]; then kill `cat %s`; sleep 1; fi\n' % (pidfile, pidfile))
os.chmod(actionfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC |
stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
prg = os.path.join(test_params['logdir'],
'alt-wpa_supplicant/wpa_supplicant/wpa_cli')
if not os.path.exists(prg):
prg = '../../wpa_supplicant/wpa_cli'
arg = [prg, '-P', pidfile, '-B', '-i', dev[0].ifname, '-a', actionfile]
subprocess.call(arg)
arg = ['ps', 'ax']
cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
out = cmd.communicate()[0].decode()
cmd.wait()
logger.debug("Processes:\n" + out)
if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) not in out:
raise Exception("Did not see wpa_cli running")
hapd.request("WPS_PIN any 12345670")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
dev[0].wait_connected(timeout=30)
for i in range(30):
if not os.path.exists(pidfile):
break
time.sleep(0.1)
if not os.path.exists(logfile):
raise Exception("wpa_cli action results file not found")
with open(logfile, 'r') as f:
res = f.read()
if "WPS-SUCCESS" not in res:
raise Exception("WPS-SUCCESS event not seen in action file")
arg = ['ps', 'ax']
cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
out = cmd.communicate()[0].decode()
cmd.wait()
logger.debug("Remaining processes:\n" + out)
if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) in out:
raise Exception("wpa_cli still running")
if os.path.exists(pidfile):
raise Exception("PID file not removed")
def test_ap_wps_er_ssdp_proto(dev, apdev):
"""WPS ER SSDP protocol testing"""
try:
_test_ap_wps_er_ssdp_proto(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_ssdp_proto(dev, apdev):
socket.setdefaulttimeout(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("239.255.255.250", 1900))
if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo foo"):
raise Exception("Invalid filter accepted")
if "OK" not in dev[0].request("WPS_ER_START ifname=lo 1.2.3.4"):
raise Exception("WPS_ER_START with filter failed")
(msg, addr) = sock.recvfrom(1000)
msg = msg.decode()
logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
if "M-SEARCH" not in msg:
raise Exception("Not an M-SEARCH")
sock.sendto(b"FOO", addr)
time.sleep(0.1)
dev[0].request("WPS_ER_STOP")
dev[0].request("WPS_ER_START ifname=lo")
(msg, addr) = sock.recvfrom(1000)
msg = msg.decode()
logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
if "M-SEARCH" not in msg:
raise Exception("Not an M-SEARCH")
sock.sendto(b"FOO", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nFOO\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nNTS:foo\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nNTS:ssdp:byebye\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\ncache-control: foo=1\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\ncache-control: max-age=1\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nusn:\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nusn:foo\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid:\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid: \r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid: foo\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nNTS:ssdp:byebye\r\n\r\n", addr)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\n\r\n", addr)
with alloc_fail(dev[0], 1, "wps_er_ap_add"):
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
time.sleep(0.1)
with alloc_fail(dev[0], 2, "wps_er_ap_add"):
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
time.sleep(0.1)
# Add an AP with bogus URL
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
# Update timeout on AP without updating URL
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
if ev is None:
raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
# Add an AP with a valid URL (but no server listing to it)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
if ev is None:
raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
sock.close()
wps_event_url = None
def gen_upnp_info(eventSubURL='wps_event', controlURL='wps_control',
udn='uuid:27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'):
payload = '''<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1</deviceType>
<friendlyName>WPS Access Point</friendlyName>
<manufacturer>Company</manufacturer>
<modelName>WAP</modelName>
<modelNumber>123</modelNumber>
<serialNumber>12345</serialNumber>
'''
if udn:
payload += '<UDN>' + udn + '</UDN>'
payload += '''<serviceList>
<service>
<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1</serviceType>
<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>
<SCPDURL>wps_scpd.xml</SCPDURL>
'''
if controlURL:
payload += '<controlURL>' + controlURL + '</controlURL>\n'
if eventSubURL:
payload += '<eventSubURL>' + eventSubURL + '</eventSubURL>\n'
payload += '''</service>
</serviceList>
</device>
</root>
'''
hdr = 'HTTP/1.1 200 OK\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
return (hdr + payload).encode()
def gen_wps_control(payload_override=None):
payload = '''<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
<NewDeviceInfo>EEoAARAQIgABBBBHABAn6oAanlxOc72C+Jy80Q1+ECAABgIAAAADABAaABCJZ7DPtbU3Ust9
Z3wJF07WEDIAwH45D3i1OqB7eJGwTzqeapS71h3KyXncK2xJZ+xqScrlorNEg6LijBJzG2Ca
+FZli0iliDJd397yAx/jk4nFXco3q5ylBSvSw9dhJ5u1xBKSnTilKGlUHPhLP75PUqM3fot9
7zwtFZ4bx6x1sBA6oEe2d0aUJmLumQGCiKEIWlnxs44zego/2tAe81bDzdPBM7o5HH/FUhD+
KoGzFXp51atP+1n9Vta6AkI0Vye99JKLcC6Md9dMJltSVBgd4Xc4lRAEAAIAIxAQAAIADRAN
AAEBEAgAAgAEEEQAAQIQIQAHQ29tcGFueRAjAANXQVAQJAADMTIzEEIABTEyMzQ1EFQACAAG
AFDyBAABEBEAC1dpcmVsZXNzIEFQEDwAAQEQAgACAAAQEgACAAAQCQACAAAQLQAEgQIDABBJ
AAYANyoAASA=
</NewDeviceInfo>
</u:GetDeviceInfoResponse>
</s:Body>
</s:Envelope>
'''
if payload_override:
payload = payload_override
hdr = 'HTTP/1.1 200 OK\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
return (hdr + payload).encode()
def gen_wps_event(sid='uuid:7eb3342a-8a5f-47fe-a585-0785bfec6d8a'):
payload = ""
hdr = 'HTTP/1.1 200 OK\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n'
if sid:
hdr += 'SID: ' + sid + '\r\n'
hdr += 'Timeout: Second-1801\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
return (hdr + payload).encode()
class WPSAPHTTPServer(StreamRequestHandler):
def handle(self):
data = self.rfile.readline().decode().strip()
logger.info("HTTP server received: " + data)
while True:
hdr = self.rfile.readline().decode().strip()
if len(hdr) == 0:
break
logger.info("HTTP header: " + hdr)
if "CALLBACK:" in hdr:
global wps_event_url
wps_event_url = hdr.split(' ')[1].strip('<>')
if "GET /foo.xml" in data:
self.handle_upnp_info()
elif "POST /wps_control" in data:
self.handle_wps_control()
elif "SUBSCRIBE /wps_event" in data:
self.handle_wps_event()
else:
self.handle_others(data)
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info())
def handle_wps_control(self):
self.wfile.write(gen_wps_control())
def handle_wps_event(self):
self.wfile.write(gen_wps_event())
def handle_others(self, data):
logger.info("Ignore HTTP request: " + data)
class MyTCPServer(TCPServer):
def __init__(self, addr, handler):
self.allow_reuse_address = True
TCPServer.__init__(self, addr, handler)
def wps_er_start(dev, http_server, max_age=1, wait_m_search=False,
location_url=None):
socket.setdefaulttimeout(1)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("239.255.255.250", 1900))
dev.request("WPS_ER_START ifname=lo")
for i in range(100):
(msg, addr) = sock.recvfrom(1000)
msg = msg.decode()
logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
if "M-SEARCH" in msg:
break
if not wait_m_search:
raise Exception("Not an M-SEARCH")
if i == 99:
raise Exception("No M-SEARCH seen")
# Add an AP with a valid URL and server listing to it
server = MyTCPServer(("127.0.0.1", 12345), http_server)
if not location_url:
location_url = 'http://127.0.0.1:12345/foo.xml'
sock.sendto(("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:%s\r\ncache-control:max-age=%d\r\n\r\n" % (location_url, max_age)).encode(), addr)
server.timeout = 1
return server, sock
def wps_er_stop(dev, sock, server, on_alloc_fail=False):
sock.close()
server.server_close()
if on_alloc_fail:
done = False
for i in range(50):
res = dev.request("GET_ALLOC_FAIL")
if res.startswith("0:"):
done = True
break
time.sleep(0.1)
if not done:
raise Exception("No allocation failure reported")
else:
ev = dev.wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
if ev is None:
raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
dev.request("WPS_ER_STOP")
-def run_wps_er_proto_test(dev, handler, no_event_url=False, location_url=None):
+def run_wps_er_proto_test(dev, handler, no_event_url=False, location_url=None,
+ max_age=1):
try:
uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
- server, sock = wps_er_start(dev, handler, location_url=location_url)
+ server, sock = wps_er_start(dev, handler, location_url=location_url,
+ max_age=max_age)
global wps_event_url
wps_event_url = None
server.handle_request()
server.handle_request()
server.handle_request()
server.server_close()
if no_event_url:
if wps_event_url:
raise Exception("Received event URL unexpectedly")
return
if wps_event_url is None:
raise Exception("Did not get event URL")
logger.info("Event URL: " + wps_event_url)
finally:
dev.request("WPS_ER_STOP")
def send_wlanevent(url, uuid, data, no_response=False):
conn = HTTPConnection(url.netloc)
payload = '''<?xml version="1.0" encoding="utf-8"?>
<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
<e:property><STAStatus>1</STAStatus></e:property>
<e:property><APStatus>1</APStatus></e:property>
<e:property><WLANEvent>'''
payload += base64.b64encode(data).decode()
payload += '</WLANEvent></e:property></e:propertyset>'
headers = {"Content-type": 'text/xml; charset="utf-8"',
"Server": "Unspecified, UPnP/1.0, Unspecified",
"HOST": url.netloc,
"NT": "upnp:event",
"SID": "uuid:" + uuid,
"SEQ": "0",
"Content-Length": str(len(payload))}
conn.request("NOTIFY", url.path, payload, headers)
if no_response:
try:
conn.getresponse()
except Exception as e:
pass
return
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
def test_ap_wps_er_http_proto(dev, apdev):
"""WPS ER HTTP protocol testing"""
try:
_test_ap_wps_er_http_proto(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_http_proto(dev, apdev):
uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
server, sock = wps_er_start(dev[0], WPSAPHTTPServer, max_age=15)
global wps_event_url
wps_event_url = None
server.handle_request()
server.handle_request()
server.handle_request()
server.server_close()
if wps_event_url is None:
raise Exception("Did not get event URL")
logger.info("Event URL: " + wps_event_url)
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
if ev is None:
raise Exception("No WPS-ER-AP-ADD event")
if uuid not in ev:
raise Exception("UUID mismatch")
sock.close()
logger.info("Valid Probe Request notification")
url = urlparse(wps_event_url)
conn = HTTPConnection(url.netloc)
payload = '''<?xml version="1.0" encoding="utf-8"?>
<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
<e:property><STAStatus>1</STAStatus></e:property>
<e:property><APStatus>1</APStatus></e:property>
<e:property><WLANEvent>ATAyOjAwOjAwOjAwOjAwOjAwEEoAARAQOgABAhAIAAIxSBBHABA2LbR7pTpRkYj7VFi5hrLk
EFQACAAAAAAAAAAAEDwAAQMQAgACAAAQCQACAAAQEgACAAAQIQABIBAjAAEgECQAASAQEQAI
RGV2aWNlIEEQSQAGADcqAAEg
</WLANEvent></e:property>
</e:propertyset>
'''
headers = {"Content-type": 'text/xml; charset="utf-8"',
"Server": "Unspecified, UPnP/1.0, Unspecified",
"HOST": url.netloc,
"NT": "upnp:event",
"SID": "uuid:" + uuid,
"SEQ": "0",
"Content-Length": str(len(payload))}
conn.request("NOTIFY", url.path, payload, headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=5)
if ev is None:
raise Exception("No WPS-ER-ENROLLEE-ADD event")
if "362db47b-a53a-5191-88fb-5458b986b2e4" not in ev:
raise Exception("No Enrollee UUID match")
logger.info("Incorrect event URL AP id")
conn = HTTPConnection(url.netloc)
conn.request("NOTIFY", url.path + '123', payload, headers)
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.info("Missing AP id")
conn = HTTPConnection(url.netloc)
conn.request("NOTIFY", '/event/' + url.path.split('/')[2],
payload, headers)
time.sleep(0.1)
logger.info("Incorrect event URL event id")
conn = HTTPConnection(url.netloc)
conn.request("NOTIFY", '/event/123456789/123', payload, headers)
time.sleep(0.1)
logger.info("Incorrect event URL prefix")
conn = HTTPConnection(url.netloc)
conn.request("NOTIFY", '/foobar/123456789/123', payload, headers)
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.info("Unsupported request")
conn = HTTPConnection(url.netloc)
conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
resp = conn.getresponse()
if resp.status != 501:
raise Exception("Unexpected HTTP response: %d" % resp.status)
logger.info("Unsupported request and OOM")
with alloc_fail(dev[0], 1, "wps_er_http_req"):
conn = HTTPConnection(url.netloc)
conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
time.sleep(0.5)
logger.info("Too short WLANEvent")
data = b'\x00'
send_wlanevent(url, uuid, data)
logger.info("Invalid WLANEventMAC")
data = b'\x00qwertyuiopasdfghjklzxcvbnm'
send_wlanevent(url, uuid, data)
logger.info("Unknown WLANEventType")
data = b'\xff02:00:00:00:00:00'
send_wlanevent(url, uuid, data)
logger.info("Probe Request notification without any attributes")
data = b'\x0102:00:00:00:00:00'
send_wlanevent(url, uuid, data)
logger.info("Probe Request notification with invalid attribute")
data = b'\x0102:00:00:00:00:00\xff'
send_wlanevent(url, uuid, data)
logger.info("EAP message without any attributes")
data = b'\x0202:00:00:00:00:00'
send_wlanevent(url, uuid, data)
logger.info("EAP message with invalid attribute")
data = b'\x0202:00:00:00:00:00\xff'
send_wlanevent(url, uuid, data)
logger.info("EAP message from new STA and not M1")
data = b'\x0202:ff:ff:ff:ff:ff' + b'\x10\x22\x00\x01\x05'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1")
data = b'\x0202:00:00:00:00:00'
data += b'\x10\x22\x00\x01\x04'
data += b'\x10\x47\x00\x10' + 16 * b'\x00'
data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
data += b'\x10\x04\x00\x02\x00\x00'
data += b'\x10\x10\x00\x02\x00\x00'
data += b'\x10\x0d\x00\x01\x00'
data += b'\x10\x08\x00\x02\x00\x00'
data += b'\x10\x44\x00\x01\x00'
data += b'\x10\x21\x00\x00'
data += b'\x10\x23\x00\x00'
data += b'\x10\x24\x00\x00'
data += b'\x10\x42\x00\x00'
data += b'\x10\x54\x00\x08' + 8 * b'\x00'
data += b'\x10\x11\x00\x00'
data += b'\x10\x3c\x00\x01\x00'
data += b'\x10\x02\x00\x02\x00\x00'
data += b'\x10\x12\x00\x02\x00\x00'
data += b'\x10\x09\x00\x02\x00\x00'
data += b'\x10\x2d\x00\x04\x00\x00\x00\x00'
m1 = data
send_wlanevent(url, uuid, data)
logger.info("EAP message: WSC_ACK")
data = b'\x0202:00:00:00:00:00' + b'\x10\x22\x00\x01\x0d'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1")
send_wlanevent(url, uuid, m1)
logger.info("EAP message: WSC_NACK")
data = b'\x0202:00:00:00:00:00' + b'\x10\x22\x00\x01\x0e'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 - Too long attribute values")
data = b'\x0202:00:00:00:00:00'
data += b'\x10\x11\x00\x21' + 33 * b'\x00'
data += b'\x10\x45\x00\x21' + 33 * b'\x00'
data += b'\x10\x42\x00\x21' + 33 * b'\x00'
data += b'\x10\x24\x00\x21' + 33 * b'\x00'
data += b'\x10\x23\x00\x21' + 33 * b'\x00'
data += b'\x10\x21\x00\x41' + 65 * b'\x00'
data += b'\x10\x49\x00\x09\x00\x37\x2a\x05\x02\x00\x00\x05\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing UUID-E")
data = b'\x0202:00:00:00:00:00'
data += b'\x10\x22\x00\x01\x04'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing MAC Address")
data += b'\x10\x47\x00\x10' + 16 * b'\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Enrollee Nonce")
data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Public Key")
data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Authentication Type flags")
data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Encryption Type Flags")
data += b'\x10\x04\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Connection Type flags")
data += b'\x10\x10\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Config Methods")
data += b'\x10\x0d\x00\x01\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Wi-Fi Protected Setup State")
data += b'\x10\x08\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Manufacturer")
data += b'\x10\x44\x00\x01\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Model Name")
data += b'\x10\x21\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Model Number")
data += b'\x10\x23\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Serial Number")
data += b'\x10\x24\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Primary Device Type")
data += b'\x10\x42\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Device Name")
data += b'\x10\x54\x00\x08' + 8 * b'\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing RF Bands")
data += b'\x10\x11\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Association State")
data += b'\x10\x3c\x00\x01\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Device Password ID")
data += b'\x10\x02\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing Configuration Error")
data += b'\x10\x12\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("EAP message: M1 missing OS Version")
data += b'\x10\x09\x00\x02\x00\x00'
send_wlanevent(url, uuid, data)
logger.info("Check max concurrent requests")
addr = (url.hostname, url.port)
socks = {}
for i in range(20):
socks[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
socks[i].settimeout(10)
socks[i].connect(addr)
for i in range(20):
socks[i].send(b"GET / HTTP/1.1\r\n\r\n")
count = 0
for i in range(20):
try:
res = socks[i].recv(100).decode()
if "HTTP/1" in res:
count += 1
else:
logger.info("recv[%d]: len=%d" % (i, len(res)))
except:
pass
socks[i].close()
logger.info("%d concurrent HTTP GET operations returned response" % count)
if count < 8:
raise Exception("Too few concurrent HTTP connections accepted")
logger.info("OOM in HTTP server")
for func in ["http_request_init", "httpread_create",
"eloop_register_timeout;httpread_create",
"eloop_sock_table_add_sock;?eloop_register_sock;httpread_create",
"httpread_hdr_analyze"]:
with alloc_fail(dev[0], 1, func):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
sock.connect(addr)
sock.send(b"GET / HTTP/1.1\r\n\r\n")
try:
sock.recv(100)
except:
pass
sock.close()
logger.info("Invalid HTTP header")
for req in [" GET / HTTP/1.1\r\n\r\n",
"HTTP/1.1 200 OK\r\n\r\n",
"HTTP/\r\n\r\n",
"GET %%a%aa% HTTP/1.1\r\n\r\n",
"GET / HTTP/1.1\r\n FOO\r\n\r\n",
"NOTIFY / HTTP/1.1\r\n" + 4097*'a' + '\r\n\r\n',
"NOTIFY / HTTP/1.1\r\n\r\n" + 8193*'a',
"POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n foo\r\n",
"POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n1\r\nfoo\r\n",
"POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\n",
"POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\naa\ra\r\n\ra"]:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
sock.settimeout(0.1)
sock.connect(addr)
sock.send(req.encode())
try:
sock.recv(100)
except:
pass
sock.close()
with alloc_fail(dev[0], 2, "httpread_read_handler"):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
sock.connect(addr)
sock.send(b"NOTIFY / HTTP/1.1\r\n\r\n" + 4500 * b'a')
try:
sock.recv(100)
except:
pass
sock.close()
conn = HTTPConnection(url.netloc)
payload = '<foo'
headers = {"Content-type": 'text/xml; charset="utf-8"',
"Server": "Unspecified, UPnP/1.0, Unspecified",
"HOST": url.netloc,
"NT": "upnp:event",
"SID": "uuid:" + uuid,
"SEQ": "0",
"Content-Length": str(len(payload))}
conn.request("NOTIFY", url.path, payload, headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
conn = HTTPConnection(url.netloc)
payload = '<WLANEvent foo></WLANEvent>'
headers = {"Content-type": 'text/xml; charset="utf-8"',
"Server": "Unspecified, UPnP/1.0, Unspecified",
"HOST": url.netloc,
"NT": "upnp:event",
"SID": "uuid:" + uuid,
"SEQ": "0",
"Content-Length": str(len(payload))}
conn.request("NOTIFY", url.path, payload, headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(dev[0], 1, "xml_get_first_item"):
send_wlanevent(url, uuid, b'')
with alloc_fail(dev[0], 1, "wpabuf_alloc_ext_data;xml_get_base64_item"):
send_wlanevent(url, uuid, b'foo')
for func in ["wps_init",
"wps_process_manufacturer",
"wps_process_model_name",
"wps_process_model_number",
"wps_process_serial_number",
"wps_process_dev_name"]:
with alloc_fail(dev[0], 1, func):
send_wlanevent(url, uuid, m1)
with alloc_fail(dev[0], 1, "wps_er_http_resp_ok"):
send_wlanevent(url, uuid, m1, no_response=True)
with alloc_fail(dev[0], 1, "wps_er_http_resp_not_found"):
url2 = urlparse(wps_event_url.replace('/event/', '/notfound/'))
send_wlanevent(url2, uuid, m1, no_response=True)
logger.info("EAP message: M1")
data = b'\x0202:11:22:00:00:00'
data += b'\x10\x22\x00\x01\x04'
data += b'\x10\x47\x00\x10' + 16 * b'\x00'
data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
data += b'\x10\x04\x00\x02\x00\x00'
data += b'\x10\x10\x00\x02\x00\x00'
data += b'\x10\x0d\x00\x01\x00'
data += b'\x10\x08\x00\x02\x00\x00'
data += b'\x10\x44\x00\x01\x00'
data += b'\x10\x21\x00\x00'
data += b'\x10\x23\x00\x00'
data += b'\x10\x24\x00\x00'
data += b'\x10\x42\x00\x00'
data += b'\x10\x54\x00\x08' + 8 * b'\x00'
data += b'\x10\x11\x00\x00'
data += b'\x10\x3c\x00\x01\x00'
data += b'\x10\x02\x00\x02\x00\x00'
data += b'\x10\x12\x00\x02\x00\x00'
data += b'\x10\x09\x00\x02\x00\x00'
data += b'\x10\x2d\x00\x04\x00\x00\x00\x00'
dev[0].dump_monitor()
with alloc_fail(dev[0], 1, "wps_er_add_sta_data"):
send_wlanevent(url, uuid, data)
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected enrollee add event")
send_wlanevent(url, uuid, data)
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=2)
if ev is None:
raise Exception("Enrollee add event not seen")
with alloc_fail(dev[0], 1,
"base64_gen_encode;?base64_encode;wps_er_soap_hdr"):
send_wlanevent(url, uuid, data)
with alloc_fail(dev[0], 1, "wpabuf_alloc;wps_er_soap_hdr"):
send_wlanevent(url, uuid, data)
with alloc_fail(dev[0], 1, "http_client_url_parse;wps_er_sta_send_msg"):
send_wlanevent(url, uuid, data)
with alloc_fail(dev[0], 1, "http_client_addr;wps_er_sta_send_msg"):
send_wlanevent(url, uuid, data)
def test_ap_wps_er_http_proto_no_event_sub_url(dev, apdev):
"""WPS ER HTTP protocol testing - no eventSubURL"""
class WPSAPHTTPServer_no_event_sub_url(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(eventSubURL=None))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_event_sub_url,
no_event_url=True)
def test_ap_wps_er_http_proto_event_sub_url_dns(dev, apdev):
"""WPS ER HTTP protocol testing - DNS name in eventSubURL"""
class WPSAPHTTPServer_event_sub_url_dns(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(eventSubURL='http://example.com/wps_event'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_event_sub_url_dns,
no_event_url=True)
def test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
"""WPS ER HTTP protocol testing - subscribe OOM"""
try:
_test_ap_wps_er_http_proto_subscribe_oom(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
tests = [(1, "http_client_url_parse"),
(1, "wpabuf_alloc;wps_er_subscribe"),
(1, "http_client_addr"),
(1, "eloop_sock_table_add_sock;?eloop_register_sock;http_client_addr"),
(1, "eloop_register_timeout;http_client_addr")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
server, sock = wps_er_start(dev[0], WPSAPHTTPServer)
server.handle_request()
server.handle_request()
wps_er_stop(dev[0], sock, server, on_alloc_fail=True)
def test_ap_wps_er_http_proto_no_sid(dev, apdev):
"""WPS ER HTTP protocol testing - no SID"""
class WPSAPHTTPServer_no_sid(WPSAPHTTPServer):
def handle_wps_event(self):
self.wfile.write(gen_wps_event(sid=None))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_sid)
def test_ap_wps_er_http_proto_invalid_sid_no_uuid(dev, apdev):
"""WPS ER HTTP protocol testing - invalid SID - no UUID"""
class WPSAPHTTPServer_invalid_sid_no_uuid(WPSAPHTTPServer):
def handle_wps_event(self):
self.wfile.write(gen_wps_event(sid='FOO'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_no_uuid)
def test_ap_wps_er_http_proto_invalid_sid_uuid(dev, apdev):
"""WPS ER HTTP protocol testing - invalid SID UUID"""
class WPSAPHTTPServer_invalid_sid_uuid(WPSAPHTTPServer):
def handle_wps_event(self):
self.wfile.write(gen_wps_event(sid='uuid:FOO'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_uuid)
def test_ap_wps_er_http_proto_subscribe_failing(dev, apdev):
"""WPS ER HTTP protocol testing - SUBSCRIBE failing"""
class WPSAPHTTPServer_fail_subscribe(WPSAPHTTPServer):
def handle_wps_event(self):
payload = ""
hdr = 'HTTP/1.1 404 Not Found\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Timeout: Second-1801\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
self.wfile.write((hdr + payload).encode())
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_fail_subscribe)
def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
"""WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
class WPSAPHTTPServer_subscribe_invalid_response(WPSAPHTTPServer):
def handle_wps_event(self):
payload = ""
hdr = 'HTTP/1.1 FOO\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Timeout: Second-1801\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
self.wfile.write((hdr + payload).encode())
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_subscribe_invalid_response)
def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
"""WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
class WPSAPHTTPServer_invalid_m1(WPSAPHTTPServer):
def handle_wps_control(self):
payload = '''<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
<NewDeviceInfo>Rk9P</NewDeviceInfo>
</u:GetDeviceInfoResponse>
</s:Body>
</s:Envelope>
'''
self.wfile.write(gen_wps_control(payload_override=payload))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_m1, no_event_url=True)
def test_ap_wps_er_http_proto_upnp_info_no_device(dev, apdev):
"""WPS ER HTTP protocol testing - No device in UPnP info"""
class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
def handle_upnp_info(self):
payload = '''<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
</root>
'''
hdr = 'HTTP/1.1 200 OK\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
self.wfile.write((hdr + payload).encode())
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
def test_ap_wps_er_http_proto_upnp_info_no_device_type(dev, apdev):
"""WPS ER HTTP protocol testing - No deviceType in UPnP info"""
class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
def handle_upnp_info(self):
payload = '''<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
</device>
</root>
'''
hdr = 'HTTP/1.1 200 OK\r\n' + \
'Content-Type: text/xml; charset="utf-8"\r\n' + \
'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
'Connection: close\r\n' + \
'Content-Length: ' + str(len(payload)) + '\r\n' + \
'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
self.wfile.write((hdr + payload).encode())
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
def test_ap_wps_er_http_proto_upnp_info_invalid_udn_uuid(dev, apdev):
"""WPS ER HTTP protocol testing - Invalid UDN UUID"""
class WPSAPHTTPServer_invalid_udn_uuid(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(udn='uuid:foo'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_udn_uuid)
def test_ap_wps_er_http_proto_no_control_url(dev, apdev):
"""WPS ER HTTP protocol testing - no controlURL"""
class WPSAPHTTPServer_no_control_url(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(controlURL=None))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_control_url,
no_event_url=True)
def test_ap_wps_er_http_proto_control_url_dns(dev, apdev):
"""WPS ER HTTP protocol testing - DNS name in controlURL"""
class WPSAPHTTPServer_control_url_dns(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(controlURL='http://example.com/wps_control'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_control_url_dns,
no_event_url=True)
def test_ap_wps_http_timeout(dev, apdev):
"""WPS AP/ER and HTTP timeout"""
try:
_test_ap_wps_http_timeout(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_http_timeout(dev, apdev):
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
url = urlparse(location)
addr = (url.hostname, url.port)
logger.debug("Open HTTP connection to hostapd, but do not complete request")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
sock.connect(addr)
sock.send(b"G")
class DummyServer(StreamRequestHandler):
def handle(self):
logger.debug("DummyServer - start 31 sec wait")
time.sleep(31)
logger.debug("DummyServer - wait done")
logger.debug("Start WPS ER")
server, sock2 = wps_er_start(dev[0], DummyServer, max_age=40,
wait_m_search=True)
logger.debug("Start server to accept, but not complete, HTTP connection from WPS ER")
# This will wait for 31 seconds..
server.handle_request()
logger.debug("Complete HTTP connection with hostapd (that should have already closed the connection)")
try:
sock.send("ET / HTTP/1.1\r\n\r\n")
res = sock.recv(100)
sock.close()
except:
pass
def test_ap_wps_er_url_parse(dev, apdev):
"""WPS ER and URL parsing special cases"""
try:
_test_ap_wps_er_url_parse(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_url_parse(dev, apdev):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.settimeout(1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("239.255.255.250", 1900))
dev[0].request("WPS_ER_START ifname=lo")
(msg, addr) = sock.recvfrom(1000)
msg = msg.decode()
logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
if "M-SEARCH" not in msg:
raise Exception("Not an M-SEARCH")
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1\r\ncache-control:max-age=1\r\n\r\n", addr)
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1/:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://255.255.255.255:0/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
sock.close()
def test_ap_wps_er_link_update(dev, apdev):
"""WPS ER and link update special cases"""
class WPSAPHTTPServer_link_update(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update)
class WPSAPHTTPServer_link_update2(WPSAPHTTPServer):
def handle_others(self, data):
if "GET / " in data:
self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update2,
location_url='http://127.0.0.1:12345')
def test_ap_wps_er_http_client(dev, apdev):
"""WPS ER and HTTP client special cases"""
with alloc_fail(dev[0], 1, "http_link_update"):
run_wps_er_proto_test(dev[0], WPSAPHTTPServer)
with alloc_fail(dev[0], 1, "wpabuf_alloc;http_client_url"):
run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
with alloc_fail(dev[0], 1, "httpread_create;http_client_tx_ready"):
run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
class WPSAPHTTPServer_req_as_resp(WPSAPHTTPServer):
def handle_upnp_info(self):
self.wfile.write(b"GET / HTTP/1.1\r\n\r\n")
run_wps_er_proto_test(dev[0], WPSAPHTTPServer_req_as_resp,
no_event_url=True)
+def test_ap_wps_er_http_client_timeout(dev, apdev):
+ """WPS ER and HTTP client timeout"""
+ class WPSAPHTTPServer_timeout(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ time.sleep(31)
+ self.wfile.write(b"GET / HTTP/1.1\r\n\r\n")
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_timeout,
+ no_event_url=True, max_age=60)
+
def test_ap_wps_init_oom(dev, apdev):
"""wps_init OOM cases"""
ssid = "test-wps"
appin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"ap_pin": appin}
hapd = hostapd.add_ap(apdev[0], params)
pin = dev[0].wps_read_pin()
with alloc_fail(hapd, 1, "wps_init"):
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
with alloc_fail(dev[0], 2, "wps_init"):
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
with alloc_fail(dev[0], 2, "wps_init"):
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PBC %s" % (apdev[0]['bssid']))
ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
dev[0].dump_monitor()
new_ssid = "wps-new-ssid"
new_passphrase = "1234567890"
with alloc_fail(dev[0], 3, "wps_init"):
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
new_passphrase, no_wait=True)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].flush_scan_cache()
@remote_compatible
def test_ap_wps_invalid_assoc_req_elem(dev, apdev):
"""WPS and invalid IE in Association Request frame"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
pin = "12345670"
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
try:
dev[0].request("VENDOR_ELEM_ADD 13 dd050050f20410")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
for i in range(5):
ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
if ev and "vendor=14122" in ev:
break
if ev is None or "vendor=14122" not in ev:
raise Exception("EAP-WSC not started")
dev[0].request("WPS_CANCEL")
finally:
dev[0].request("VENDOR_ELEM_REMOVE 13 *")
def test_ap_wps_pbc_pin_mismatch(dev, apdev):
"""WPS PBC/PIN mismatch"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("SET wps_version_number 0x10")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
hapd.request("WPS_PBC")
pin = dev[0].wps_read_pin()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Scan did not complete")
dev[0].request("WPS_CANCEL")
hapd.request("WPS_CANCEL")
dev[0].flush_scan_cache()
@remote_compatible
def test_ap_wps_ie_invalid(dev, apdev):
"""WPS PIN attempt with AP that has invalid WSC IE"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"vendor_elements": "dd050050f20410"}
hapd = hostapd.add_ap(apdev[0], params)
params = {'ssid': "another", "vendor_elements": "dd050050f20410"}
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
pin = dev[0].wps_read_pin()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Scan did not complete")
dev[0].request("WPS_CANCEL")
@remote_compatible
def test_ap_wps_scan_prio_order(dev, apdev):
"""WPS scan priority ordering"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
params = {'ssid': "another", "vendor_elements": "dd050050f20410"}
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
pin = dev[0].wps_read_pin()
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Scan did not complete")
dev[0].request("WPS_CANCEL")
def test_ap_wps_probe_req_ie_oom(dev, apdev):
"""WPS ProbeReq IE OOM"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with alloc_fail(dev[0], 1, "wps_build_probe_req_ie"):
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Association not seen")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "wps_ie_encapsulate"):
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Association not seen")
dev[0].request("WPS_CANCEL")
hapd.disable()
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
time.sleep(0.2)
dev[0].flush_scan_cache()
def test_ap_wps_assoc_req_ie_oom(dev, apdev):
"""WPS AssocReq IE OOM"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with alloc_fail(dev[0], 1, "wps_build_assoc_req_ie"):
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Association not seen")
dev[0].request("WPS_CANCEL")
def test_ap_wps_assoc_resp_ie_oom(dev, apdev):
"""WPS AssocResp IE OOM"""
ssid = "test-wps"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
hapd = hostapd.add_ap(apdev[0], params)
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with alloc_fail(hapd, 1, "wps_build_assoc_resp_ie"):
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Association not seen")
dev[0].request("WPS_CANCEL")
@remote_compatible
def test_ap_wps_bss_info_errors(dev, apdev):
"""WPS BSS info errors"""
params = {"ssid": "1",
"vendor_elements": "dd0e0050f20410440001ff101100010a"}
hostapd.add_ap(apdev[0], params)
params = {'ssid': "2", "vendor_elements": "dd050050f20410"}
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
bss = dev[0].get_bss(apdev[0]['bssid'])
logger.info("BSS: " + str(bss))
if "wps_state" in bss:
raise Exception("Unexpected wps_state in BSS info")
if 'wps_device_name' not in bss:
raise Exception("No wps_device_name in BSS info")
if bss['wps_device_name'] != '_':
raise Exception("Unexpected wps_device_name value")
bss = dev[0].get_bss(apdev[1]['bssid'])
logger.info("BSS: " + str(bss))
with alloc_fail(dev[0], 1, "=wps_attr_text"):
bss = dev[0].get_bss(apdev[0]['bssid'])
logger.info("BSS(OOM): " + str(bss))
def wps_run_pbc_fail_ap(apdev, dev, hapd):
hapd.request("WPS_PBC")
dev.scan_for_bss(apdev['bssid'], freq="2412")
dev.request("WPS_PBC " + apdev['bssid'])
ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev.request("WPS_CANCEL")
dev.wait_disconnected()
for i in range(5):
try:
dev.flush_scan_cache()
break
except Exception as e:
if str(e).startswith("Failed to trigger scan"):
# Try again
time.sleep(1)
else:
raise
def wps_run_pbc_fail(apdev, dev):
hapd = wps_start_ap(apdev)
wps_run_pbc_fail_ap(apdev, dev, hapd)
@remote_compatible
def test_ap_wps_pk_oom(dev, apdev):
"""WPS and public key OOM"""
with alloc_fail(dev[0], 1, "wps_build_public_key"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_pk_oom_ap(dev, apdev):
"""WPS and public key OOM on AP"""
hapd = wps_start_ap(apdev[0])
with alloc_fail(hapd, 1, "wps_build_public_key"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
@remote_compatible
def test_ap_wps_encr_oom_ap(dev, apdev):
"""WPS and encrypted settings decryption OOM on AP"""
hapd = wps_start_ap(apdev[0])
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with alloc_fail(hapd, 1, "wps_decrypt_encr_settings"):
dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
ev = hapd.wait_event(["WPS-FAIL"], timeout=10)
if ev is None:
raise Exception("No WPS-FAIL reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
@remote_compatible
def test_ap_wps_encr_no_random_ap(dev, apdev):
"""WPS and no random data available for encryption on AP"""
hapd = wps_start_ap(apdev[0])
with fail_test(hapd, 1, "os_get_random;wps_build_encr_settings"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
@remote_compatible
def test_ap_wps_e_hash_no_random_sta(dev, apdev):
"""WPS and no random data available for e-hash on STA"""
with fail_test(dev[0], 1, "os_get_random;wps_build_e_hash"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_m1_no_random(dev, apdev):
"""WPS and no random for M1 on STA"""
with fail_test(dev[0], 1, "os_get_random;wps_build_m1"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_m1_oom(dev, apdev):
"""WPS and OOM for M1 on STA"""
with alloc_fail(dev[0], 1, "wps_build_m1"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_m3_oom(dev, apdev):
"""WPS and OOM for M3 on STA"""
with alloc_fail(dev[0], 1, "wps_build_m3"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_m5_oom(dev, apdev):
"""WPS and OOM for M5 on STA"""
hapd = wps_start_ap(apdev[0])
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
for i in range(1, 3):
with alloc_fail(dev[0], i, "wps_build_m5"):
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
@remote_compatible
def test_ap_wps_m5_no_random(dev, apdev):
"""WPS and no random for M5 on STA"""
with fail_test(dev[0], 1,
"os_get_random;wps_build_encr_settings;wps_build_m5"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_m7_oom(dev, apdev):
"""WPS and OOM for M7 on STA"""
hapd = wps_start_ap(apdev[0])
hapd.request("WPS_PBC")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
for i in range(1, 3):
with alloc_fail(dev[0], i, "wps_build_m7"):
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
@remote_compatible
def test_ap_wps_m7_no_random(dev, apdev):
"""WPS and no random for M7 on STA"""
with fail_test(dev[0], 1,
"os_get_random;wps_build_encr_settings;wps_build_m7"):
wps_run_pbc_fail(apdev[0], dev[0])
@remote_compatible
def test_ap_wps_wsc_done_oom(dev, apdev):
"""WPS and OOM for WSC_Done on STA"""
with alloc_fail(dev[0], 1, "wps_build_wsc_done"):
wps_run_pbc_fail(apdev[0], dev[0])
def test_ap_wps_random_psk_fail(dev, apdev):
"""WPS and no random for PSK on AP"""
ssid = "test-wps"
pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
appin = "12345670"
try:
os.remove(pskfile)
except:
pass
try:
with open(pskfile, "w") as f:
f.write("# WPA PSKs\n")
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa": "2", "wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP", "ap_pin": appin,
"wpa_psk_file": pskfile}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
with fail_test(hapd, 1, "os_get_random;wps_build_cred_network_key"):
dev[0].request("WPS_REG " + apdev[0]['bssid'] + " " + appin)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP failure reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
with fail_test(hapd, 1, "os_get_random;wps_build_cred"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
with alloc_fail(hapd, 1, "wps_build_cred"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
with alloc_fail(hapd, 2, "wps_build_cred"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
finally:
os.remove(pskfile)
def wps_ext_eap_identity_req(dev, hapd, bssid):
logger.debug("EAP-Identity/Request")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
def wps_ext_eap_identity_resp(hapd, dev, addr):
ev = dev.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
def wps_ext_eap_wsc(dst, src, src_addr, msg):
logger.debug(msg)
ev = src.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
res = dst.request("EAPOL_RX " + src_addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX failed")
def wps_start_ext(apdev, dev, pbc=False, pin=None):
addr = dev.own_addr()
bssid = apdev['bssid']
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
hapd = hostapd.add_ap(apdev, params)
if pbc:
hapd.request("WPS_PBC")
else:
if pin is None:
pin = dev.wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev.scan_for_bss(bssid, freq="2412")
hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
if pbc:
dev.request("WPS_PBC " + bssid)
else:
dev.request("WPS_PIN " + bssid + " " + pin)
return addr, bssid, hapd
def wps_auth_corrupt(dst, src, addr):
ev = src.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
src.request("SET ext_eapol_frame_io 0")
dst.request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[-24:-16] != '10050008':
raise Exception("Could not find Authenticator attribute")
# Corrupt Authenticator value
msg = msg[:-1] + '%x' % ((int(msg[-1], 16) + 1) % 16)
res = dst.request("EAPOL_RX " + addr + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
def wps_fail_finish(hapd, dev, fail_str):
ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("WPS-FAIL not indicated")
if fail_str not in ev:
raise Exception("Unexpected WPS-FAIL value: " + ev)
dev.request("WPS_CANCEL")
dev.wait_disconnected()
def wps_auth_corrupt_from_ap(dev, hapd, bssid, fail_str):
wps_auth_corrupt(dev, hapd, bssid)
wps_fail_finish(hapd, dev, fail_str)
def wps_auth_corrupt_to_ap(dev, hapd, addr, fail_str):
wps_auth_corrupt(hapd, dev, addr)
wps_fail_finish(hapd, dev, fail_str)
def test_ap_wps_authenticator_mismatch_m2(dev, apdev):
"""WPS and Authenticator attribute mismatch in M2"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=5")
def test_ap_wps_authenticator_mismatch_m3(dev, apdev):
"""WPS and Authenticator attribute mismatch in M3"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
logger.debug("M3")
wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=7")
def test_ap_wps_authenticator_mismatch_m4(dev, apdev):
"""WPS and Authenticator attribute mismatch in M4"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
logger.debug("M4")
wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=8")
def test_ap_wps_authenticator_mismatch_m5(dev, apdev):
"""WPS and Authenticator attribute mismatch in M5"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
logger.debug("M5")
wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=9")
def test_ap_wps_authenticator_mismatch_m6(dev, apdev):
"""WPS and Authenticator attribute mismatch in M6"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
logger.debug("M6")
wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=10")
def test_ap_wps_authenticator_mismatch_m7(dev, apdev):
"""WPS and Authenticator attribute mismatch in M7"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
logger.debug("M7")
wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=11")
def test_ap_wps_authenticator_mismatch_m8(dev, apdev):
"""WPS and Authenticator attribute mismatch in M8"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
wps_ext_eap_wsc(hapd, dev[0], addr, "M7")
logger.debug("M8")
wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=12")
def test_ap_wps_authenticator_missing_m2(dev, apdev):
"""WPS and Authenticator attribute missing from M2"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[-24:-16] != '10050008':
raise Exception("Could not find Authenticator attribute")
# Remove Authenticator value
msg = msg[:-24]
mlen = "%04x" % (int(msg[4:8], 16) - 12)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
wps_fail_finish(hapd, dev[0], "msg=5")
def test_ap_wps_m2_dev_passwd_id_p2p(dev, apdev):
"""WPS and M2 with different Device Password ID (P2P)"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[722:730] != '10120002':
raise Exception("Could not find Device Password ID attribute")
# Replace Device Password ID value. This will fail Authenticator check, but
# allows the code path in wps_process_dev_pw_id() to be checked from debug
# log.
msg = msg[0:730] + "0005" + msg[734:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
wps_fail_finish(hapd, dev[0], "msg=5")
def test_ap_wps_m2_dev_passwd_id_change_pin_to_pbc(dev, apdev):
"""WPS and M2 with different Device Password ID (PIN to PBC)"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[722:730] != '10120002':
raise Exception("Could not find Device Password ID attribute")
# Replace Device Password ID value (PIN --> PBC). This will be rejected.
msg = msg[0:730] + "0004" + msg[734:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
wps_fail_finish(hapd, dev[0], "msg=5")
def test_ap_wps_m2_dev_passwd_id_change_pbc_to_pin(dev, apdev):
"""WPS and M2 with different Device Password ID (PBC to PIN)"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[722:730] != '10120002':
raise Exception("Could not find Device Password ID attribute")
# Replace Device Password ID value. This will fail Authenticator check, but
# allows the code path in wps_process_dev_pw_id() to be checked from debug
# log.
msg = msg[0:730] + "0000" + msg[734:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
wps_fail_finish(hapd, dev[0], "msg=5")
dev[0].flush_scan_cache()
def test_ap_wps_m2_missing_dev_passwd_id(dev, apdev):
"""WPS and M2 without Device Password ID"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[722:730] != '10120002':
raise Exception("Could not find Device Password ID attribute")
# Remove Device Password ID value. This will fail Authenticator check, but
# allows the code path in wps_process_dev_pw_id() to be checked from debug
# log.
mlen = "%04x" % (int(msg[4:8], 16) - 6)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:722] + msg[734:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
wps_fail_finish(hapd, dev[0], "msg=5")
def test_ap_wps_m2_missing_registrar_nonce(dev, apdev):
"""WPS and M2 without Registrar Nonce"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[96:104] != '10390010':
raise Exception("Could not find Registrar Nonce attribute")
# Remove Registrar Nonce. This will fail Authenticator check, but
# allows the code path in wps_process_registrar_nonce() to be checked from
# the debug log.
mlen = "%04x" % (int(msg[4:8], 16) - 20)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:96] + msg[136:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_missing_enrollee_nonce(dev, apdev):
"""WPS and M2 without Enrollee Nonce"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[56:64] != '101a0010':
raise Exception("Could not find enrollee Nonce attribute")
# Remove Enrollee Nonce. This will fail Authenticator check, but
# allows the code path in wps_process_enrollee_nonce() to be checked from
# the debug log.
mlen = "%04x" % (int(msg[4:8], 16) - 20)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:56] + msg[96:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_missing_uuid_r(dev, apdev):
"""WPS and M2 without UUID-R"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[136:144] != '10480010':
raise Exception("Could not find enrollee Nonce attribute")
# Remove UUID-R. This will fail Authenticator check, but allows the code
# path in wps_process_uuid_r() to be checked from the debug log.
mlen = "%04x" % (int(msg[4:8], 16) - 20)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:136] + msg[176:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_invalid(dev, apdev):
"""WPS and M2 parsing failure"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[136:144] != '10480010':
raise Exception("Could not find enrollee Nonce attribute")
# Remove UUID-R. This will fail Authenticator check, but allows the code
# path in wps_process_uuid_r() to be checked from the debug log.
mlen = "%04x" % (int(msg[4:8], 16) - 1)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:-2]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_missing_msg_type(dev, apdev):
"""WPS and M2 without Message Type"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[46:54] != '10220001':
raise Exception("Could not find Message Type attribute")
# Remove Message Type. This will fail Authenticator check, but allows the
# code path in wps_process_wsc_msg() to be checked from the debug log.
mlen = "%04x" % (int(msg[4:8], 16) - 5)
msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:46] + msg[56:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_unknown_msg_type(dev, apdev):
"""WPS and M2 but unknown Message Type"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[46:54] != '10220001':
raise Exception("Could not find Message Type attribute")
# Replace Message Type value. This will be rejected.
msg = msg[0:54] + "00" + msg[56:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
if ev is None:
raise Exception("Disconnect event not seen")
dev[0].request("WPS_CANCEL")
dev[0].flush_scan_cache()
def test_ap_wps_m2_unknown_opcode(dev, apdev):
"""WPS and M2 but unknown opcode"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
# Replace opcode. This will be discarded in EAP-WSC processing.
msg = msg[0:32] + "00" + msg[34:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_ap_wps_m2_unknown_opcode2(dev, apdev):
"""WPS and M2 but unknown opcode (WSC_Start)"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
# Replace opcode. This will be discarded in EAP-WSC processing.
msg = msg[0:32] + "01" + msg[34:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_ap_wps_m2_unknown_opcode3(dev, apdev):
"""WPS and M2 but unknown opcode (WSC_Done)"""
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
logger.debug("M2")
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
# Replace opcode. This will be discarded in WPS Enrollee processing.
msg = msg[0:32] + "05" + msg[34:]
res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def wps_m2_but_other(dev, apdev, title, msgtype):
addr, bssid, hapd = wps_start_ext(apdev, dev)
wps_ext_eap_identity_req(dev, hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev, addr)
wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev, addr, "M1")
logger.debug(title)
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev.request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[46:54] != '10220001':
raise Exception("Could not find Message Type attribute")
# Replace Message Type value. This will be rejected.
msg = msg[0:54] + msgtype + msg[56:]
res = dev.request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = dev.wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("WPS-FAIL event not seen")
dev.request("WPS_CANCEL")
dev.wait_disconnected()
def wps_m4_but_other(dev, apdev, title, msgtype):
addr, bssid, hapd = wps_start_ext(apdev, dev)
wps_ext_eap_identity_req(dev, hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev, addr)
wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
wps_ext_eap_wsc(hapd, dev, addr, "M1")
wps_ext_eap_wsc(dev, hapd, bssid, "M2")
wps_ext_eap_wsc(hapd, dev, addr, "M3")
logger.debug(title)
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
hapd.request("SET ext_eapol_frame_io 0")
dev.request("SET ext_eapol_frame_io 0")
msg = ev.split(' ')[2]
if msg[46:54] != '10220001':
raise Exception("Could not find Message Type attribute")
# Replace Message Type value. This will be rejected.
msg = msg[0:54] + msgtype + msg[56:]
res = dev.request("EAPOL_RX " + bssid + " " + msg)
if "OK" not in res:
raise Exception("EAPOL_RX failed")
ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("WPS-FAIL event not seen")
dev.request("WPS_CANCEL")
dev.wait_disconnected()
def test_ap_wps_m2_msg_type_m4(dev, apdev):
"""WPS and M2 but Message Type M4"""
wps_m2_but_other(dev[0], apdev[0], "M2/M4", "08")
def test_ap_wps_m2_msg_type_m6(dev, apdev):
"""WPS and M2 but Message Type M6"""
wps_m2_but_other(dev[0], apdev[0], "M2/M6", "0a")
def test_ap_wps_m2_msg_type_m8(dev, apdev):
"""WPS and M2 but Message Type M8"""
wps_m2_but_other(dev[0], apdev[0], "M2/M8", "0c")
def test_ap_wps_m4_msg_type_m2(dev, apdev):
"""WPS and M4 but Message Type M2"""
wps_m4_but_other(dev[0], apdev[0], "M4/M2", "05")
def test_ap_wps_m4_msg_type_m2d(dev, apdev):
"""WPS and M4 but Message Type M2D"""
wps_m4_but_other(dev[0], apdev[0], "M4/M2D", "06")
@remote_compatible
def test_ap_wps_config_methods(dev, apdev):
"""WPS configuration method parsing"""
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"config_methods": "ethernet display ext_nfc_token int_nfc_token physical_display physical_push_button"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"config_methods": "display push_button"}
hapd2 = hostapd.add_ap(apdev[1], params)
def test_ap_wps_set_selected_registrar_proto(dev, apdev):
"""WPS UPnP SetSelectedRegistrar protocol testing"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
urls = upnp_get_urls(location)
eventurl = urlparse(urls['event_sub_url'])
ctrlurl = urlparse(urls['control_url'])
url = urlparse(location)
conn = HTTPConnection(url.netloc)
class WPSERHTTPServer(StreamRequestHandler):
def handle(self):
data = self.rfile.readline().strip()
logger.debug(data)
self.wfile.write(gen_wps_event())
server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
server.timeout = 1
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
sid = resp.getheader("sid")
logger.debug("Subscription SID " + sid)
server.handle_request()
tests = [(500, "10"),
(200, "104a000110" + "1041000101" + "101200020000" +
"105300023148" +
"1049002c00372a0001200124111111111111222222222222333333333333444444444444555555555555666666666666" +
"10480010362db47ba53a519188fb5458b986b2e4"),
(200, "104a000110" + "1041000100" + "101200020000" +
"105300020000"),
(200, "104a000110" + "1041000100"),
(200, "104a000110")]
for status, test in tests:
tlvs = binascii.unhexlify(test)
newmsg = base64.b64encode(tlvs).decode()
msg = '<?xml version="1.0"?>\n'
msg += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
msg += '<s:Body>'
msg += '<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">'
msg += '<NewMessage>'
msg += newmsg
msg += "</NewMessage></u:SetSelectedRegistrar></s:Body></s:Envelope>"
headers = {"Content-type": 'text/xml; charset="utf-8"'}
headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
conn.request("POST", ctrlurl.path, msg, headers)
resp = conn.getresponse()
if resp.status != status:
raise Exception("Unexpected HTTP response: %d (expected %d)" % (resp.status, status))
def test_ap_wps_adv_oom(dev, apdev):
"""WPS AP and advertisement OOM"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
with alloc_fail(hapd, 1, "=msearchreply_state_machine_start"):
ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
no_recv=True)
time.sleep(0.2)
with alloc_fail(hapd, 1, "eloop_register_timeout;msearchreply_state_machine_start"):
ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
no_recv=True)
time.sleep(0.2)
with alloc_fail(hapd, 1,
"next_advertisement;advertisement_state_machine_stop"):
hapd.disable()
with alloc_fail(hapd, 1, "ssdp_listener_start"):
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("ENABLE succeeded during OOM")
def test_wps_config_methods(dev):
"""WPS config method update"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
if "OK" not in wpas.request("SET config_methods display label"):
raise Exception("Failed to set config_methods")
if wpas.request("GET config_methods").strip() != "display label":
raise Exception("config_methods were not updated")
if "OK" not in wpas.request("SET config_methods "):
raise Exception("Failed to clear config_methods")
if wpas.request("GET config_methods").strip() != "":
raise Exception("config_methods were not cleared")
WPS_VENDOR_ID_WFA = 14122
WPS_VENDOR_TYPE = 1
# EAP-WSC Op-Code values
WSC_Start = 0x01
WSC_ACK = 0x02
WSC_NACK = 0x03
WSC_MSG = 0x04
WSC_Done = 0x05
WSC_FRAG_ACK = 0x06
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
# Message 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
def get_wsc_msg(dev):
ev = dev.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX")
data = binascii.unhexlify(ev.split(' ')[2])
msg = {}
# Parse EAPOL header
if len(data) < 4:
raise Exception("No room for EAPOL header")
version, type, length = struct.unpack('>BBH', data[0:4])
msg['eapol_version'] = version
msg['eapol_type'] = type
msg['eapol_length'] = length
data = data[4:]
if length != len(data):
raise Exception("EAPOL header length mismatch (%d != %d)" % (length, len(data)))
if type != 0:
raise Exception("Unexpected EAPOL header type: %d" % type)
# Parse EAP header
if len(data) < 4:
raise Exception("No room for EAP header")
code, identifier, length = struct.unpack('>BBH', data[0:4])
msg['eap_code'] = code
msg['eap_identifier'] = identifier
msg['eap_length'] = length
data = data[4:]
if msg['eapol_length'] != msg['eap_length']:
raise Exception("EAP header length mismatch (%d != %d)" % (msg['eapol_length'], length))
# Parse EAP expanded header
if len(data) < 1:
raise Exception("No EAP type included")
msg['eap_type'], = struct.unpack('B', data[0:1])
data = data[1:]
if msg['eap_type'] == 254:
if len(data) < 3 + 4:
raise Exception("Truncated EAP expanded header")
msg['eap_vendor_id'], msg['eap_vendor_type'] = struct.unpack('>LL', b'\x00' + data[0:7])
data = data[7:]
else:
raise Exception("Unexpected EAP type")
if msg['eap_vendor_id'] != WPS_VENDOR_ID_WFA:
raise Exception("Unexpected Vendor-Id")
if msg['eap_vendor_type'] != WPS_VENDOR_TYPE:
raise Exception("Unexpected Vendor-Type")
# Parse EAP-WSC header
if len(data) < 2:
raise Exception("Truncated EAP-WSC header")
msg['wsc_opcode'], msg['wsc_flags'] = struct.unpack('BB', data[0:2])
data = data[2:]
# Parse WSC attributes
msg['raw_attrs'] = data
attrs = {}
while len(data) > 0:
if len(data) < 4:
raise Exception("Truncated attribute header")
attr, length = struct.unpack('>HH', data[0:4])
data = data[4:]
if length > len(data):
raise Exception("Truncated attribute 0x%04x" % attr)
attrs[attr] = data[0:length]
data = data[length:]
msg['wsc_attrs'] = attrs
if ATTR_MSG_TYPE in attrs:
msg['wsc_msg_type'], = struct.unpack('B', attrs[ATTR_MSG_TYPE])
return msg
def recv_wsc_msg(dev, opcode, msg_type):
msg = get_wsc_msg(dev)
if msg['wsc_opcode'] != opcode or msg['wsc_msg_type'] != msg_type:
raise Exception("Unexpected Op-Code/MsgType")
return msg, msg['wsc_attrs'], msg['raw_attrs']
def build_wsc_attr(attr, payload):
_payload = payload if type(payload) == bytes else payload.encode()
return struct.pack('>HH', attr, len(_payload)) + _payload
def build_attr_msg_type(msg_type):
return build_wsc_attr(ATTR_MSG_TYPE, struct.pack('B', msg_type))
def build_eap_wsc(eap_code, eap_id, payload, opcode=WSC_MSG):
length = 4 + 8 + 2 + len(payload)
# EAPOL header
msg = struct.pack('>BBH', 2, 0, length)
# EAP header
msg += struct.pack('>BBH', eap_code, eap_id, length)
# EAP expanded header for EAP-WSC
msg += struct.pack('B', 254)
msg += struct.pack('>L', WPS_VENDOR_ID_WFA)[1:4]
msg += struct.pack('>L', WPS_VENDOR_TYPE)
# EAP-WSC header
msg += struct.pack('BB', opcode, 0)
# WSC attributes
msg += payload
return msg
def build_eap_success(eap_id):
length = 4
# EAPOL header
msg = struct.pack('>BBH', 2, 0, length)
# EAP header
msg += struct.pack('>BBH', 3, eap_id, length)
return msg
def build_eap_failure(eap_id):
length = 4
# EAPOL header
msg = struct.pack('>BBH', 2, 0, length)
# EAP header
msg += struct.pack('>BBH', 4, eap_id, length)
return msg
def send_wsc_msg(dev, src, msg):
res = dev.request("EAPOL_RX " + src + " " + binascii.hexlify(msg).decode())
if "OK" not in res:
raise Exception("EAPOL_RX failed")
group_5_prime = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF
group_5_generator = 2
def wsc_kdf(key, label, bits):
result = b''
i = 1
while len(result) * 8 < bits:
data = struct.pack('>L', i) + label.encode() + struct.pack('>L', bits)
m = hmac.new(key, data, hashlib.sha256)
result += m.digest()
i += 1
return result[0:bits // 8]
def wsc_keys(kdk):
keys = wsc_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation", 640)
authkey = keys[0:32]
keywrapkey = keys[32:48]
emsk = keys[48:80]
return authkey, keywrapkey, emsk
def wsc_dev_pw_half_psk(authkey, dev_pw):
m = hmac.new(authkey, dev_pw.encode(), hashlib.sha256)
return m.digest()[0:16]
def wsc_dev_pw_psk(authkey, dev_pw):
dev_pw_1 = dev_pw[0:len(dev_pw) // 2]
dev_pw_2 = dev_pw[len(dev_pw) // 2:]
psk1 = wsc_dev_pw_half_psk(authkey, dev_pw_1)
psk2 = wsc_dev_pw_half_psk(authkey, dev_pw_2)
return psk1, psk2
def build_attr_authenticator(authkey, prev_msg, curr_msg):
m = hmac.new(authkey, prev_msg + curr_msg, hashlib.sha256)
auth = m.digest()[0:8]
return build_wsc_attr(ATTR_AUTHENTICATOR, auth)
def build_attr_encr_settings(authkey, keywrapkey, data):
m = hmac.new(authkey, data, hashlib.sha256)
kwa = m.digest()[0:8]
data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = pad_len * struct.pack('B', pad_len)
data += ps
wrapped = aes.encrypt(data)
return build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
def decrypt_attr_encr_settings(authkey, keywrapkey, data):
if len(data) < 32 or len(data) % 16 != 0:
raise Exception("Unexpected Encrypted Settings length: %d" % len(data))
iv = data[0:16]
encr = data[16:]
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
decrypted = aes.decrypt(encr)
pad_len, = struct.unpack('B', decrypted[-1:])
if pad_len > len(decrypted):
raise Exception("Invalid padding in Encrypted Settings")
for i in range(-pad_len, -1):
if decrypted[i] != decrypted[-1]:
raise Exception("Invalid PS value in Encrypted Settings")
decrypted = decrypted[0:len(decrypted) - pad_len]
if len(decrypted) < 12:
raise Exception("Truncated Encrypted Settings plaintext")
kwa = decrypted[-12:]
attr, length = struct.unpack(">HH", kwa[0:4])
if attr != ATTR_KEY_WRAP_AUTH or length != 8:
raise Exception("Invalid KWA header")
kwa = kwa[4:]
decrypted = decrypted[0:len(decrypted) - 12]
m = hmac.new(authkey, decrypted, hashlib.sha256)
calc_kwa = m.digest()[0:8]
if kwa != calc_kwa:
raise Exception("KWA mismatch")
return decrypted
def zeropad_str(val, pad_len):
while len(val) < pad_len * 2:
val = '0' + val
return val
def wsc_dh_init():
# For now, use a hardcoded private key. In theory, this is supposed to be
# randomly selected.
own_private = 0x123456789
own_public = pow(group_5_generator, own_private, group_5_prime)
pk = binascii.unhexlify(zeropad_str(format(own_public, '02x'), 192))
return own_private, pk
def wsc_dh_kdf(peer_pk, own_private, mac_addr, e_nonce, r_nonce):
peer_public = int(binascii.hexlify(peer_pk), 16)
if peer_public < 2 or peer_public >= group_5_prime:
raise Exception("Invalid peer public key")
if pow(peer_public, (group_5_prime - 1) // 2, group_5_prime) != 1:
raise Exception("Unexpected Legendre symbol for peer public key")
shared_secret = pow(peer_public, own_private, group_5_prime)
ss = zeropad_str(format(shared_secret, "02x"), 192)
logger.debug("DH shared secret: " + ss)
dhkey = hashlib.sha256(binascii.unhexlify(ss)).digest()
logger.debug("DHKey: " + binascii.hexlify(dhkey).decode())
m = hmac.new(dhkey, e_nonce + mac_addr + r_nonce, hashlib.sha256)
kdk = m.digest()
logger.debug("KDK: " + binascii.hexlify(kdk).decode())
authkey, keywrapkey, emsk = wsc_keys(kdk)
logger.debug("AuthKey: " + binascii.hexlify(authkey).decode())
logger.debug("KeyWrapKey: " + binascii.hexlify(keywrapkey).decode())
logger.debug("EMSK: " + binascii.hexlify(emsk).decode())
return authkey, keywrapkey
def wsc_dev_pw_hash(authkey, dev_pw, e_pk, r_pk):
psk1, psk2 = wsc_dev_pw_psk(authkey, dev_pw)
logger.debug("PSK1: " + binascii.hexlify(psk1).decode())
logger.debug("PSK2: " + binascii.hexlify(psk2).decode())
# Note: Secret values are supposed to be random, but hardcoded values are
# fine for testing.
s1 = 16*b'\x77'
m = hmac.new(authkey, s1 + psk1 + e_pk + r_pk, hashlib.sha256)
hash1 = m.digest()
logger.debug("Hash1: " + binascii.hexlify(hash1).decode())
s2 = 16*b'\x88'
m = hmac.new(authkey, s2 + psk2 + e_pk + r_pk, hashlib.sha256)
hash2 = m.digest()
logger.debug("Hash2: " + binascii.hexlify(hash2).decode())
return s1, s2, hash1, hash2
def build_m1(eap_id, uuid_e, mac_addr, e_nonce, e_pk,
manufacturer='', model_name='', config_methods='\x00\x00'):
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M1)
attrs += build_wsc_attr(ATTR_UUID_E, uuid_e)
attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_PUBLIC_KEY, e_pk)
attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
attrs += build_wsc_attr(ATTR_CONFIG_METHODS, config_methods)
attrs += build_wsc_attr(ATTR_WPS_STATE, '\x00')
attrs += build_wsc_attr(ATTR_MANUFACTURER, manufacturer)
attrs += build_wsc_attr(ATTR_MODEL_NAME, model_name)
attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
attrs += build_wsc_attr(ATTR_DEV_NAME, '')
attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
m1 = build_eap_wsc(2, eap_id, attrs)
return m1, attrs
def build_m2(authkey, m1, eap_id, e_nonce, r_nonce, uuid_r, r_pk,
dev_pw_id='\x00\x00', eap_code=1):
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M2)
if e_nonce:
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
if r_nonce:
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
if r_pk:
attrs += build_wsc_attr(ATTR_PUBLIC_KEY, r_pk)
attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
attrs += build_wsc_attr(ATTR_DEV_NAME, '')
attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
attrs += build_attr_authenticator(authkey, m1, attrs)
m2 = build_eap_wsc(eap_code, eap_id, attrs)
return m2, attrs
def build_m2d(m1, eap_id, e_nonce, r_nonce, uuid_r, dev_pw_id=None, eap_code=1):
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M2D)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
#attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
attrs += build_wsc_attr(ATTR_DEV_NAME, '')
attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
if dev_pw_id:
attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
m2d = build_eap_wsc(eap_code, eap_id, attrs)
return m2d, attrs
def build_ack(eap_id, e_nonce, r_nonce, msg_type=WPS_WSC_ACK, eap_code=1):
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
if msg_type is not None:
attrs += build_attr_msg_type(msg_type)
if e_nonce:
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
if r_nonce:
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_ACK)
return msg, attrs
def build_nack(eap_id, e_nonce, r_nonce, config_error='\x00\x00',
msg_type=WPS_WSC_NACK, eap_code=1):
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
if msg_type is not None:
attrs += build_attr_msg_type(msg_type)
if e_nonce:
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
if r_nonce:
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
if config_error:
attrs += build_wsc_attr(ATTR_CONFIG_ERROR, config_error)
msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_NACK)
return msg, attrs
def test_wps_ext(dev, apdev):
"""WPS against external implementation"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
wsc_start_id = msg['eap_identifier']
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
authkey, keywrapkey = wsc_dh_kdf(m2_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, e_nonce,
m2_attrs[ATTR_REGISTRAR_NONCE])
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk,
m2_attrs[ATTR_PUBLIC_KEY])
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
m2_attrs[ATTR_REGISTRAR_NONCE])
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
m2_attrs[ATTR_REGISTRAR_NONCE])
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
logger.debug("Receive M6 from AP")
msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
logger.debug("Send M7 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
m2_attrs[ATTR_REGISTRAR_NONCE])
data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
raw_m7_attrs = attrs
send_wsc_msg(hapd, addr, m7)
logger.debug("Receive M8 from AP")
msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
m8_cred = decrypt_attr_encr_settings(authkey, keywrapkey,
m8_attrs[ATTR_ENCR_SETTINGS])
logger.debug("M8 Credential: " + binascii.hexlify(m8_cred).decode())
logger.debug("Prepare WSC_Done")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_WSC_DONE)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
m2_attrs[ATTR_REGISTRAR_NONCE])
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
# Do not send WSC_Done yet to allow exchangw with STA complete before the
# AP disconnects.
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
eap_id = wsc_start_id
logger.debug("Send WSC/Start to STA")
wsc_start = build_eap_wsc(1, eap_id, b'', opcode=WSC_Start)
send_wsc_msg(dev[0], bssid, wsc_start)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
raw_m4_attrs = attrs
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 from STA")
msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
m1_attrs[ATTR_ENROLLEE_NONCE])
data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
raw_m6_attrs = attrs
m6 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m6)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M7 from STA")
msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
logger.debug("Send M8 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M8)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
raw_m8_attrs = attrs
m8 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m8)
eap_id = (eap_id + 1) % 256
ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=5)
if ev is None:
raise Exception("wpa_supplicant did not report credential")
logger.debug("Receive WSC_Done from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
logger.debug("Send WSC_Done to AP")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
send_wsc_msg(hapd, addr, wsc_done)
ev = hapd.wait_event(["WPS-REG-SUCCESS"], timeout=5)
if ev is None:
raise Exception("hostapd did not report WPS success")
dev[0].wait_connected()
def wps_start_kwa(dev, apdev):
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
return r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs
def wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id):
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_kwa_proto_no_kwa(dev, apdev):
"""WPS and KWA error: No KWA attribute"""
r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
# Encrypted Settings without KWA
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = pad_len * struct.pack('B', pad_len)
data += ps
wrapped = aes.encrypt(data)
attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
def test_wps_ext_kwa_proto_data_after_kwa(dev, apdev):
"""WPS and KWA error: Data after KWA"""
r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
# Encrypted Settings and data after KWA
m = hmac.new(authkey, data, hashlib.sha256)
kwa = m.digest()[0:8]
data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
data += build_wsc_attr(ATTR_VENDOR_EXT, "1234567890")
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = pad_len * struct.pack('B', pad_len)
data += ps
wrapped = aes.encrypt(data)
attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
def test_wps_ext_kwa_proto_kwa_mismatch(dev, apdev):
"""WPS and KWA error: KWA mismatch"""
r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
# Encrypted Settings and KWA with incorrect value
data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, 8*'\x00')
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = pad_len * struct.pack('B', pad_len)
data += ps
wrapped = aes.encrypt(data)
attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
def wps_run_cred_proto(dev, apdev, m8_cred, connect=False, no_connect=False):
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
raw_m4_attrs = attrs
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 from STA")
msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
m1_attrs[ATTR_ENROLLEE_NONCE])
data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
raw_m6_attrs = attrs
m6 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m6)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M7 from STA")
msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
logger.debug("Send M8 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M8)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
raw_m8_attrs = attrs
m8 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m8)
eap_id = (eap_id + 1) % 256
if no_connect:
logger.debug("Receive WSC_Done from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
dev[0].request("REMOVE_NETWORK all")
elif connect:
logger.debug("Receive WSC_Done from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_connected()
else:
# Verify STA NACK's the credential
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def build_cred(nw_idx='\x01', ssid='test-wps-conf', auth_type='\x00\x20',
encr_type='\x00\x08', nw_key="12345678",
mac_addr='\x00\x00\x00\x00\x00\x00'):
attrs = b''
if nw_idx is not None:
attrs += build_wsc_attr(ATTR_NETWORK_INDEX, nw_idx)
if ssid is not None:
attrs += build_wsc_attr(ATTR_SSID, ssid)
if auth_type is not None:
attrs += build_wsc_attr(ATTR_AUTH_TYPE, auth_type)
if encr_type is not None:
attrs += build_wsc_attr(ATTR_ENCR_TYPE, encr_type)
if nw_key is not None:
attrs += build_wsc_attr(ATTR_NETWORK_KEY, nw_key)
if mac_addr is not None:
attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
return build_wsc_attr(ATTR_CRED, attrs)
def test_wps_ext_cred_proto_success(dev, apdev):
"""WPS and Credential: success"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr)
wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
def test_wps_ext_cred_proto_mac_addr_mismatch(dev, apdev):
"""WPS and Credential: MAC Address mismatch"""
m8_cred = build_cred()
wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
def test_wps_ext_cred_proto_zero_padding(dev, apdev):
"""WPS and Credential: zeropadded attributes"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, ssid='test-wps-conf\x00',
nw_key="12345678\x00")
wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
def test_wps_ext_cred_proto_ssid_missing(dev, apdev):
"""WPS and Credential: SSID missing"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, ssid=None)
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_ssid_zero_len(dev, apdev):
"""WPS and Credential: Zero-length SSID"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, ssid="")
wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
def test_wps_ext_cred_proto_auth_type_missing(dev, apdev):
"""WPS and Credential: Auth Type missing"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, auth_type=None)
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_encr_type_missing(dev, apdev):
"""WPS and Credential: Encr Type missing"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, encr_type=None)
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_network_key_missing(dev, apdev):
"""WPS and Credential: Network Key missing"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, nw_key=None)
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_network_key_missing_open(dev, apdev):
"""WPS and Credential: Network Key missing (open)"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, auth_type='\x00\x01',
encr_type='\x00\x01', nw_key=None, ssid="foo")
wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
def test_wps_ext_cred_proto_mac_addr_missing(dev, apdev):
"""WPS and Credential: MAC Address missing"""
m8_cred = build_cred(mac_addr=None)
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_invalid_encr_type(dev, apdev):
"""WPS and Credential: Invalid Encr Type"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = build_cred(mac_addr=mac_addr, encr_type='\x00\x00')
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_cred_proto_missing_cred(dev, apdev):
"""WPS and Credential: Missing Credential"""
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
m8_cred = b''
wps_run_cred_proto(dev, apdev, m8_cred)
def test_wps_ext_proto_m2_no_public_key(dev, apdev):
"""WPS and no Public Key in M2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, None)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
# Verify STA NACK's the credential
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m2_invalid_public_key(dev, apdev):
"""WPS and invalid Public Key in M2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, 192*b'\xff')
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
# Verify STA NACK's the credential
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m2_public_key_oom(dev, apdev):
"""WPS and Public Key OOM in M2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
with alloc_fail(dev[0], 1, "wpabuf_alloc_copy;wps_process_pubkey"):
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
# Verify STA NACK's the credential
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_nack_m3(dev, apdev):
"""WPS and NACK M3"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, config_error='\x01\x23')
send_wsc_msg(dev[0], bssid, msg)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("Failure not reported")
if "msg=7 config_error=291" not in ev:
raise Exception("Unexpected failure reason: " + ev)
def test_wps_ext_proto_nack_m5(dev, apdev):
"""WPS and NACK M5"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
raw_m4_attrs = attrs
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 from STA")
msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, config_error='\x01\x24')
send_wsc_msg(dev[0], bssid, msg)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("Failure not reported")
if "msg=9 config_error=292" not in ev:
raise Exception("Unexpected failure reason: " + ev)
def wps_nack_m3(dev, apdev):
pin = "00000000"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk, dev_pw_id='\x00\x04')
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid
def test_wps_ext_proto_nack_m3_no_config_error(dev, apdev):
"""WPS and NACK M3 missing Config Error"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, e_nonce, r_nonce, config_error=None)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_no_e_nonce(dev, apdev):
"""WPS and NACK M3 missing E-Nonce"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, None, r_nonce)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_e_nonce_mismatch(dev, apdev):
"""WPS and NACK M3 E-Nonce mismatch"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, 16*'\x00', r_nonce)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_no_r_nonce(dev, apdev):
"""WPS and NACK M3 missing R-Nonce"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, e_nonce, None)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_r_nonce_mismatch(dev, apdev):
"""WPS and NACK M3 R-Nonce mismatch"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, e_nonce, 16*'\x00')
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_no_msg_type(dev, apdev):
"""WPS and NACK M3 no Message Type"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=None)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_invalid_msg_type(dev, apdev):
"""WPS and NACK M3 invalid Message Type"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=123)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_nack_m3_invalid_attr(dev, apdev):
"""WPS and NACK M3 invalid attribute"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
attrs = b'\x10\x10\x00'
msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_NACK)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_no_e_nonce(dev, apdev):
"""WPS and ACK M3 missing E-Nonce"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, None, r_nonce)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_e_nonce_mismatch(dev, apdev):
"""WPS and ACK M3 E-Nonce mismatch"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, 16*'\x00', r_nonce)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_no_r_nonce(dev, apdev):
"""WPS and ACK M3 missing R-Nonce"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, e_nonce, None)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_r_nonce_mismatch(dev, apdev):
"""WPS and ACK M3 R-Nonce mismatch"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, e_nonce, 16*'\x00')
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_no_msg_type(dev, apdev):
"""WPS and ACK M3 no Message Type"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=None)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_invalid_msg_type(dev, apdev):
"""WPS and ACK M3 invalid Message Type"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send NACK to STA")
msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=123)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3_invalid_attr(dev, apdev):
"""WPS and ACK M3 invalid attribute"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send ACK to STA")
attrs = b'\x10\x10\x00'
msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_ACK)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def test_wps_ext_proto_ack_m3(dev, apdev):
"""WPS and ACK M3"""
eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
logger.debug("Send ACK to STA")
msg, attrs = build_ack(eap_id, e_nonce, r_nonce)
send_wsc_msg(dev[0], bssid, msg)
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
dev[0].flush_scan_cache()
def wps_to_m3_helper(dev, apdev):
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, e_pk = wsc_dh_init()
logger.debug("Receive M1 from STA")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
eap_id = (msg['eap_identifier'] + 1) % 256
authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
m1_attrs[ATTR_PUBLIC_KEY],
e_pk)
logger.debug("Send M2 to STA")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
m1_attrs[ATTR_ENROLLEE_NONCE],
r_nonce, uuid_r, e_pk)
send_wsc_msg(dev[0], bssid, m2)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M3 from STA")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
return eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey
def wps_to_m3(dev, apdev):
eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s1, raw_m3_attrs, authkey, keywrapkey
def wps_to_m5(dev, apdev):
eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
raw_m4_attrs = attrs
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 from STA")
msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s2, raw_m5_attrs, authkey, keywrapkey
def test_wps_ext_proto_m4_missing_r_hash1(dev, apdev):
"""WPS and no R-Hash1 in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
#attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m4_missing_r_hash2(dev, apdev):
"""WPS and no R-Hash2 in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
#attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m4_missing_r_snonce1(dev, apdev):
"""WPS and no R-SNonce1 in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
#data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
data = b''
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m4_invalid_pad_string(dev, apdev):
"""WPS and invalid pad string in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
m = hmac.new(authkey, data, hashlib.sha256)
kwa = m.digest()[0:8]
data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', pad_len - 1)
data += ps
wrapped = aes.encrypt(data)
attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m4_invalid_pad_value(dev, apdev):
"""WPS and invalid pad value in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
m = hmac.new(authkey, data, hashlib.sha256)
kwa = m.digest()[0:8]
data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
iv = 16*b'\x99'
aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
pad_len = 16 - len(data) % 16
ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', 255)
data += ps
wrapped = aes.encrypt(data)
attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m4_no_encr_settings(dev, apdev):
"""WPS and no Encr Settings in M4"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
logger.debug("Send M4 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
attrs += build_attr_authenticator(authkey, m3, attrs)
m4 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m4)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M5 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m6_missing_r_snonce2(dev, apdev):
"""WPS and no R-SNonce2 in M6"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
#data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
data = b''
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m5, attrs)
m6 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m6)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M7 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m6_no_encr_settings(dev, apdev):
"""WPS and no Encr Settings in M6"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
#attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m5, attrs)
m6 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m6)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M7 (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def test_wps_ext_proto_m8_no_encr_settings(dev, apdev):
"""WPS and no Encr Settings in M6"""
eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, m5, attrs)
raw_m6_attrs = attrs
m6 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m6)
eap_id = (eap_id + 1) % 256
logger.debug("Receive M7 from STA")
msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
logger.debug("Send M8 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M8)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
#attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
raw_m8_attrs = attrs
m8 = build_eap_wsc(1, eap_id, attrs)
send_wsc_msg(dev[0], bssid, m8)
logger.debug("Receive WSC_Done (NACK) from STA")
msg = get_wsc_msg(dev[0])
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_Nack")
dev[0].request("WPS_CANCEL")
send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
dev[0].wait_disconnected()
def wps_start_ext_reg(apdev, dev):
addr = dev.own_addr()
bssid = apdev['bssid']
ssid = "test-wps-conf"
appin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"ap_pin": appin}
hapd = hostapd.add_ap(apdev, params)
dev.scan_for_bss(bssid, freq="2412")
hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
dev.request("WPS_REG " + bssid + " " + appin)
return addr, bssid, hapd
def wps_run_ap_settings_proto(dev, apdev, ap_settings, success):
addr, bssid, hapd = wps_start_ext_reg(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive M1 from AP")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
mac_addr = m1_attrs[ATTR_MAC_ADDR]
e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
e_pk = m1_attrs[ATTR_PUBLIC_KEY]
appin = '12345670'
uuid_r = 16*b'\x33'
r_nonce = 16*b'\x44'
own_private, r_pk = wsc_dh_init()
authkey, keywrapkey = wsc_dh_kdf(e_pk, own_private, mac_addr, e_nonce,
r_nonce)
r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, appin, e_pk, r_pk)
logger.debug("Send M2 to AP")
m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, msg['eap_identifier'],
e_nonce, r_nonce, uuid_r, r_pk, eap_code=2)
send_wsc_msg(hapd, addr, m2)
logger.debug("Receive M3 from AP")
msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M3)
logger.debug("Send M4 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M4)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
raw_m4_attrs = attrs
m4 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m4)
logger.debug("Receive M5 from AP")
msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M5)
logger.debug("Send M6 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M6)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
raw_m6_attrs = attrs
m6 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m6)
logger.debug("Receive M7 from AP")
msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M7)
logger.debug("Send M8 to STA")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M8)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
if ap_settings:
attrs += build_attr_encr_settings(authkey, keywrapkey, ap_settings)
attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
raw_m8_attrs = attrs
m8 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m8)
if success:
ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
if ev is None:
raise Exception("New AP settings not reported")
logger.debug("Receive WSC_Done from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Done:
raise Exception("Unexpected message - expected WSC_Done")
logger.debug("Send WSC_ACK to AP")
ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
eap_code=2)
send_wsc_msg(hapd, addr, ack)
dev[0].wait_disconnected()
else:
ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
if ev is None:
raise Exception("WPS failure not reported")
logger.debug("Receive WSC_NACK from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_NACK")
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
eap_code=2)
send_wsc_msg(hapd, addr, nack)
dev[0].wait_disconnected()
def test_wps_ext_ap_settings_success(dev, apdev):
"""WPS and AP Settings: success"""
ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
ap_settings += build_wsc_attr(ATTR_SSID, "test")
ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
@remote_compatible
def test_wps_ext_ap_settings_missing(dev, apdev):
"""WPS and AP Settings: missing"""
wps_run_ap_settings_proto(dev, apdev, None, False)
@remote_compatible
def test_wps_ext_ap_settings_mac_addr_mismatch(dev, apdev):
"""WPS and AP Settings: MAC Address mismatch"""
ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
ap_settings += build_wsc_attr(ATTR_SSID, "test")
ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
ap_settings += build_wsc_attr(ATTR_MAC_ADDR, '\x00\x00\x00\x00\x00\x00')
wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
@remote_compatible
def test_wps_ext_ap_settings_mac_addr_missing(dev, apdev):
"""WPS and AP Settings: missing MAC Address"""
ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
ap_settings += build_wsc_attr(ATTR_SSID, "test")
ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
@remote_compatible
def test_wps_ext_ap_settings_reject_encr_type(dev, apdev):
"""WPS and AP Settings: reject Encr Type"""
ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
ap_settings += build_wsc_attr(ATTR_SSID, "test")
ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x00')
ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
@remote_compatible
def test_wps_ext_ap_settings_m2d(dev, apdev):
"""WPS and AP Settings: M2D"""
addr, bssid, hapd = wps_start_ext_reg(apdev[0], dev[0])
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive M1 from AP")
msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
r_nonce = 16*'\x44'
uuid_r = 16*'\x33'
logger.debug("Send M2D to AP")
m2d, raw_m2d_attrs = build_m2d(raw_m1_attrs, msg['eap_identifier'],
e_nonce, r_nonce, uuid_r,
dev_pw_id='\x00\x00', eap_code=2)
send_wsc_msg(hapd, addr, m2d)
ev = hapd.wait_event(["WPS-M2D"], timeout=5)
if ev is None:
raise Exception("M2D not reported")
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
def wps_wait_ap_nack(hapd, dev, e_nonce, r_nonce):
logger.debug("Receive WSC_NACK from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_NACK:
raise Exception("Unexpected message - expected WSC_NACK")
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
eap_code=2)
send_wsc_msg(hapd, dev.own_addr(), nack)
dev.wait_disconnected()
@remote_compatible
def test_wps_ext_m3_missing_e_hash1(dev, apdev):
"""WPS proto: M3 missing E-Hash1"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
#attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m3_missing_e_hash2(dev, apdev):
"""WPS proto: M3 missing E-Hash2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
#attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m5_missing_e_snonce1(dev, apdev):
"""WPS proto: M5 missing E-SNonce1"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
#data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
data = b''
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m5_e_snonce1_mismatch(dev, apdev):
"""WPS proto: M5 E-SNonce1 mismatch"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, 16*'\x00')
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
def test_wps_ext_m7_missing_e_snonce2(dev, apdev):
"""WPS proto: M7 missing E-SNonce2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
logger.debug("Receive M6 from AP")
msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
logger.debug("Send M7 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
#data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
data = b''
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
raw_m7_attrs = attrs
send_wsc_msg(hapd, addr, m7)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m7_e_snonce2_mismatch(dev, apdev):
"""WPS proto: M7 E-SNonce2 mismatch"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
logger.debug("Receive M6 from AP")
msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
logger.debug("Send M7 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE2, 16*'\x00')
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
raw_m7_attrs = attrs
send_wsc_msg(hapd, addr, m7)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m1_pubkey_oom(dev, apdev):
"""WPS proto: M1 PubKey OOM"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*'\x11'
e_nonce = 16*'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
with alloc_fail(hapd, 1, "wpabuf_alloc_copy;wps_process_pubkey"):
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
wps_wait_eap_failure(hapd, dev[0])
def wps_wait_eap_failure(hapd, dev):
ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP-Failure not reported")
dev.wait_disconnected()
@remote_compatible
def test_wps_ext_m3_m1(dev, apdev):
"""WPS proto: M3 replaced with M1"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3(M1) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M1)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m5_m3(dev, apdev):
"""WPS proto: M5 replaced with M3"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5(M3) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m3_m2(dev, apdev):
"""WPS proto: M3 replaced with M2"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3(M2) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M2)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m3_m5(dev, apdev):
"""WPS proto: M3 replaced with M5"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3(M5) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m3_m7(dev, apdev):
"""WPS proto: M3 replaced with M7"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3(M7) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m3_done(dev, apdev):
"""WPS proto: M3 replaced with WSC_Done"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3(WSC_Done) to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_WSC_DONE)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, addr, m3)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_nack_invalid(dev, apdev):
"""WPS proto: M2 followed by invalid NACK"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_NACK to AP")
attrs = b'\x10\x00\x00'
nack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_NACK)
send_wsc_msg(hapd, addr, nack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_nack_no_msg_type(dev, apdev):
"""WPS proto: M2 followed by NACK without Msg Type"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
msg_type=None, eap_code=2)
send_wsc_msg(hapd, addr, nack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_nack_invalid_msg_type(dev, apdev):
"""WPS proto: M2 followed by NACK with invalid Msg Type"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
msg_type=WPS_WSC_ACK, eap_code=2)
send_wsc_msg(hapd, addr, nack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_nack_e_nonce_mismatch(dev, apdev):
"""WPS proto: M2 followed by NACK with e-nonce mismatch"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], 16*b'\x00', r_nonce,
eap_code=2)
send_wsc_msg(hapd, addr, nack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_nack_no_config_error(dev, apdev):
"""WPS proto: M2 followed by NACK without Config Error"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_NACK to AP")
nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
config_error=None, eap_code=2)
send_wsc_msg(hapd, addr, nack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_ack_invalid(dev, apdev):
"""WPS proto: M2 followed by invalid ACK"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_ACK to AP")
attrs = b'\x10\x00\x00'
ack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_ACK)
send_wsc_msg(hapd, addr, ack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_ack(dev, apdev):
"""WPS proto: M2 followed by ACK"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_ACK to AP")
ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce, eap_code=2)
send_wsc_msg(hapd, addr, ack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_ack_no_msg_type(dev, apdev):
"""WPS proto: M2 followed by ACK missing Msg Type"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_ACK to AP")
ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
msg_type=None, eap_code=2)
send_wsc_msg(hapd, addr, ack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_ack_invalid_msg_type(dev, apdev):
"""WPS proto: M2 followed by ACK with invalid Msg Type"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_ACK to AP")
ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
msg_type=WPS_WSC_NACK, eap_code=2)
send_wsc_msg(hapd, addr, ack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m2_ack_e_nonce_mismatch(dev, apdev):
"""WPS proto: M2 followed by ACK with e-nonce mismatch"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send WSC_ACK to AP")
ack, attrs = build_ack(msg['eap_identifier'], 16*b'\x00', r_nonce,
eap_code=2)
send_wsc_msg(hapd, addr, ack)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m1_invalid(dev, apdev):
"""WPS proto: M1 failing parsing"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
logger.debug("Send M1 to AP")
attrs = b'\x10\x00\x00'
m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m1)
wps_wait_eap_failure(hapd, dev[0])
def test_wps_ext_m1_missing_msg_type(dev, apdev):
"""WPS proto: M1 missing Msg Type"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
logger.debug("Send M1 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m1)
wps_wait_ap_nack(hapd, dev[0], 16*b'\x00', 16*b'\x00')
def wps_ext_wsc_done(dev, apdev):
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
logger.debug("Receive M6 from AP")
msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
logger.debug("Send M7 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
raw_m7_attrs = attrs
send_wsc_msg(hapd, addr, m7)
logger.debug("Receive M8 from AP")
msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
return hapd, msg, e_nonce, r_nonce
@remote_compatible
def test_wps_ext_wsc_done_invalid(dev, apdev):
"""WPS proto: invalid WSC_Done"""
hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
logger.debug("Send WSC_Done to AP")
attrs = b'\x10\x00\x00'
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_wsc_done_no_msg_type(dev, apdev):
"""WPS proto: invalid WSC_Done"""
hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
logger.debug("Send WSC_Done to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
#attrs += build_attr_msg_type(WPS_WSC_DONE)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_wsc_done_wrong_msg_type(dev, apdev):
"""WPS proto: WSC_Done with wrong Msg Type"""
hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
logger.debug("Send WSC_Done to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_WSC_ACK)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_wsc_done_no_e_nonce(dev, apdev):
"""WPS proto: WSC_Done without e_nonce"""
hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
logger.debug("Send WSC_Done to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_WSC_DONE)
#attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
wps_wait_eap_failure(hapd, dev[0])
def test_wps_ext_wsc_done_no_r_nonce(dev, apdev):
"""WPS proto: WSC_Done without r_nonce"""
hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
logger.debug("Send WSC_Done to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_WSC_DONE)
attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
#attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
wps_wait_eap_failure(hapd, dev[0])
@remote_compatible
def test_wps_ext_m7_no_encr_settings(dev, apdev):
"""WPS proto: M7 without Encr Settings"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk)
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
r_pk = m2_attrs[ATTR_PUBLIC_KEY]
authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
r_nonce)
e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
logger.debug("Send M3 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M3)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
raw_m3_attrs = attrs
m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m3)
logger.debug("Receive M4 from AP")
msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
logger.debug("Send M5 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M5)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
raw_m5_attrs = attrs
m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
send_wsc_msg(hapd, addr, m5)
logger.debug("Receive M6 from AP")
msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
logger.debug("Send M7 to AP")
attrs = build_wsc_attr(ATTR_VERSION, '\x10')
attrs += build_attr_msg_type(WPS_M7)
attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
#data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
#attrs += build_attr_encr_settings(authkey, keywrapkey, data)
attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
raw_m7_attrs = attrs
send_wsc_msg(hapd, addr, m7)
wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
@remote_compatible
def test_wps_ext_m1_workaround(dev, apdev):
"""WPS proto: M1 Manufacturer/Model workaround"""
pin = "12345670"
addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
wps_ext_eap_identity_req(dev[0], hapd, bssid)
wps_ext_eap_identity_resp(hapd, dev[0], addr)
logger.debug("Receive WSC/Start from AP")
msg = get_wsc_msg(hapd)
if msg['wsc_opcode'] != WSC_Start:
raise Exception("Unexpected Op-Code for WSC/Start")
mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
uuid_e = 16*b'\x11'
e_nonce = 16*b'\x22'
own_private, e_pk = wsc_dh_init()
logger.debug("Send M1 to AP")
m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
e_nonce, e_pk, manufacturer='Apple TEST',
model_name='AirPort', config_methods=b'\xff\xff')
send_wsc_msg(hapd, addr, m1)
logger.debug("Receive M2 from AP")
msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
@remote_compatible
def test_ap_wps_disable_enable(dev, apdev):
"""WPS and DISABLE/ENABLE AP"""
hapd = wps_start_ap(apdev[0])
hapd.disable()
hapd.enable()
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
def test_ap_wps_upnp_web_oom(dev, apdev, params):
"""hostapd WPS UPnP web OOM"""
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
hapd = add_ssdp_ap(apdev[0], ap_uuid)
location = ssdp_get_location(ap_uuid)
url = urlparse(location)
urls = upnp_get_urls(location)
eventurl = urlparse(urls['event_sub_url'])
ctrlurl = urlparse(urls['control_url'])
conn = HTTPConnection(url.netloc)
with alloc_fail(hapd, 1, "web_connection_parse_get"):
conn.request("GET", "/wps_device.xml")
try:
resp = conn.getresponse()
except:
pass
conn = HTTPConnection(url.netloc)
conn.request("GET", "/unknown")
resp = conn.getresponse()
if resp.status != 404:
raise Exception("Unexpected HTTP result for unknown URL: %d" + resp.status)
with alloc_fail(hapd, 1, "web_connection_parse_get"):
conn.request("GET", "/unknown")
try:
resp = conn.getresponse()
print(resp.status)
except:
pass
conn = HTTPConnection(url.netloc)
conn.request("GET", "/wps_device.xml")
resp = conn.getresponse()
if resp.status != 200:
raise Exception("GET /wps_device.xml failed")
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
if resp.status != 200:
raise Exception("GetDeviceInfo failed")
with alloc_fail(hapd, 1, "web_process_get_device_info"):
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
if resp.status != 500:
raise Exception("Internal error not reported from GetDeviceInfo OOM")
with alloc_fail(hapd, 1, "wps_build_m1;web_process_get_device_info"):
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
if resp.status != 500:
raise Exception("Internal error not reported from GetDeviceInfo OOM")
with alloc_fail(hapd, 1, "wpabuf_alloc;web_connection_send_reply"):
conn = HTTPConnection(url.netloc)
try:
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
except:
pass
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
if resp.status != 200:
raise Exception("GetDeviceInfo failed")
# No NewWLANEventType in PutWLANResponse NewMessage
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse", newmsg="foo")
if resp.status != 600:
raise Exception("Unexpected HTTP response: %d" % resp.status)
# No NewWLANEventMAC in PutWLANResponse NewMessage
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
newmsg="foo", neweventtype="1")
if resp.status != 600:
raise Exception("Unexpected HTTP response: %d" % resp.status)
# Invalid NewWLANEventMAC in PutWLANResponse NewMessage
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
newmsg="foo", neweventtype="1",
neweventmac="foo")
if resp.status != 600:
raise Exception("Unexpected HTTP response: %d" % resp.status)
# Workaround for NewWLANEventMAC in PutWLANResponse NewMessage
# Ignored unexpected PutWLANResponse WLANEventType 1
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
newmsg="foo", neweventtype="1",
neweventmac="00.11.22.33.44.55")
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
# PutWLANResponse NewMessage with invalid EAP message
conn = HTTPConnection(url.netloc)
resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
newmsg="foo", neweventtype="2",
neweventmac="00:11:22:33:44:55")
if resp.status != 200:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "web_connection_parse_subscribe"):
conn = HTTPConnection(url.netloc)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
try:
resp = conn.getresponse()
except:
pass
with alloc_fail(hapd, 1, "dup_binstr;web_connection_parse_subscribe"):
conn = HTTPConnection(url.netloc)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
resp = conn.getresponse()
if resp.status != 500:
raise Exception("Unexpected HTTP response: %d" % resp.status)
with alloc_fail(hapd, 1, "wpabuf_alloc;web_connection_parse_unsubscribe"):
conn = HTTPConnection(url.netloc)
headers = {"callback": '<http://127.0.0.1:12345/event>',
"NT": "upnp:event",
"timeout": "Second-1234"}
conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
try:
resp = conn.getresponse()
except:
pass
with alloc_fail(hapd, 1, "web_connection_unimplemented"):
conn = HTTPConnection(url.netloc)
conn.request("HEAD", "/wps_device.xml")
try:
resp = conn.getresponse()
except:
pass
def test_ap_wps_frag_ack_oom(dev, apdev):
"""WPS and fragment ack OOM"""
dev[0].request("SET wps_fragment_size 50")
hapd = wps_start_ap(apdev[0])
with alloc_fail(hapd, 1, "eap_wsc_build_frag_ack"):
wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
def wait_scan_stopped(dev):
dev.request("ABORT_SCAN")
for i in range(50):
res = dev.get_driver_status_field("scan_state")
if "SCAN_STARTED" not in res and "SCAN_REQUESTED" not in res:
break
logger.debug("Waiting for scan to complete")
time.sleep(0.1)
@remote_compatible
def test_ap_wps_eap_wsc_errors(dev, apdev):
"""WPS and EAP-WSC error cases"""
ssid = "test-wps-conf-pin"
appin = "12345670"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"fragment_size": "300", "ap_pin": appin}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
dev[0].wps_reg(bssid, appin + " new_ssid=a", "new ssid", "WPA2PSK", "CCMP",
"new passphrase", no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
if ev is None:
raise Exception("WPS-FAIL not reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
dev[0].wps_reg(bssid, appin, "new ssid", "FOO", "CCMP",
"new passphrase", no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
if ev is None:
raise Exception("WPS-FAIL not reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
dev[0].wps_reg(bssid, appin, "new ssid", "WPA2PSK", "FOO",
"new passphrase", no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
if ev is None:
raise Exception("WPS-FAIL not reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
dev[0].wps_reg(bssid, appin + "new_key=a", "new ssid", "WPA2PSK", "CCMP",
"new passphrase", no_wait=True)
ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
if ev is None:
raise Exception("WPS-FAIL not reported")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
tests = ["eap_wsc_init",
"eap_msg_alloc;eap_wsc_build_msg",
"wpabuf_alloc;eap_wsc_process_fragment"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].request("WPS_PIN %s %s" % (bssid, pin))
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
tests = [(1, "wps_decrypt_encr_settings"),
(2, "hmac_sha256;wps_derive_psk")]
for count, func in tests:
hapd.request("WPS_PIN any " + pin)
with fail_test(dev[0], count, func):
dev[0].request("WPS_PIN %s %s" % (bssid, pin))
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sm_build_expanded_nak"):
dev[0].wps_reg(bssid, appin + " new_ssid=a", "new ssid", "WPA2PSK",
"CCMP", "new passphrase", no_wait=True)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("WPS_CANCEL")
dev[0].wait_disconnected()
wait_scan_stopped(dev[0])
dev[0].dump_monitor()
def test_ap_wps_eap_wsc(dev, apdev):
"""WPS and EAP-WSC in network profile"""
params = int_eap_server_params()
params["wps_state"] = "2"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
logger.info("Unexpected identity")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-unexpected",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("No phase1 parameter")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("No PIN/PBC in phase1")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="foo", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Invalid pkhash in phase1")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="foo pkhash=q pbc=1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Zero fragment_size")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
fragment_size="0", phase1="pin=12345670", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["WPS-M2D"], timeout=5)
if ev is None:
raise Exception("No M2D seen")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Missing new_auth")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="pin=12345670 new_ssid=aa", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Missing new_encr")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="pin=12345670 new_auth=WPA2PSK new_ssid=aa", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Missing new_key")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="pin=12345670 new_auth=WPA2PSK new_ssid=aa new_encr=CCMP",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_ap_wps_and_bss_limit(dev, apdev):
"""WPS and wpa_supplicant BSS entry limit"""
try:
_test_ap_wps_and_bss_limit(dev, apdev)
finally:
dev[0].request("SET bss_max_count 200")
pass
def _test_ap_wps_and_bss_limit(dev, apdev):
params = {"ssid": "test-wps", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "test-wps-2", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "1234567890", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
hapd2 = hostapd.add_ap(apdev[1], params)
id = dev[1].add_network()
dev[1].set_network(id, "mode", "2")
dev[1].set_network_quoted(id, "ssid", "wpas-ap-no-wps")
dev[1].set_network_quoted(id, "psk", "12345678")
dev[1].set_network(id, "frequency", "2462")
dev[1].set_network(id, "scan_freq", "2462")
dev[1].set_network(id, "wps_disabled", "1")
dev[1].select_network(id)
id = dev[2].add_network()
dev[2].set_network(id, "mode", "2")
dev[2].set_network_quoted(id, "ssid", "wpas-ap")
dev[2].set_network_quoted(id, "psk", "12345678")
dev[2].set_network(id, "frequency", "2437")
dev[2].set_network(id, "scan_freq", "2437")
dev[2].select_network(id)
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
id = wpas.add_network()
wpas.set_network(id, "mode", "2")
wpas.set_network_quoted(id, "ssid", "wpas-ap")
wpas.set_network_quoted(id, "psk", "12345678")
wpas.set_network(id, "frequency", "2437")
wpas.set_network(id, "scan_freq", "2437")
wpas.select_network(id)
dev[1].wait_connected()
dev[2].wait_connected()
wpas.wait_connected()
wpas.request("WPS_PIN any 12345670")
hapd.request("WPS_PBC")
hapd2.request("WPS_PBC")
dev[0].request("SET bss_max_count 1")
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "testing")
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "testing")
dev[0].set_network(id, "key_mgmt", "WPS")
dev[0].request("WPS_PBC")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
dev[0].request("WPS_CANCEL")
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "testing")
dev[0].set_network(id, "key_mgmt", "WPS")
dev[0].scan(freq="2412")
def test_ap_wps_pbc_2ap(dev, apdev):
"""WPS PBC with two APs advertising same SSID"""
params = {"ssid": "wps", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "wps", "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "123456789", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_independent": "1"}
hapd2 = hostapd.add_ap(apdev[1], params)
hapd.request("WPS_PBC")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.dump_monitor()
wpas.flush_scan_cache()
wpas.scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
wpas.scan_for_bss(apdev[1]['bssid'], freq="2412")
wpas.request("WPS_PBC")
wpas.wait_connected()
wpas.request("DISCONNECT")
hapd.request("DISABLE")
hapd2.request("DISABLE")
wpas.flush_scan_cache()
def test_ap_wps_er_enrollee_to_conf_ap(dev, apdev):
"""WPS ER enrolling a new device to a configured AP"""
try:
_test_ap_wps_er_enrollee_to_conf_ap(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_enrollee_to_conf_ap(dev, apdev):
ssid = "wps-er-enrollee-to-conf-ap"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
id = dev[0].connect(ssid, psk="12345678", scan_freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
pin = dev[2].wps_read_pin()
addr2 = dev[2].own_addr()
dev[0].dump_monitor()
dev[2].scan_for_bss(bssid, freq=2412)
dev[2].dump_monitor()
dev[2].request("WPS_PIN %s %s" % (bssid, pin))
for i in range(3):
ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
if ev is None:
raise Exception("Enrollee not seen")
if addr2 in ev:
break
if addr2 not in ev:
raise Exception("Unexpected Enrollee MAC address")
dev[0].dump_monitor()
dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " " + str(id))
dev[0].request("WPS_ER_PIN " + addr2 + " " + pin + " " + addr2)
dev[2].wait_connected(timeout=30)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
def test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev):
"""WPS ER enrolling a new device to a configured AP (2)"""
try:
_test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev)
finally:
dev[0].request("WPS_ER_STOP")
def _test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev):
ssid = "wps-er-enrollee-to-conf-ap"
ap_pin = "12345670"
ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"device_name": "Wireless AP", "manufacturer": "Company",
"model_name": "WAP", "model_number": "123",
"serial_number": "12345", "device_type": "6-0050F204-1",
"os_version": "01020300",
"config_methods": "label push_button",
"ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
id = dev[0].connect(ssid, psk="12345678", scan_freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_ER_START ifname=lo")
ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
if ev is None:
raise Exception("AP discovery timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not found")
dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
if ev is None:
raise Exception("AP learn timed out")
if ap_uuid not in ev:
raise Exception("Expected AP UUID not in settings")
ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
if ev is None:
raise Exception("WPS-FAIL after AP learn timed out")
time.sleep(0.1)
pin = dev[1].wps_read_pin()
addr1 = dev[1].own_addr()
dev[0].dump_monitor()
dev[0].request("WPS_ER_PIN any " + pin)
time.sleep(0.1)
dev[1].scan_for_bss(bssid, freq=2412)
dev[1].request("WPS_PIN any %s" % pin)
ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
if ev is None:
raise Exception("Enrollee did not report success")
dev[1].wait_connected(timeout=15)
ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
if ev is None:
raise Exception("WPS ER did not report success")
def test_ap_wps_ignore_broadcast_ssid(dev, apdev):
"""WPS AP trying to ignore broadcast SSID"""
ssid = "test-wps"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"ignore_broadcast_ssid": "1"})
if "FAIL" not in hapd.request("WPS_PBC"):
raise Exception("WPS unexpectedly enabled")
def test_ap_wps_wep(dev, apdev):
"""WPS AP trying to enable WEP"""
check_wep_capa(dev[0])
ssid = "test-wps"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"ieee80211n": "0", "wep_key0": '"hello"'})
if "FAIL" not in hapd.request("WPS_PBC"):
raise Exception("WPS unexpectedly enabled")
def test_ap_wps_tkip(dev, apdev):
"""WPS AP trying to enable TKIP"""
ssid = "test-wps"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"ieee80211n": "0", "wpa": '1',
"wpa_key_mgmt": "WPA-PSK",
"wpa_passphrase": "12345678"})
if "FAIL" not in hapd.request("WPS_PBC"):
raise Exception("WPS unexpectedly enabled")
def test_ap_wps_conf_dummy_cred(dev, apdev):
"""WPS PIN provisioning with configured AP using dummy cred"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
hapd.request("WPS_PIN any 12345670")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].dump_monitor()
try:
hapd.set("wps_testing_dummy_cred", "1")
dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
for i in range(1, 3):
ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=15)
if ev is None:
raise Exception("WPS credential %d not received" % i)
dev[0].wait_connected(timeout=30)
finally:
hapd.set("wps_testing_dummy_cred", "0")
def test_ap_wps_rf_bands(dev, apdev):
"""WPS and wps_rf_bands configuration"""
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wps_rf_bands": "ag"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
hapd.request("WPS_PBC")
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + bssid)
dev[0].wait_connected(timeout=30)
bss = dev[0].get_bss(bssid)
logger.info("BSS: " + str(bss))
if "103c000103" not in bss['ie']:
raise Exception("RF Bands attribute with expected values not found")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.set("wps_rf_bands", "ad")
hapd.set("wps_rf_bands", "a")
hapd.set("wps_rf_bands", "g")
hapd.set("wps_rf_bands", "b")
hapd.set("wps_rf_bands", "ga")
hapd.disable()
dev[0].dump_monitor()
dev[0].flush_scan_cache()
def test_ap_wps_pbc_in_m1(dev, apdev):
"""WPS and pbc_in_m1"""
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"config_methods": "virtual_push_button virtual_display",
"pbc_in_m1": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
hapd.request("WPS_PBC")
dev[0].scan_for_bss(bssid, freq="2412")
dev[0].dump_monitor()
dev[0].request("WPS_PBC " + bssid)
dev[0].wait_connected(timeout=30)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
dev[0].dump_monitor()
dev[0].flush_scan_cache()
def test_ap_wps_pbc_mac_addr_change(dev, apdev, params):
"""WPS M1 with MAC address change"""
skip_without_tkip(dev[0])
ssid = "test-wps-mac-addr-change"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1"})
hapd.request("WPS_PBC")
if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
dev[0].flush_scan_cache()
test_addr = '02:11:22:33:44:55'
addr = dev[0].get_status_field("address")
if addr == test_addr:
raise Exception("Unexpected initial MAC address")
try:
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
test_addr])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
addr1 = dev[0].get_status_field("address")
if addr1 != test_addr:
raise Exception("Failed to change MAC address")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].request("WPS_PBC " + apdev[0]['bssid'])
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or \
status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
"wps.message_type == 0x04",
display=["wps.mac_address"])
res = out.splitlines()
if len(res) < 1:
raise Exception("No M1 message with MAC address found")
if res[0] != addr1:
raise Exception("Wrong M1 MAC address")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
dev[0].dump_monitor()
dev[0].flush_scan_cache()
finally:
# Restore MAC address
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
addr])
subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
def test_ap_wps_pin_start_failure(dev, apdev):
"""WPS_PIN start failure"""
with alloc_fail(dev[0], 1, "wpas_wps_start_dev_pw"):
if "FAIL" not in dev[0].request("WPS_PIN any 12345670"):
raise Exception("WPS_PIN not rejected during OOM")
with alloc_fail(dev[0], 1, "wpas_wps_start_dev_pw"):
if "FAIL" not in dev[0].request("WPS_PIN any"):
raise Exception("WPS_PIN not rejected during OOM")
def test_ap_wps_ap_pin_failure(dev, apdev):
"""WPS_AP_PIN failure"""
id = dev[0].add_network()
dev[0].set_network(id, "mode", "2")
dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
dev[0].set_network_quoted(id, "psk", "1234567890")
dev[0].set_network(id, "frequency", "2412")
dev[0].set_network(id, "scan_freq", "2412")
dev[0].select_network(id)
dev[0].wait_connected()
with fail_test(dev[0], 1,
"os_get_random;wpa_supplicant_ctrl_iface_wps_ap_pin"):
if "FAIL" not in dev[0].request("WPS_AP_PIN random"):
raise Exception("WPS_AP_PIN random accepted")
with alloc_fail(dev[0], 1, "wpas_wps_ap_pin_set"):
if "FAIL" not in dev[0].request("WPS_AP_PIN set 12345670"):
raise Exception("WPS_AP_PIN set accepted")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_ap_wps_random_uuid(dev, apdev, params):
"""WPS and random UUID on Enrollee"""
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
config = os.path.join(params['logdir'], 'ap_wps_random_uuid.conf')
with open(config, "w") as f:
f.write("auto_uuid=1\n")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
uuid = []
for i in range(3):
wpas.interface_add("wlan5", config=config)
wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
wpas.dump_monitor()
wpas.request("WPS_PBC " + apdev[0]['bssid'])
ev = hapd.wait_event(["WPS-ENROLLEE-SEEN"], timeout=10)
if ev is None:
raise Exception("Enrollee not seen")
uuid.append(ev.split(' ')[2])
wpas.request("WPS_CANCEL")
wpas.dump_monitor()
wpas.interface_remove("wlan5")
hapd.dump_monitor()
logger.info("Seen UUIDs: " + str(uuid))
if uuid[0] == uuid[1] or uuid[0] == uuid[2] or uuid[1] == uuid[2]:
raise Exception("Same UUID used multiple times")
def test_ap_wps_conf_pin_gcmp_128(dev, apdev):
"""WPS PIN provisioning with configured AP using GCMP-128"""
run_ap_wps_conf_pin_cipher(dev, apdev, "GCMP")
def test_ap_wps_conf_pin_gcmp_256(dev, apdev):
"""WPS PIN provisioning with configured AP using GCMP-256"""
run_ap_wps_conf_pin_cipher(dev, apdev, "GCMP-256")
def test_ap_wps_conf_pin_ccmp_256(dev, apdev):
"""WPS PIN provisioning with configured AP using CCMP-256"""
run_ap_wps_conf_pin_cipher(dev, apdev, "CCMP-256")
def run_ap_wps_conf_pin_cipher(dev, apdev, cipher):
if cipher not in dev[0].get_capability("pairwise"):
raise HwsimSkip("Cipher %s not supported" % cipher)
ssid = "test-wps-conf-pin"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": cipher})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=15)
def test_ap_wps_and_sae(dev, apdev):
"""Initial AP configuration with first WPS Enrollee and adding SAE"""
skip_without_tkip(dev[0])
skip_without_tkip(dev[1])
try:
run_ap_wps_and_sae(dev, apdev)
finally:
dev[0].set("wps_cred_add_sae", "0")
def run_ap_wps_and_sae(dev, apdev):
check_sae_capab(dev[0])
ssid = "test-wps-sae"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "1",
"wps_cred_add_sae": "1"})
logger.info("WPS provisioning step")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].set("wps_cred_add_sae", "1")
dev[0].request("SET sae_groups ")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['key_mgmt'] != "SAE":
raise Exception("SAE not used")
if 'pmf' not in status or status['pmf'] != "1":
raise Exception("PMF not enabled")
pin = dev[1].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
dev[1].wait_connected(timeout=30)
status = dev[1].get_status()
if status['key_mgmt'] != "WPA2-PSK":
raise Exception("WPA2-PSK not used")
if 'pmf' in status:
raise Exception("PMF enabled")
def test_ap_wps_conf_and_sae(dev, apdev):
"""WPS PBC provisioning with configured AP using PSK+SAE"""
try:
run_ap_wps_conf_and_sae(dev, apdev)
finally:
dev[0].set("wps_cred_add_sae", "0")
def run_ap_wps_conf_and_sae(dev, apdev):
check_sae_capab(dev[0])
ssid = "test-wps-conf-sae"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"ieee80211w": "1", "sae_require_mfp": "1",
"wpa_key_mgmt": "WPA-PSK SAE",
"rsn_pairwise": "CCMP"})
dev[0].set("wps_cred_add_sae", "1")
dev[0].request("SET sae_groups ")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
dev[0].wait_connected(timeout=30)
status = dev[0].get_status()
if status['key_mgmt'] != "SAE":
raise Exception("SAE not used")
if 'pmf' not in status or status['pmf'] != "1":
raise Exception("PMF not enabled")
dev[1].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
key_mgmt="WPA-PSK", ieee80211w="0")
def test_ap_wps_reg_config_and_sae(dev, apdev):
"""WPS registrar configuring an AP using AP PIN and using PSK+SAE"""
try:
run_ap_wps_reg_config_and_sae(dev, apdev)
finally:
dev[0].set("wps_cred_add_sae", "0")
def run_ap_wps_reg_config_and_sae(dev, apdev):
check_sae_capab(dev[0])
ssid = "test-wps-init-ap-pin-sae"
appin = "12345670"
hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"ap_pin": appin, "wps_cred_add_sae": "1"})
logger.info("WPS configuration step")
dev[0].flush_scan_cache()
dev[0].set("wps_cred_add_sae", "1")
dev[0].request("SET sae_groups ")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
dev[0].dump_monitor()
new_ssid = "wps-new-ssid"
new_passphrase = "1234567890"
dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
new_passphrase)
status = dev[0].get_status()
if status['key_mgmt'] != "SAE":
raise Exception("SAE not used")
if 'pmf' not in status or status['pmf'] != "1":
raise Exception("PMF not enabled")
dev[1].connect(new_ssid, psk=new_passphrase, scan_freq="2412", proto="WPA2",
key_mgmt="WPA-PSK", ieee80211w="0")
def test_ap_wps_appl_ext(dev, apdev):
"""WPS Application Extension attribute"""
ssid = "test-wps-conf"
params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wps_application_ext": 16*"11" + 5*"ee",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
hapd = hostapd.add_ap(apdev[0], params)
pin = dev[0].wps_read_pin()
hapd.request("WPS_PIN any " + pin)
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
dev[0].wait_connected(timeout=30)
@long_duration_test
def test_ap_wps_pbc_ap_timeout(dev, apdev):
"""WPS PBC timeout on AP"""
run_ap_wps_ap_timeout(dev, apdev, "WPS_PBC")
@long_duration_test
def test_ap_wps_pin_ap_timeout(dev, apdev):
"""WPS PIN timeout on AP"""
run_ap_wps_ap_timeout(dev, apdev, "WPS_PIN any 12345670 10")
def run_ap_wps_ap_timeout(dev, apdev, cmd):
ssid = "test-wps-conf"
hapd = hostapd.add_ap(apdev[0],
{"ssid": ssid, "eap_server": "1", "wps_state": "2",
"wpa_passphrase": "12345678", "wpa": "2",
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
bssid = hapd.own_addr()
hapd.request(cmd)
time.sleep(1)
dev[0].scan_for_bss(bssid, freq="2412")
bss = dev[0].get_bss(bssid)
logger.info("BSS during active Registrar: " + str(bss))
if not bss['ie'].endswith("0106ffffffffffff"):
raise Exception("Authorized MAC not included")
ev = hapd.wait_event(["WPS-TIMEOUT"], timeout=130)
if ev is None and "PBC" in cmd:
raise Exception("WPS-TIMEOUT not reported")
if "PBC" in cmd and \
"PBC Status: Timed-out" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
time.sleep(5)
dev[0].flush_scan_cache()
dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
bss = dev[0].get_bss(bssid)
logger.info("BSS after timeout: " + str(bss))
if bss['ie'].endswith("0106ffffffffffff"):
raise Exception("Authorized MAC not removed")
+
+def test_ap_wps_er_unsubscribe_errors(dev, apdev):
+ """WPS ER and UNSUBSCRIBE errors"""
+ start_wps_ap(apdev[0])
+ tests = [(1, "http_client_url_parse;wps_er_ap_unsubscribe"),
+ (1, "wpabuf_alloc;wps_er_ap_unsubscribe"),
+ (1, "http_client_addr;wps_er_ap_unsubscribe")]
+ try:
+ for count, func in tests:
+ start_wps_er(dev[0])
+ with alloc_fail(dev[0], count, func):
+ dev[0].request("WPS_ER_STOP")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def start_wps_ap(apdev):
+ ssid = "wps-er-ap-config"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hostapd.add_ap(apdev, params)
+
+def start_wps_er(dev):
+ ssid = "wps-er-ap-config"
+ dev.connect(ssid, psk="12345678", scan_freq="2412")
+ dev.request("WPS_ER_START ifname=lo")
+ ev = dev.wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+
+def test_ap_wps_registrar_init_errors(dev, apdev):
+ """WPS Registrar init errors"""
+ hapd = wps_start_ap(apdev[0], extra_cred="wps-mixed-cred")
+ hapd.disable()
+ tests = [(1, "wps_registrar_init"),
+ (1, "wpabuf_alloc_copy;wps_registrar_init"),
+ (1, "wps_set_ie;wps_registrar_init")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded unexpectedly")
diff --git a/tests/hwsim/test_dpp.py b/tests/hwsim/test_dpp.py
index 3ba99bb1b0c7..b696c5d1dc2e 100644
--- a/tests/hwsim/test_dpp.py
+++ b/tests/hwsim/test_dpp.py
@@ -1,6350 +1,6874 @@
# Test cases for Device Provisioning Protocol (DPP)
# Copyright (c) 2017, Qualcomm Atheros, Inc.
# Copyright (c) 2018-2019, The Linux Foundation
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import base64
import binascii
import hashlib
import logging
logger = logging.getLogger()
import os
import socket
import struct
import subprocess
import time
import hostapd
import hwsim_utils
from hwsim import HWSimRadio
from utils import *
from wpasupplicant import WpaSupplicant
from wlantest import WlantestCapture
try:
import OpenSSL
openssl_imported = True
except ImportError:
openssl_imported = False
def check_dpp_capab(dev, brainpool=False, min_ver=1):
if "UNKNOWN COMMAND" in dev.request("DPP_BOOTSTRAP_GET_URI 0"):
raise HwsimSkip("DPP not supported")
if brainpool:
tls = dev.request("GET tls_library")
if not tls.startswith("OpenSSL") or "run=BoringSSL" in tls:
raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
capa = dev.request("GET_CAPABILITY dpp")
ver = 1
if capa.startswith("DPP="):
ver = int(capa[4:])
if ver < min_ver:
raise HwsimSkip("DPP version %d not supported" % min_ver)
return ver
def wait_dpp_fail(dev, expected=None):
ev = dev.wait_event(["DPP-FAIL"], timeout=5)
if ev is None:
raise Exception("Failure not reported")
if expected and expected not in ev:
raise Exception("Unexpected result: " + ev)
def test_dpp_qr_code_parsing(dev, apdev):
"""DPP QR Code parsing"""
check_dpp_capab(dev[0])
id = []
tests = ["DPP:C:81/1,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:81/1,81/2,81/3,81/4,81/5,81/6,81/7,81/8,81/9,81/10,81/11,81/12,81/13,82/14,83/1,83/2,83/3,83/4,83/5,83/6,83/7,83/8,83/9,84/5,84/6,84/7,84/8,84/9,84/10,84/11,84/12,84/13,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:81/1,2,3,4,5,6,7,8,9,10,11,12,13,82/14,83/1,2,3,4,5,6,7,8,9,84/5,6,7,8,9,10,11,12,13,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:81/1,2,3;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
"DPP:I:;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"]
for uri in tests:
id.append(dev[0].dpp_qr_code(uri))
uri2 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id[-1])
if uri != uri2:
raise Exception("Returned URI does not match")
tests = ["foo",
"DPP:",
"DPP:;;",
"DPP:C:1/2;M:;K;;",
"DPP:I:;M:01020304050;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
"DPP:K:" + base64.b64encode(b"hello").decode() + ";;",
"DPP:K:MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXiJuIWt1Q/CPCkuULechh37UsXPmbUANOeN5U9sOQROE4o/NEFeFEejROHYwwehF;;",
"DPP:K:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANNZaZA4T/kRDjnmpI1ACOJhAuTIIEk2KFOpS6XPpGF+EVr/ao3XemkE0/nzXmGaLzLqTUCJknSdxTnVPeWfCVsCAwEAAQ==;;",
"DPP:K:MIIBCjCB0wYHKoZIzj0CATCBxwIBATAkBgcqhkjOPQEBAhkA/////////////////////v//////////MEsEGP////////////////////7//////////AQYZCEFGeWcgOcPp+mrciQwSf643uzBRrmxAxUAMEWub8hCL2TtV5Uo04Eg6uEhltUEMQQYjagOsDCQ9ny/IOtDoYgA9P8K/YL/EBIHGSuV/8jaeGMQEe1rJM3Vc/l3oR55SBECGQD///////////////+Z3vg2FGvJsbTSKDECAQEDMgAEXiJuIWt1Q/CPCkuULechh37UsXPmbUANOeN5U9sOQROE4o/NEFeFEejROHYwwehF;;",
"DPP:I:foo\tbar;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
"DPP:C:1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:81/1a;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:1/2000,81/-1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
"DPP:C:-1/1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;"]
for t in tests:
res = dev[0].request("DPP_QR_CODE " + t)
if "FAIL" not in res:
raise Exception("Accepted invalid QR Code: " + t)
logger.info("ID: " + str(id))
if id[0] == id[1] or id[0] == id[2] or id[1] == id[2]:
raise Exception("Duplicate ID returned")
if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_REMOVE 12345678"):
raise Exception("DPP_BOOTSTRAP_REMOVE accepted unexpectedly")
if "OK" not in dev[0].request("DPP_BOOTSTRAP_REMOVE %d" % id[1]):
raise Exception("DPP_BOOTSTRAP_REMOVE failed")
id = dev[0].dpp_bootstrap_gen()
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
logger.info("Generated URI: " + uri)
dev[0].dpp_qr_code(uri)
id = dev[0].dpp_bootstrap_gen(chan="81/1,115/36", mac="010203040506",
info="foo")
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
logger.info("Generated URI: " + uri)
dev[0].dpp_qr_code(uri)
def test_dpp_uri_version(dev, apdev):
"""DPP URI version information"""
check_dpp_capab(dev[0], min_ver=2)
id0 = dev[0].dpp_bootstrap_gen()
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("Generated URI: " + uri)
id1 = dev[0].dpp_qr_code(uri)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id1)
logger.info("Parsed URI info:\n" + info)
if "version=2" not in info.splitlines():
raise Exception("Unexpected version information (v2)")
dev[0].set("dpp_version_override", "1")
id0 = dev[0].dpp_bootstrap_gen()
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("Generated URI: " + uri)
id1 = dev[0].dpp_qr_code(uri)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id1)
logger.info("Parsed URI info:\n" + info)
if "version=0" not in info.splitlines():
raise Exception("Unexpected version information (without indication)")
def test_dpp_qr_code_parsing_fail(dev, apdev):
"""DPP QR Code parsing local failure"""
check_dpp_capab(dev[0])
with alloc_fail(dev[0], 1, "dpp_parse_uri_info"):
if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
raise Exception("DPP_QR_CODE failure not reported")
with alloc_fail(dev[0], 1, "dpp_parse_uri_pk"):
if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
raise Exception("DPP_QR_CODE failure not reported")
with fail_test(dev[0], 1, "dpp_parse_uri_pk"):
if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
raise Exception("DPP_QR_CODE failure not reported")
with alloc_fail(dev[0], 1, "dpp_parse_uri"):
if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
raise Exception("DPP_QR_CODE failure not reported")
dpp_key_p256 = "30570201010420777fc55dc51e967c10ec051b91d860b5f1e6c934e48d5daffef98d032c64b170a00a06082a8648ce3d030107a124032200020c804188c7f85beb6e91070d2b3e5e39b90ca77b4d3c5251bc1844d6ca29dcad"
dpp_key_p384 = "307402010104302f56fdd83b5345cacb630eb7c22fa5ad5daba37307c95191e2a75756d137003bd8b32dbcb00eb5650c1eb499ecfcaec0a00706052b81040022a13403320003615ec2141b5b77aebb6523f8a012755f9a34405a8398d2ceeeebca7f5ce868bf55056cba4c4ec62fad3ed26dd29e0f23"
dpp_key_p521 = "308198020101044200c8010d5357204c252551aaf4e210343111e503fd1dc615b257058997c49b6b643c975226e93be8181cca3d83a7072defd161dfbdf433c19abe1f2ad51867a05761a00706052b81040023a1460344000301cdf3608b1305fe34a1f976095dcf001182b9973354efe156291a66830292f9babd8f412ad462958663e7a75d1d0610abdfc3dd95d40669f7ab3bc001668cfb3b7c"
dpp_key_bp256 = "3058020101042057133a676fb60bf2a3e6797e19833c7b0f89dc192ab99ab5fa377ae23a157765a00b06092b2403030208010107a12403220002945d9bf7ce30c9c1ac0ff21ca62b984d5bb80ff69d2be8c9716ab39a10d2caf0"
dpp_key_bp384 = "307802010104304902df9f3033a9b7128554c0851dc7127c3573eed150671dae74c0013e9896a9b1c22b6f7d43d8a2ebb7cd474dc55039a00b06092b240303020801010ba13403320003623cb5e68787f351faababf3425161571560add2e6f9a306fcbffb507735bf955bb46dd20ba246b0d5cadce73e5bd6a6"
dpp_key_bp512 = "30819802010104405803494226eb7e50bf0e90633f37e7e35d33f5fa502165eeba721d927f9f846caf12e925701d18e123abaaaf4a7edb4fc4de21ce18bc10c4d12e8b3439f74e40a00b06092b240303020801010da144034200033b086ccd47486522d35dc16fbb2229642c2e9e87897d45abbf21f9fb52acb5a6272b31d1b227c3e53720769cc16b4cb181b26cd0d35fe463218aaedf3b6ec00a"
def test_dpp_qr_code_curves(dev, apdev):
"""DPP QR Code and supported curves"""
check_dpp_capab(dev[0])
tests = [("prime256v1", dpp_key_p256),
("secp384r1", dpp_key_p384),
("secp521r1", dpp_key_p521)]
for curve, hex in tests:
id = dev[0].dpp_bootstrap_gen(key=hex)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
if "FAIL" in info:
raise Exception("Failed to get info for " + curve)
if "curve=" + curve not in info:
raise Exception("Curve mismatch for " + curve)
def test_dpp_qr_code_curves_brainpool(dev, apdev):
"""DPP QR Code and supported Brainpool curves"""
check_dpp_capab(dev[0], brainpool=True)
tests = [("brainpoolP256r1", dpp_key_bp256),
("brainpoolP384r1", dpp_key_bp384),
("brainpoolP512r1", dpp_key_bp512)]
for curve, hex in tests:
id = dev[0].dpp_bootstrap_gen(key=hex)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
if "FAIL" in info:
raise Exception("Failed to get info for " + curve)
if "curve=" + curve not in info:
raise Exception("Curve mismatch for " + curve)
def test_dpp_qr_code_unsupported_curve(dev, apdev):
"""DPP QR Code and unsupported curve"""
check_dpp_capab(dev[0])
id = dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode curve=unsupported")
if "FAIL" not in id:
raise Exception("Unsupported curve accepted")
tests = ["30",
"305f02010104187f723ed9e1b41979ec5cd02eb82696efc76b40e277661049a00a06082a8648ce3d030101a134033200043f292614dea97c43f500f069e79ae9fb48f8b07369180de5eec8fa2bc9eea5af7a46dc335f52f10cb1c0e9464201d41b"]
for hex in tests:
id = dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode key=" + hex)
if "FAIL" not in id:
raise Exception("Unsupported/invalid curve accepted")
def test_dpp_qr_code_keygen_fail(dev, apdev):
"""DPP QR Code and keygen failure"""
check_dpp_capab(dev[0])
with alloc_fail(dev[0], 1, "dpp_bootstrap_key_der;dpp_keygen"):
if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode"):
raise Exception("Failure not reported")
with alloc_fail(dev[0], 1, "base64_gen_encode;dpp_keygen"):
if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode"):
raise Exception("Failure not reported")
def test_dpp_qr_code_curve_select(dev, apdev):
"""DPP QR Code and curve selection"""
check_dpp_capab(dev[0], brainpool=True)
check_dpp_capab(dev[1], brainpool=True)
bi = []
for key in [dpp_key_p256, dpp_key_p384, dpp_key_p521,
dpp_key_bp256, dpp_key_bp384, dpp_key_bp512]:
id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, key=key)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
for i in info.splitlines():
if '=' in i:
name, val = i.split('=')
if name == "curve":
curve = val
break
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
bi.append((curve, uri))
for curve, uri in bi:
logger.info("Curve: " + curve)
logger.info("URI: " + uri)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True, stop_responder=True,
stop_initiator=True)
def test_dpp_qr_code_auth_broadcast(dev, apdev):
"""DPP QR Code and authentication exchange (broadcast)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1")
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0)
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_unicast(dev, apdev):
"""DPP QR Code and authentication exchange (unicast)"""
run_dpp_qr_code_auth_unicast(dev, apdev, None)
def test_dpp_qr_code_auth_unicast_ap_enrollee(dev, apdev):
"""DPP QR Code and authentication exchange (AP enrollee)"""
run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="ap")
def run_dpp_configurator_enrollee(dev, apdev, conf_curve=None):
run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="configurator",
configurator=True, conf_curve=conf_curve,
conf="configurator")
ev = dev[0].wait_event(["DPP-CONFIGURATOR-ID"], timeout=2)
if ev is None:
raise Exception("No Configurator instance added")
def test_dpp_configurator_enrollee(dev, apdev):
"""DPP Configurator enrolling"""
run_dpp_configurator_enrollee(dev, apdev)
def test_dpp_configurator_enrollee_prime256v1(dev, apdev):
"""DPP Configurator enrolling (prime256v1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="prime256v1")
def test_dpp_configurator_enrollee_secp384r1(dev, apdev):
"""DPP Configurator enrolling (secp384r1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="secp384r1")
def test_dpp_configurator_enrollee_secp521r1(dev, apdev):
"""DPP Configurator enrolling (secp521r1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="secp521r1")
def test_dpp_configurator_enrollee_brainpoolP256r1(dev, apdev):
"""DPP Configurator enrolling (brainpoolP256r1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP256r1")
def test_dpp_configurator_enrollee_brainpoolP384r1(dev, apdev):
"""DPP Configurator enrolling (brainpoolP384r1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP384r1")
def test_dpp_configurator_enrollee_brainpoolP512r1(dev, apdev):
"""DPP Configurator enrolling (brainpoolP512r1)"""
run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP512r1")
def test_dpp_configurator_enroll_conf(dev, apdev):
"""DPP Configurator enrolling followed by use of the new Configurator"""
check_dpp_capab(dev[0], min_ver=2)
try:
dev[0].set("dpp_config_processing", "2")
run_dpp_configurator_enroll_conf(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_configurator_enroll_conf(dev, apdev):
run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="configurator",
configurator=True, conf="configurator",
qr="mutual", stop_responder=False)
ev = dev[0].wait_event(["DPP-CONFIGURATOR-ID"], timeout=2)
if ev is None:
raise Exception("No Configurator instance added")
dev[1].reset()
dev[0].dump_monitor()
ssid = "test-network"
passphrase = "test-passphrase"
dev[0].set("dpp_configurator_params",
"conf=sta-psk ssid=%s pass=%s" % (binascii.hexlify(ssid.encode()).decode(), binascii.hexlify(passphrase.encode()).decode()))
dev[0].dpp_listen(2412, role="configurator")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1")
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1])
def test_dpp_qr_code_curve_prime256v1(dev, apdev):
"""DPP QR Code and curve prime256v1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1")
def test_dpp_qr_code_curve_secp384r1(dev, apdev):
"""DPP QR Code and curve secp384r1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1")
def test_dpp_qr_code_curve_secp521r1(dev, apdev):
"""DPP QR Code and curve secp521r1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1")
def test_dpp_qr_code_curve_brainpoolP256r1(dev, apdev):
"""DPP QR Code and curve brainpoolP256r1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP256r1")
def test_dpp_qr_code_curve_brainpoolP384r1(dev, apdev):
"""DPP QR Code and curve brainpoolP384r1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP384r1")
def test_dpp_qr_code_curve_brainpoolP512r1(dev, apdev):
"""DPP QR Code and curve brainpoolP512r1"""
run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP512r1")
def test_dpp_qr_code_set_key(dev, apdev):
"""DPP QR Code and fixed bootstrapping key"""
run_dpp_qr_code_auth_unicast(dev, apdev, None, key="30770201010420e5143ac74682cc6869a830e8f5301a5fa569130ac329b1d7dd6f2a7495dbcbe1a00a06082a8648ce3d030107a144034200045e13e167c33dbc7d85541e5509600aa8139bbb3e39e25898992c5d01be92039ee2850f17e71506ded0d6b25677441eae249f8e225c68dd15a6354dca54006383")
def run_dpp_qr_code_auth_unicast(dev, apdev, curve, netrole=None, key=None,
require_conf_success=False, init_extra=None,
require_conf_failure=False,
configurator=False, conf_curve=None,
conf=None, qr=None, stop_responder=True):
check_dpp_capab(dev[0], curve and "brainpool" in curve)
check_dpp_capab(dev[1], curve and "brainpool" in curve)
if configurator:
conf_id = dev[1].dpp_configurator_add(curve=conf_curve)
else:
conf_id = None
if qr == "mutual":
logger.info("dev1 displays QR Code and dev0 scans it")
id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
id1c = dev[0].dpp_qr_code(uri1)
else:
id1 = None
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve, key=key)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412, netrole=netrole, qr=qr)
dev[1].dpp_auth_init(uri=uri0, extra=init_extra, configurator=conf_id,
conf=conf, own=id1)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True,
allow_configurator_failure=not require_conf_success,
require_configurator_failure=require_conf_failure,
stop_responder=stop_responder)
def test_dpp_qr_code_auth_mutual(dev, apdev):
"""DPP QR Code and authentication exchange (mutual)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 displays QR Code")
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
logger.info("dev0 scans QR Code")
id0b = dev[0].dpp_qr_code(uri1b)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, own=id1b)
ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
if ev is None:
raise Exception("DPP authentication direction not indicated (Initiator)")
if "mutual=1" not in ev:
raise Exception("Mutual authentication not used")
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_mutual2(dev, apdev):
"""DPP QR Code and authentication exchange (mutual2)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 displays QR Code")
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412, qr="mutual")
dev[1].dpp_auth_init(uri=uri0, own=id1b)
ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
logger.info("dev0 scans QR Code")
id0b = dev[0].dpp_qr_code(uri1b)
ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
if ev is None:
raise Exception("DPP authentication direction not indicated (Initiator)")
if "mutual=1" not in ev:
raise Exception("Mutual authentication not used")
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_mutual_p_256(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen P-256)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "P-256")
def test_dpp_qr_code_auth_mutual_p_384(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen P-384)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "P-384")
def test_dpp_qr_code_auth_mutual_p_521(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen P-521)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "P-521")
def test_dpp_qr_code_auth_mutual_bp_256(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen BP-256)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "BP-256")
def test_dpp_qr_code_auth_mutual_bp_384(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen BP-384)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "BP-384")
def test_dpp_qr_code_auth_mutual_bp_512(dev, apdev):
"""DPP QR Code and authentication exchange (mutual, autogen BP-512)"""
run_dpp_qr_code_auth_mutual(dev, apdev, "BP-512")
def run_dpp_qr_code_auth_mutual(dev, apdev, curve):
check_dpp_capab(dev[0], curve and "BP-" in curve)
check_dpp_capab(dev[1], curve and "BP-" in curve)
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412, qr="mutual")
dev[1].dpp_auth_init(uri=uri0)
ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
uri = ev.split(' ')[1]
ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
logger.info("dev0 scans QR Code")
dev[0].dpp_qr_code(uri)
ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
if ev is None:
raise Exception("DPP authentication direction not indicated (Initiator)")
if "mutual=1" not in ev:
raise Exception("Mutual authentication not used")
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_auth_resp_retries(dev, apdev):
"""DPP Authentication Response retries"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].set("dpp_resp_max_tries", "3")
dev[0].set("dpp_resp_retry_time", "100")
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 displays QR Code")
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412, qr="mutual")
dev[1].dpp_auth_init(uri=uri0, own=id1b)
ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
# Stop Initiator from listening to frames to force retransmission of the
# DPP Authentication Response frame with Status=0
dev[1].request("DPP_STOP_LISTEN")
dev[1].dump_monitor()
dev[0].dump_monitor()
logger.info("dev0 scans QR Code")
id0b = dev[0].dpp_qr_code(uri1b)
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None or "type=1" not in ev:
raise Exception("DPP Authentication Response not sent")
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("TX status for DPP Authentication Response not reported")
if "result=no-ACK" not in ev:
raise Exception("Unexpected TX status for Authentication Response: " + ev)
ev = dev[0].wait_event(["DPP-TX "], timeout=15)
if ev is None or "type=1" not in ev:
raise Exception("DPP Authentication Response retransmission not sent")
def test_dpp_qr_code_auth_mutual_not_used(dev, apdev):
"""DPP QR Code and authentication exchange (mutual not used)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 displays QR Code")
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
logger.info("dev0 does not scan QR Code")
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, own=id1b)
ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
if ev is None:
raise Exception("DPP authentication direction not indicated (Initiator)")
if "mutual=0" not in ev:
raise Exception("Mutual authentication not used")
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_mutual_curve_mismatch(dev, apdev):
"""DPP QR Code and authentication exchange (mutual/mismatch)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 displays QR Code")
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve="secp384r1")
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
logger.info("dev0 scans QR Code")
id0b = dev[0].dpp_qr_code(uri1b)
logger.info("dev1 scans QR Code")
dev[1].dpp_auth_init(uri=uri0, own=id1b, expect_fail=True)
def test_dpp_qr_code_auth_hostapd_mutual2(dev, apdev):
"""DPP QR Code and authentication exchange (hostapd mutual2)"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
logger.info("AP displays QR Code")
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
logger.info("dev0 displays QR Code")
id0b = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0b)
logger.info("dev0 scans QR Code and initiates DPP Authentication")
hapd.dpp_listen(2412, qr="mutual")
dev[0].dpp_auth_init(uri=uri_h, own=id0b)
ev = dev[0].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
ev = hapd.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
logger.info("AP scans QR Code")
hapd.dpp_qr_code(uri0)
wait_auth_success(hapd, dev[0], stop_responder=True)
def test_dpp_qr_code_listen_continue(dev, apdev):
"""DPP QR Code and listen operation needing continuation"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
logger.info("Wait for listen to expire and get restarted")
time.sleep(5.5)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[1].dpp_auth_init(uri=uri0)
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_initiator_enrollee(dev, apdev):
"""DPP QR Code and authentication exchange (Initiator in Enrollee role)"""
try:
run_dpp_qr_code_auth_initiator_enrollee(dev, apdev)
finally:
dev[0].set("gas_address3", "0")
dev[1].set("gas_address3", "0")
def run_dpp_qr_code_auth_initiator_enrollee(dev, apdev):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].request("SET gas_address3 1")
dev[1].request("SET gas_address3 1")
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
allow_enrollee_failure=True, stop_responder=True)
def test_dpp_qr_code_auth_initiator_either_1(dev, apdev):
"""DPP QR Code and authentication exchange (Initiator in either role)"""
run_dpp_qr_code_auth_initiator_either(dev, apdev, None, dev[1], dev[0])
def test_dpp_qr_code_auth_initiator_either_2(dev, apdev):
"""DPP QR Code and authentication exchange (Initiator in either role)"""
run_dpp_qr_code_auth_initiator_either(dev, apdev, "enrollee",
dev[1], dev[0])
def test_dpp_qr_code_auth_initiator_either_3(dev, apdev):
"""DPP QR Code and authentication exchange (Initiator in either role)"""
run_dpp_qr_code_auth_initiator_either(dev, apdev, "configurator",
dev[0], dev[1])
def run_dpp_qr_code_auth_initiator_either(dev, apdev, resp_role,
conf_dev, enrollee_dev):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412, role=resp_role)
dev[1].dpp_auth_init(uri=uri0, role="either")
wait_auth_success(dev[0], dev[1], configurator=conf_dev,
enrollee=enrollee_dev, allow_enrollee_failure=True,
stop_responder=True)
def run_init_incompatible_roles(dev, role="enrollee"):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code")
id1 = dev[1].dpp_qr_code(uri0)
logger.info("dev1 initiates DPP Authentication")
dev[0].dpp_listen(2412, role=role)
return id1
def test_dpp_qr_code_auth_incompatible_roles(dev, apdev):
"""DPP QR Code and authentication exchange (incompatible roles)"""
id1 = run_init_incompatible_roles(dev)
dev[1].dpp_auth_init(peer=id1, role="enrollee")
ev = dev[1].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE event on initiator timed out")
ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
dev[1].dpp_auth_init(peer=id1, role="configurator")
wait_auth_success(dev[0], dev[1], stop_responder=True)
def test_dpp_qr_code_auth_incompatible_roles2(dev, apdev):
"""DPP QR Code and authentication exchange (incompatible roles 2)"""
id1 = run_init_incompatible_roles(dev, role="configurator")
dev[1].dpp_auth_init(peer=id1, role="configurator")
ev = dev[1].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE event on initiator timed out")
ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
def test_dpp_qr_code_auth_incompatible_roles_failure(dev, apdev):
"""DPP QR Code and authentication exchange (incompatible roles failure)"""
id1 = run_init_incompatible_roles(dev, role="configurator")
with alloc_fail(dev[0], 1, "dpp_auth_build_resp_status"):
dev[1].dpp_auth_init(peer=id1, role="configurator")
ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
def test_dpp_qr_code_auth_incompatible_roles_failure2(dev, apdev):
"""DPP QR Code and authentication exchange (incompatible roles failure 2)"""
id1 = run_init_incompatible_roles(dev, role="configurator")
with alloc_fail(dev[1], 1, "dpp_auth_resp_rx_status"):
dev[1].dpp_auth_init(peer=id1, role="configurator")
wait_fail_trigger(dev[1], "GET_ALLOC_FAIL")
def test_dpp_qr_code_auth_incompatible_roles_failure3(dev, apdev):
"""DPP QR Code and authentication exchange (incompatible roles failure 3)"""
id1 = run_init_incompatible_roles(dev, role="configurator")
with fail_test(dev[1], 1, "dpp_auth_resp_rx_status"):
dev[1].dpp_auth_init(peer=id1, role="configurator")
wait_dpp_fail(dev[1], "AES-SIV decryption failed")
def test_dpp_qr_code_auth_neg_chan(dev, apdev):
"""DPP QR Code and authentication exchange with requested different channel"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf_id = dev[1].dpp_configurator_add()
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", neg_freq=2462,
configurator=conf_id)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("DPP Authentication Request not sent")
if "freq=2412 type=0" not in ev:
raise Exception("Unexpected TX data for Authentication Request: " + ev)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Request not received")
if "freq=2412 type=0" not in ev:
raise Exception("Unexpected RX data for Authentication Request: " + ev)
ev = dev[1].wait_event(["DPP-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("TX status for DPP Authentication Request not reported")
if "freq=2412 result=SUCCESS" not in ev:
raise Exception("Unexpected TX status for Authentication Request: " + ev)
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("DPP Authentication Response not sent")
if "freq=2462 type=1" not in ev:
raise Exception("Unexpected TX data for Authentication Response: " + ev)
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Response not received")
if "freq=2462 type=1" not in ev:
raise Exception("Unexpected RX data for Authentication Response: " + ev)
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("TX status for DPP Authentication Response not reported")
if "freq=2462 result=SUCCESS" not in ev:
raise Exception("Unexpected TX status for Authentication Response: " + ev)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("DPP Authentication Confirm not sent")
if "freq=2462 type=2" not in ev:
raise Exception("Unexpected TX data for Authentication Confirm: " + ev)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Confirm not received")
if "freq=2462 type=2" not in ev:
raise Exception("Unexpected RX data for Authentication Confirm: " + ev)
ev = dev[1].wait_event(["DPP-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("TX status for DPP Authentication Confirm not reported")
if "freq=2462 result=SUCCESS" not in ev:
raise Exception("Unexpected TX status for Authentication Confirm: " + ev)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
stop_responder=True)
def test_dpp_config_legacy(dev, apdev):
"""DPP Config Object for legacy network using passphrase"""
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}'
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
require_conf_success=True)
def test_dpp_config_legacy_psk_hex(dev, apdev):
"""DPP Config Object for legacy network using PSK"""
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"' + 32*"12" + '"}}'
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
require_conf_success=True)
def test_dpp_config_fragmentation(dev, apdev):
"""DPP Config Object for legacy network requiring fragmentation"""
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
require_conf_success=True)
def test_dpp_config_legacy_gen(dev, apdev):
"""Generate DPP Config Object for legacy network"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-psk pass=%s" % binascii.hexlify(b"passphrase").decode(),
require_conf_success=True)
def test_dpp_config_legacy_gen_psk(dev, apdev):
"""Generate DPP Config Object for legacy network (PSK)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-psk psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
require_conf_success=True)
def test_dpp_config_dpp_gen_prime256v1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-256)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True)
def test_dpp_config_dpp_gen_secp384r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-384)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True)
def test_dpp_config_dpp_gen_secp521r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-521)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True)
def test_dpp_config_dpp_gen_prime256v1_prime256v1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-256 + P-256)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="prime256v1")
def test_dpp_config_dpp_gen_prime256v1_secp384r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-256 + P-384)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp384r1")
def test_dpp_config_dpp_gen_prime256v1_secp521r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-256 + P-521)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp521r1")
def test_dpp_config_dpp_gen_secp384r1_prime256v1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-384 + P-256)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="prime256v1")
def test_dpp_config_dpp_gen_secp384r1_secp384r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-384 + P-384)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp384r1")
def test_dpp_config_dpp_gen_secp384r1_secp521r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-384 + P-521)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp521r1")
def test_dpp_config_dpp_gen_secp521r1_prime256v1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-521 + P-256)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="prime256v1")
def test_dpp_config_dpp_gen_secp521r1_secp384r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-521 + P-384)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp384r1")
def test_dpp_config_dpp_gen_secp521r1_secp521r1(dev, apdev):
"""Generate DPP Config Object for DPP network (P-521 + P-521)"""
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True,
conf_curve="secp521r1")
def test_dpp_config_dpp_gen_expiry(dev, apdev):
"""Generate DPP Config Object for DPP network with expiry value"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp expiry=%d" % (time.time() + 1000),
require_conf_success=True,
configurator=True)
def test_dpp_config_dpp_gen_expired_key(dev, apdev):
"""Generate DPP Config Object for DPP network with expiry value"""
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp expiry=%d" % (time.time() - 10),
require_conf_failure=True,
configurator=True)
def test_dpp_config_dpp_override_prime256v1(dev, apdev):
"""DPP Config Object override (P-256)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiYVRGNEpFR0lQS1NaMFh2OXpkQ01qbS10bjVYcE1zWUlWWjl3eVNBejFnSSIsInkiOiJRR2NIV0FfNnJiVTlYRFhBenRvWC1NNVEzc3VUbk1hcUVoVUx0bjdTU1h3In19._sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A","csign":{"kty":"EC","crv":"P-256","x":"W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s","y":"Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}}}'
dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
require_conf_success=True)
def test_dpp_config_dpp_override_secp384r1(dev, apdev):
"""DPP Config Object override (P-384)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJabi1iMndjbjRLM2pGQklkYmhGZkpVTHJTXzdESS0yMWxFQi02R3gxNjl3IiwiYWxnIjoiRVMzODQifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0zODQiLCJ4IjoickdrSGg1UUZsOUtfWjdqYUZkVVhmbThoY1RTRjM1b25Xb1NIRXVsbVNzWW9oX1RXZGpoRjhiVGdiS0ZRN2tBViIsInkiOiJBbU1QVDA5VmFENWpGdzMwTUFKQlp2VkZXeGNlVVlKLXR5blQ0bVJ5N0xOZWxhZ0dEWHpfOExaRlpOU2FaNUdLIn19.Yn_F7m-bbOQ5PlaYQJ9-1qsuqYQ6V-rAv8nWw1COKiCYwwbt3WFBJ8DljY0dPrlg5CHJC4saXwkytpI-CpELW1yUdzYb4Lrun07d20Eo_g10ICyOl5sqQCAUElKMe_Xr","csign":{"kty":"EC","crv":"P-384","x":"dmTyXXiPV2Y8a01fujL-jo08gvzyby23XmzOtzjAiujKQZZgPJsbhfEKrZDlc6ey","y":"H5Z0av5c7bqInxYb2_OOJdNiMhVf3zlcULR0516ZZitOY4U31KhL4wl4KGV7g2XW","kid":"Zn-b2wcn4K3jFBIdbhFfJULrS_7DI-21lEB-6Gx169w"}}}'
dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
require_conf_success=True)
def test_dpp_config_dpp_override_secp521r1(dev, apdev):
"""DPP Config Object override (P-521)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJMZkhKY3hnV2ZKcG1uS2IwenZRT0F2VDB2b0ZKc0JjZnBmYzgxY3Y5ZXFnIiwiYWxnIjoiRVM1MTIifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC01MjEiLCJ4IjoiQVJlUFBrMFNISkRRR2NWbnlmM3lfbTlaQllHNjFJeElIbDN1NkdwRHVhMkU1WVd4TE1BSUtMMnZuUGtlSGFVRXljRmZaZlpYZ2JlNkViUUxMVkRVUm1VUSIsInkiOiJBWUtaYlNwUkFFNjJVYm9YZ2c1ZWRBVENzbEpzTlpwcm9RR1dUcW9Md04weXkzQkVoT3ZRZmZrOWhaR2lKZ295TzFobXFRRVRrS0pXb2tIYTBCQUpLSGZtIn19.ACEZLyPk13cM_OFScpLoCElQ2t1sxq5z2d_W_3_QslTQQe5SFiH_o8ycL4632YLAH4RV0gZcMKKRMtZdHgBYHjkzASDqgY-_aYN2SBmpfl8hw0YdDlUJWX3DJf-ofqNAlTbnGmhpSg69cEAhFn41Xgvx2MdwYcPVncxxESVOtWl5zNLK","csign":{"kty":"EC","crv":"P-521","x":"ADiOI_YJOAipEXHB-SpGl4KqokX8m8h3BVYCc8dgiwssZ061-nIIY3O1SIO6Re4Jjfy53RPgzDG6jitOgOGLtzZs","y":"AZKggKaQi0ExutSpJAU3-lqDV03sBQLA9C7KabfWoAn8qD6Vk4jU0WAJdt-wBBTF9o1nVuiqS2OxMVYrxN4lOz79","kid":"LfHJcxgWfJpmnKb0zvQOAvT0voFJsBcfpfc81cv9eqg"}}}'
dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
require_conf_success=True)
def test_dpp_config_override_objects(dev, apdev):
"""Generate DPP Config Object and override objects)"""
check_dpp_capab(dev[1])
discovery = '{\n"ssid":"mywifi"\n}'
groups = '[\n {"groupId":"home","netRole":"sta"},\n {"groupId":"cottage","netRole":"sta"}\n]'
dev[1].set("dpp_discovery_override", discovery)
dev[1].set("dpp_groups_override", groups)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True)
def build_conf_obj(kty="EC", crv="P-256",
x="W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s",
y="Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE",
kid="TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU",
prot_hdr='{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}',
signed_connector=None,
no_signed_connector=False,
csign=True):
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{'
conf += '"akm":"dpp",'
if signed_connector:
conn = signed_connector
conf += '"signedConnector":"%s",' % conn
elif not no_signed_connector:
payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
sign = "_sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A"
conn = base64.urlsafe_b64encode(prot_hdr.encode()).decode().rstrip('=') + '.'
conn += base64.urlsafe_b64encode(payload.encode()).decode().rstrip('=') + '.'
conn += sign
conf += '"signedConnector":"%s",' % conn
if csign:
conf += '"csign":{'
if kty:
conf += '"kty":"%s",' % kty
if crv:
conf += '"crv":"%s",' % crv
if x:
conf += '"x":"%s",' % x
if y:
conf += '"y":"%s",' % y
if kid:
conf += '"kid":"%s"' % kid
conf = conf.rstrip(',')
conf += '}'
else:
conf = conf.rstrip(',')
conf += '}}'
return conf
def run_dpp_config_error(dev, apdev, conf,
skip_net_access_key_mismatch=True,
conf_failure=True):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
if skip_net_access_key_mismatch:
dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
dev[1].set("dpp_config_obj_override", conf)
run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
require_conf_success=not conf_failure,
require_conf_failure=conf_failure)
def test_dpp_config_jwk_error_no_kty(dev, apdev):
"""DPP Config Object JWK error - no kty"""
run_dpp_config_error(dev, apdev, build_conf_obj(kty=None))
def test_dpp_config_jwk_error_unexpected_kty(dev, apdev):
"""DPP Config Object JWK error - unexpected kty"""
run_dpp_config_error(dev, apdev, build_conf_obj(kty="unknown"))
def test_dpp_config_jwk_error_no_crv(dev, apdev):
"""DPP Config Object JWK error - no crv"""
run_dpp_config_error(dev, apdev, build_conf_obj(crv=None))
def test_dpp_config_jwk_error_unsupported_crv(dev, apdev):
"""DPP Config Object JWK error - unsupported curve"""
run_dpp_config_error(dev, apdev, build_conf_obj(crv="unsupported"))
def test_dpp_config_jwk_error_no_x(dev, apdev):
"""DPP Config Object JWK error - no x"""
run_dpp_config_error(dev, apdev, build_conf_obj(x=None))
def test_dpp_config_jwk_error_invalid_x(dev, apdev):
"""DPP Config Object JWK error - invalid x"""
run_dpp_config_error(dev, apdev, build_conf_obj(x="MTIz"))
def test_dpp_config_jwk_error_no_y(dev, apdev):
"""DPP Config Object JWK error - no y"""
run_dpp_config_error(dev, apdev, build_conf_obj(y=None))
def test_dpp_config_jwk_error_invalid_y(dev, apdev):
"""DPP Config Object JWK error - invalid y"""
run_dpp_config_error(dev, apdev, build_conf_obj(y="MTIz"))
def test_dpp_config_jwk_error_invalid_xy(dev, apdev):
"""DPP Config Object JWK error - invalid x,y"""
conf = build_conf_obj(x="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY",
y="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY")
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_jwk_error_no_kid(dev, apdev):
"""DPP Config Object JWK error - no kid"""
# csign kid is optional field, so this results in success
run_dpp_config_error(dev, apdev, build_conf_obj(kid=None),
conf_failure=False)
def test_dpp_config_jws_error_prot_hdr_not_an_object(dev, apdev):
"""DPP Config Object JWS error - protected header not an object"""
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr="1"))
def test_dpp_config_jws_error_prot_hdr_no_typ(dev, apdev):
"""DPP Config Object JWS error - protected header - no typ"""
prot_hdr = '{"kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_jws_error_prot_hdr_unsupported_typ(dev, apdev):
"""DPP Config Object JWS error - protected header - unsupported typ"""
prot_hdr = '{"typ":"unsupported","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_jws_error_prot_hdr_no_alg(dev, apdev):
"""DPP Config Object JWS error - protected header - no alg"""
prot_hdr = '{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_jws_error_prot_hdr_unexpected_alg(dev, apdev):
"""DPP Config Object JWS error - protected header - unexpected alg"""
prot_hdr = '{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"unexpected"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_jws_error_prot_hdr_no_kid(dev, apdev):
"""DPP Config Object JWS error - protected header - no kid"""
prot_hdr = '{"typ":"dppCon","alg":"ES256"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_jws_error_prot_hdr_unexpected_kid(dev, apdev):
"""DPP Config Object JWS error - protected header - unexpected kid"""
prot_hdr = '{"typ":"dppCon","kid":"MTIz","alg":"ES256"}'
run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
def test_dpp_config_signed_connector_error_no_dot_1(dev, apdev):
"""DPP Config Object signedConnector error - no dot(1)"""
conn = "MTIz"
run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
def test_dpp_config_signed_connector_error_no_dot_2(dev, apdev):
"""DPP Config Object signedConnector error - no dot(2)"""
conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz"
run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
def test_dpp_config_signed_connector_error_unexpected_signature_len(dev, apdev):
"""DPP Config Object signedConnector error - unexpected signature length"""
conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz.MTIz"
run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
def test_dpp_config_signed_connector_error_invalid_signature_der(dev, apdev):
"""DPP Config Object signedConnector error - invalid signature DER"""
conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz.MTI"
run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
def test_dpp_config_no_csign(dev, apdev):
"""DPP Config Object error - no csign"""
run_dpp_config_error(dev, apdev, build_conf_obj(csign=False))
def test_dpp_config_no_signed_connector(dev, apdev):
"""DPP Config Object error - no signedConnector"""
run_dpp_config_error(dev, apdev, build_conf_obj(no_signed_connector=True))
def test_dpp_config_unexpected_signed_connector_char(dev, apdev):
"""DPP Config Object error - unexpected signedConnector character"""
run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector='a\nb'))
def test_dpp_config_root_not_an_object(dev, apdev):
"""DPP Config Object error - root not an object"""
conf = "1"
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_no_wi_fi_tech(dev, apdev):
"""DPP Config Object error - no wi-fi_tech"""
conf = "{}"
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_unsupported_wi_fi_tech(dev, apdev):
"""DPP Config Object error - unsupported wi-fi_tech"""
conf = '{"wi-fi_tech":"unsupported"}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_no_discovery(dev, apdev):
"""DPP Config Object error - no discovery"""
conf = '{"wi-fi_tech":"infra"}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_no_discovery_ssid(dev, apdev):
"""DPP Config Object error - no discovery::ssid"""
conf = '{"wi-fi_tech":"infra","discovery":{}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_too_long_discovery_ssid(dev, apdev):
"""DPP Config Object error - too long discovery::ssid"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"%s"}}' % (33*'A')
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_no_cred(dev, apdev):
"""DPP Config Object error - no cred"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_no_cred_akm(dev, apdev):
"""DPP Config Object error - no cred::akm"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_unsupported_cred_akm(dev, apdev):
"""DPP Config Object error - unsupported cred::akm"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"unsupported"}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_no_pass(dev, apdev):
"""DPP Config Object legacy error - no pass/psk"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk"}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_too_short_pass(dev, apdev):
"""DPP Config Object legacy error - too short pass/psk"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"1"}}'
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_too_long_pass(dev, apdev):
"""DPP Config Object legacy error - too long pass/psk"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"%s"}}' % (64*'A')
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_psk_with_sae(dev, apdev):
"""DPP Config Object legacy error - psk_hex with SAE"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"sae","psk_hex":"%s"}}' % (32*"12")
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_no_pass_for_sae(dev, apdev):
"""DPP Config Object legacy error - no pass for SAE"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk+sae","psk_hex":"%s"}}' % (32*"12")
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_invalid_psk(dev, apdev):
"""DPP Config Object legacy error - invalid psk_hex"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"%s"}}' % (32*"qa")
run_dpp_config_error(dev, apdev, conf)
def test_dpp_config_error_legacy_too_short_psk(dev, apdev):
"""DPP Config Object legacy error - too short psk_hex"""
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"%s"}}' % (31*"12")
run_dpp_config_error(dev, apdev, conf)
def get_der_int_32(val):
a, b = struct.unpack('BB', val[0:2])
if a != 0x02:
raise Exception("Invalid DER encoding of INTEGER")
if b > len(val) - 2:
raise Exception("Invalid length of INTEGER (truncated)")
val = val[2:]
if b == 32:
r = val[0:32]
elif b == 33:
if val[0] != 0:
raise Exception("Too large INTEGER (32)")
r = val[1:33]
elif b < 32:
r = (32 - b) * b'\x00' + val[0:b]
else:
raise Exception("Invalid length of INTEGER (32): %d" % b)
return r, val[b:]
def ecdsa_sign(pkey, message, alg="sha256"):
sign = OpenSSL.crypto.sign(pkey, message, alg)
logger.debug("sign=" + binascii.hexlify(sign).decode())
a, b = struct.unpack('BB', sign[0:2])
if a != 0x30:
raise Exception("Invalid DER encoding of ECDSA signature")
if b != len(sign) - 2:
raise Exception("Invalid length of ECDSA signature")
sign = sign[2:]
r, sign = get_der_int_32(sign)
s, sign = get_der_int_32(sign)
if len(sign) != 0:
raise Exception("Extra data at the end of ECDSA signature")
logger.info("r=" + binascii.hexlify(r).decode())
logger.info("s=" + binascii.hexlify(s).decode())
raw_sign = r + s
return base64.urlsafe_b64encode(raw_sign).decode().rstrip('=')
p256_priv_key = """-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBVQij9ah629f1pu3tarDQGQvrzHgAkgYd1jHGiLxNajoAoGCCqGSM49
AwEHoUQDQgAEAC9d2/JirKu72F2qLuv5jEFMD1Cqu9EiyGk7cOzn/2DJ51p2mEoW
n03N6XRvTC+G7WPol9Ng97NAM2sK57+F/Q==
-----END EC PRIVATE KEY-----"""
p256_pub_key_x = binascii.unhexlify("002f5ddbf262acabbbd85daa2eebf98c414c0f50aabbd122c8693b70ece7ff60")
p256_pub_key_y = binascii.unhexlify("c9e75a76984a169f4dcde9746f4c2f86ed63e897d360f7b340336b0ae7bf85fd")
def run_dpp_config_connector(dev, apdev, expiry=None, payload=None,
skip_net_access_key_mismatch=True,
conf_failure=True):
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
p256_priv_key)
x = base64.urlsafe_b64encode(p256_pub_key_x).decode().rstrip('=')
y = base64.urlsafe_b64encode(p256_pub_key_y).decode().rstrip('=')
pubkey = b'\x04' + p256_pub_key_x + p256_pub_key_y
kid = base64.urlsafe_b64encode(hashlib.sha256(pubkey).digest()).decode().rstrip('=')
prot_hdr = '{"typ":"dppCon","kid":"%s","alg":"ES256"}' % kid
if not payload:
payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}'
if expiry:
payload += ',"expiry":"%s"' % expiry
payload += '}'
conn = base64.urlsafe_b64encode(prot_hdr.encode()).decode().rstrip('=') + '.'
conn += base64.urlsafe_b64encode(payload.encode()).decode().rstrip('=')
sign = ecdsa_sign(pkey, conn)
conn += '.' + sign
run_dpp_config_error(dev, apdev,
build_conf_obj(x=x, y=y, signed_connector=conn),
skip_net_access_key_mismatch=skip_net_access_key_mismatch,
conf_failure=conf_failure)
def test_dpp_config_connector_error_ext_sign(dev, apdev):
"""DPP Config Object connector error - external signature calculation"""
run_dpp_config_connector(dev, apdev, conf_failure=False)
def test_dpp_config_connector_error_too_short_timestamp(dev, apdev):
"""DPP Config Object connector error - too short timestamp"""
run_dpp_config_connector(dev, apdev, expiry="1")
def test_dpp_config_connector_error_invalid_timestamp(dev, apdev):
"""DPP Config Object connector error - invalid timestamp"""
run_dpp_config_connector(dev, apdev, expiry=19*"1")
def test_dpp_config_connector_error_invalid_timestamp_date(dev, apdev):
"""DPP Config Object connector error - invalid timestamp date"""
run_dpp_config_connector(dev, apdev, expiry="9999-99-99T99:99:99Z")
def test_dpp_config_connector_error_invalid_time_zone(dev, apdev):
"""DPP Config Object connector error - invalid time zone"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00*")
def test_dpp_config_connector_error_invalid_time_zone_2(dev, apdev):
"""DPP Config Object connector error - invalid time zone 2"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+")
def test_dpp_config_connector_error_expired_1(dev, apdev):
"""DPP Config Object connector error - expired 1"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00")
def test_dpp_config_connector_error_expired_2(dev, apdev):
"""DPP Config Object connector error - expired 2"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00Z")
def test_dpp_config_connector_error_expired_3(dev, apdev):
"""DPP Config Object connector error - expired 3"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+01")
def test_dpp_config_connector_error_expired_4(dev, apdev):
"""DPP Config Object connector error - expired 4"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+01:02")
def test_dpp_config_connector_error_expired_5(dev, apdev):
"""DPP Config Object connector error - expired 5"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00-01")
def test_dpp_config_connector_error_expired_6(dev, apdev):
"""DPP Config Object connector error - expired 6"""
run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00-01:02")
def test_dpp_config_connector_error_no_groups(dev, apdev):
"""DPP Config Object connector error - no groups"""
payload = '{"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
run_dpp_config_connector(dev, apdev, payload=payload)
def test_dpp_config_connector_error_empty_groups(dev, apdev):
"""DPP Config Object connector error - empty groups"""
payload = '{"groups":[],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
run_dpp_config_connector(dev, apdev, payload=payload)
def test_dpp_config_connector_error_missing_group_id(dev, apdev):
"""DPP Config Object connector error - missing groupId"""
payload = '{"groups":[{"netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
run_dpp_config_connector(dev, apdev, payload=payload)
def test_dpp_config_connector_error_missing_net_role(dev, apdev):
"""DPP Config Object connector error - missing netRole"""
payload = '{"groups":[{"groupId":"*"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
run_dpp_config_connector(dev, apdev, payload=payload)
def test_dpp_config_connector_error_missing_net_access_key(dev, apdev):
"""DPP Config Object connector error - missing netAccessKey"""
payload = '{"groups":[{"groupId":"*","netRole":"sta"}]}'
run_dpp_config_connector(dev, apdev, payload=payload)
def test_dpp_config_connector_error_net_access_key_mismatch(dev, apdev):
"""DPP Config Object connector error - netAccessKey mismatch"""
payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
run_dpp_config_connector(dev, apdev, payload=payload,
skip_net_access_key_mismatch=False)
def test_dpp_gas_timeout(dev, apdev):
"""DPP and GAS server timeout for a query"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].set("ext_mgmt_frame_handling", "1")
dev[0].dpp_listen(2412)
# Force GAS fragmentation
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
dev[1].set("dpp_config_obj_override", conf)
dev[1].dpp_auth_init(uri=uri0)
# DPP Authentication Request
msg = dev[0].mgmt_rx()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
raise Exception("MGMT_RX_PROCESS failed")
# DPP Authentication Confirmation
msg = dev[0].mgmt_rx()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
raise Exception("MGMT_RX_PROCESS failed")
wait_auth_success(dev[0], dev[1])
# DPP Configuration Response (GAS Initial Response frame)
msg = dev[0].mgmt_rx()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
raise Exception("MGMT_RX_PROCESS failed")
# GAS Comeback Response frame
msg = dev[0].mgmt_rx()
# Do not continue to force timeout on GAS server
ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("GAS result not reported (Enrollee)")
if "result=TIMEOUT" not in ev:
raise Exception("Unexpected GAS result (Enrollee): " + ev)
dev[0].set("ext_mgmt_frame_handling", "0")
ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=15)
if ev is None:
raise Exception("DPP configuration failure not reported (Configurator)")
ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=1)
if ev is None:
raise Exception("DPP configuration failure not reported (Enrollee)")
def test_dpp_akm_sha256(dev, apdev):
"""DPP AKM (SHA256)"""
run_dpp_akm(dev, apdev, 32)
def test_dpp_akm_sha384(dev, apdev):
"""DPP AKM (SHA384)"""
run_dpp_akm(dev, apdev, 48)
def test_dpp_akm_sha512(dev, apdev):
"""DPP AKM (SHA512)"""
run_dpp_akm(dev, apdev, 64)
def run_dpp_akm(dev, apdev, pmk_len):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"rsn_pairwise": "CCMP",
"ieee80211w": "2"}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
conf = hapd.request("GET_CONFIG")
if "key_mgmt=DPP" not in conf.splitlines():
logger.info("GET_CONFIG:\n" + conf)
raise Exception("GET_CONFIG did not report correct key_mgmt")
id = dev[0].connect("dpp", key_mgmt="DPP", ieee80211w="2", scan_freq="2412",
dpp_pfs="2", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=2)
if not ev:
raise Exception("Network mismatch not reported")
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
bssid = hapd.own_addr()
pmkid = 16*'11'
akmp = 2**23
pmk = pmk_len*'22'
cmd = "PMKSA_ADD %d %s %s %s 30240 43200 %d 0" % (id, bssid, pmkid, pmk, akmp)
if "OK" not in dev[0].request(cmd):
raise Exception("PMKSA_ADD failed (wpa_supplicant)")
dev[0].select_network(id, freq="2412")
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=2)
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
if not ev:
raise Exception("Association attempt was not rejected")
if "status_code=53" not in ev:
raise Exception("Unexpected status code: " + ev)
addr = dev[0].own_addr()
cmd = "PMKSA_ADD %s %s %s 0 %d" % (addr, pmkid, pmk, akmp)
if "OK" not in hapd.request(cmd):
raise Exception("PMKSA_ADD failed (hostapd)")
dev[0].select_network(id, freq="2412")
dev[0].wait_connected()
val = dev[0].get_status_field("key_mgmt")
if val != "DPP":
raise Exception("Unexpected key_mgmt: " + val)
params1_csign = "3059301306072a8648ce3d020106082a8648ce3d03010703420004d02e5bd81a120762b5f0f2994777f5d40297238a6c294fd575cdf35fabec44c050a6421c401d98d659fd2ed13c961cc8287944dd3202f516977800d3ab2f39ee"
params1_ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJzOEFrYjg5bTV4UGhoYk5UbTVmVVo0eVBzNU5VMkdxYXNRY3hXUWhtQVFRIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIwOHF4TlNYRzRWemdCV3BjVUdNSmc1czNvbElOVFJsRVQ1aERpNkRKY3ZjIiwieSI6IlVhaGFYQXpKRVpRQk1YaHRUQnlZZVlrOWtJYjk5UDA3UV9NcW9TVVZTVEkifX0.a5_nfMVr7Qe1SW0ZL3u6oQRm5NUCYUSfixDAJOUFN3XUfECBZ6E8fm8xjeSfdOytgRidTz0CTlIRjzPQo82dmQ"
params1_ap_netaccesskey = "30770201010420f6531d17f29dfab655b7c9e923478d5a345164c489aadd44a3519c3e9dcc792da00a06082a8648ce3d030107a14403420004d3cab13525c6e15ce0056a5c506309839b37a2520d4d19444f98438ba0c972f751a85a5c0cc911940131786d4c1c9879893d9086fdf4fd3b43f32aa125154932"
params1_sta_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJzOEFrYjg5bTV4UGhoYk5UbTVmVVo0eVBzNU5VMkdxYXNRY3hXUWhtQVFRIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiZWMzR3NqQ3lQMzVBUUZOQUJJdEltQnN4WXVyMGJZX1dES1lfSE9zUGdjNCIsInkiOiJTRS1HVllkdWVnTFhLMU1TQXZNMEx2QWdLREpTNWoyQVhCbE9PMTdUSTRBIn19.PDK9zsGlK-e1pEOmNxVeJfCS8pNeay6ckIS1TXCQsR64AR-9wFPCNVjqOxWvVKltehyMFqVAtOcv0IrjtMJFqQ"
params1_sta_netaccesskey = "30770201010420bc33380c26fd2168b69cd8242ed1df07ba89aa4813f8d4e8523de6ca3f8dd28ba00a06082a8648ce3d030107a1440342000479cdc6b230b23f7e40405340048b48981b3162eaf46d8fd60ca63f1ceb0f81ce484f8655876e7a02d72b531202f3342ef020283252e63d805c194e3b5ed32380"
def test_dpp_network_introduction(dev, apdev):
"""DPP network introduction"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
id = dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey)
val = dev[0].get_status_field("key_mgmt")
if val != "DPP":
raise Exception("Unexpected key_mgmt: " + val)
def test_dpp_network_introduction_expired(dev, apdev):
"""DPP network introduction with expired netaccesskey"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey,
"dpp_netaccesskey_expiry": "1565530889"}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = hapd.wait_event(["DPP-RX"], timeout=10)
if ev is None:
raise Exception("No DPP Peer Discovery Request seen")
if "type=5" not in ev:
raise Exception("Unexpected DPP message received: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
dev[0].request("DISCONNECT")
if ev:
raise Exception("Connection reported")
hapd.disable()
hapd.set("dpp_netaccesskey_expiry", "2565530889")
hapd.enable()
dev[0].request("RECONNECT")
dev[0].wait_connected()
def test_dpp_and_sae_akm(dev, apdev):
"""DPP and SAE AKMs"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
if "SAE" not in dev[1].get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
params = {"ssid": "dpp+sae",
"wpa": "2",
"wpa_key_mgmt": "DPP SAE",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"sae_password": "sae-password",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
id = dev[0].connect("dpp+sae", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey)
val = dev[0].get_status_field("key_mgmt")
if val != "DPP":
raise Exception("Unexpected key_mgmt for DPP: " + val)
dev[1].request("SET sae_groups ")
id = dev[1].connect("dpp+sae", key_mgmt="SAE", scan_freq="2412",
ieee80211w="2", psk="sae-password")
val = dev[1].get_status_field("key_mgmt")
if val != "SAE":
raise Exception("Unexpected key_mgmt for SAE: " + val)
def test_dpp_ap_config(dev, apdev):
"""DPP and AP configuration"""
run_dpp_ap_config(dev, apdev)
def test_dpp_ap_config_p256_p256(dev, apdev):
"""DPP and AP configuration (P-256 + P-256)"""
run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-256")
def test_dpp_ap_config_p256_p384(dev, apdev):
"""DPP and AP configuration (P-256 + P-384)"""
run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-384")
def test_dpp_ap_config_p256_p521(dev, apdev):
"""DPP and AP configuration (P-256 + P-521)"""
run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-521")
def test_dpp_ap_config_p384_p256(dev, apdev):
"""DPP and AP configuration (P-384 + P-256)"""
run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-256")
def test_dpp_ap_config_p384_p384(dev, apdev):
"""DPP and AP configuration (P-384 + P-384)"""
run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-384")
def test_dpp_ap_config_p384_p521(dev, apdev):
"""DPP and AP configuration (P-384 + P-521)"""
run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-521")
def test_dpp_ap_config_p521_p256(dev, apdev):
"""DPP and AP configuration (P-521 + P-256)"""
run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-256")
def test_dpp_ap_config_p521_p384(dev, apdev):
"""DPP and AP configuration (P-521 + P-384)"""
run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-384")
def test_dpp_ap_config_p521_p521(dev, apdev):
"""DPP and AP configuration (P-521 + P-521)"""
run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-521")
def test_dpp_ap_config_bp256_bp256(dev, apdev):
"""DPP and AP configuration (BP-256 + BP-256)"""
run_dpp_ap_config(dev, apdev, curve="BP-256", conf_curve="BP-256")
def test_dpp_ap_config_bp384_bp384(dev, apdev):
"""DPP and AP configuration (BP-384 + BP-384)"""
run_dpp_ap_config(dev, apdev, curve="BP-384", conf_curve="BP-384")
def test_dpp_ap_config_bp512_bp512(dev, apdev):
"""DPP and AP configuration (BP-512 + BP-512)"""
run_dpp_ap_config(dev, apdev, curve="BP-512", conf_curve="BP-512")
def test_dpp_ap_config_p256_bp256(dev, apdev):
"""DPP and AP configuration (P-256 + BP-256)"""
run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="BP-256")
def test_dpp_ap_config_bp256_p256(dev, apdev):
"""DPP and AP configuration (BP-256 + P-256)"""
run_dpp_ap_config(dev, apdev, curve="BP-256", conf_curve="P-256")
def test_dpp_ap_config_p521_bp512(dev, apdev):
"""DPP and AP configuration (P-521 + BP-512)"""
run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="BP-512")
def test_dpp_ap_config_bp512_p521(dev, apdev):
"""DPP and AP configuration (BP-512 + P-521)"""
run_dpp_ap_config(dev, apdev, curve="BP-512", conf_curve="P-521")
def test_dpp_ap_config_reconfig_configurator(dev, apdev):
"""DPP and AP configuration with Configurator reconfiguration"""
run_dpp_ap_config(dev, apdev, reconf_configurator=True)
def update_hapd_config(hapd):
ev = hapd.wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
if ev is None:
raise Exception("SSID not reported (AP)")
ssid = ev.split(' ')[1]
ev = hapd.wait_event(["DPP-CONNECTOR"], timeout=1)
if ev is None:
raise Exception("Connector not reported (AP)")
connector = ev.split(' ')[1]
ev = hapd.wait_event(["DPP-C-SIGN-KEY"], timeout=1)
if ev is None:
raise Exception("C-sign-key not reported (AP)")
p = ev.split(' ')
csign = p[1]
ev = hapd.wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
if ev is None:
raise Exception("netAccessKey not reported (AP)")
p = ev.split(' ')
net_access_key = p[1]
net_access_key_expiry = p[2] if len(p) > 2 else None
logger.info("Update AP configuration to use key_mgmt=DPP")
hapd.disable()
hapd.set("ssid", ssid)
hapd.set("utf8_ssid", "1")
hapd.set("wpa", "2")
hapd.set("wpa_key_mgmt", "DPP")
hapd.set("ieee80211w", "2")
hapd.set("rsn_pairwise", "CCMP")
hapd.set("dpp_connector", connector)
hapd.set("dpp_csign", csign)
hapd.set("dpp_netaccesskey", net_access_key)
if net_access_key_expiry:
hapd.set("dpp_netaccesskey_expiry", net_access_key_expiry)
hapd.enable()
def run_dpp_ap_config(dev, apdev, curve=None, conf_curve=None,
reconf_configurator=False):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
conf_id = dev[0].dpp_configurator_add(curve=conf_curve)
if reconf_configurator:
csign = dev[0].request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
if "FAIL" in csign or len(csign) == 0:
raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
update_hapd_config(hapd)
id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
if reconf_configurator:
dev[0].dpp_configurator_remove(conf_id)
conf_id = dev[0].dpp_configurator_add(curve=conf_curve, key=csign)
dev[1].dpp_listen(2412)
dev[0].dpp_auth_init(uri=uri1, conf="sta-dpp", configurator=conf_id)
wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1],
stop_responder=True)
ev = dev[1].wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
if ev is None:
raise Exception("SSID not reported")
ssid = ev.split(' ')[1]
ev = dev[1].wait_event(["DPP-CONNECTOR"], timeout=1)
if ev is None:
raise Exception("Connector not reported")
connector = ev.split(' ')[1]
ev = dev[1].wait_event(["DPP-C-SIGN-KEY"], timeout=1)
if ev is None:
raise Exception("C-sign-key not reported")
p = ev.split(' ')
csign = p[1]
ev = dev[1].wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
if ev is None:
raise Exception("netAccessKey not reported")
p = ev.split(' ')
net_access_key = p[1]
net_access_key_expiry = p[2] if len(p) > 2 else None
dev[1].dump_monitor()
id = dev[1].connect(ssid, key_mgmt="DPP", ieee80211w="2", scan_freq="2412",
only_add_network=True)
dev[1].set_network_quoted(id, "dpp_connector", connector)
dev[1].set_network(id, "dpp_csign", csign)
dev[1].set_network(id, "dpp_netaccesskey", net_access_key)
if net_access_key_expiry:
dev[1].set_network(id, "dpp_netaccess_expiry", net_access_key_expiry)
logger.info("Check data connection")
dev[1].select_network(id, freq="2412")
dev[1].wait_connected()
def test_dpp_auto_connect_1(dev, apdev):
"""DPP and auto connect (1)"""
try:
run_dpp_auto_connect(dev, apdev, 1)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2(dev, apdev):
"""DPP and auto connect (2)"""
try:
run_dpp_auto_connect(dev, apdev, 2)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2_connect_cmd(dev, apdev):
"""DPP and auto connect (2) using connect_cmd"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
dev_new = [wpas, dev[1]]
try:
run_dpp_auto_connect(dev_new, apdev, 2)
finally:
wpas.set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2_sta_ver1(dev, apdev):
"""DPP and auto connect (2; STA using ver 1)"""
try:
run_dpp_auto_connect(dev, apdev, 2, sta_version=1)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2_ap_ver1(dev, apdev):
"""DPP and auto connect (2; AP using ver 1)"""
try:
run_dpp_auto_connect(dev, apdev, 2, ap_version=1)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2_ver1(dev, apdev):
"""DPP and auto connect (2; AP and STA using ver 1)"""
try:
run_dpp_auto_connect(dev, apdev, 2, ap_version=1, sta_version=1)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_2_conf_ver1(dev, apdev):
"""DPP and auto connect (2; Configurator using ver 1)"""
try:
run_dpp_auto_connect(dev, apdev, 2, sta1_version=1)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_auto_connect(dev, apdev, processing, ap_version=0, sta_version=0,
sta1_version=0):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
csign = "30770201010420768240a3fc89d6662d9782f120527fe7fb9edc6366ab0b9c7dde96125cfd250fa00a06082a8648ce3d030107a144034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
csign_pub = "3059301306072a8648ce3d020106082a8648ce3d030107034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJwYWtZbXVzd1dCdWpSYTl5OEsweDViaTVrT3VNT3dzZHRlaml2UG55ZHZzIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIybU5vNXZuRkI5bEw3d1VWb1hJbGVPYzBNSEE1QXZKbnpwZXZULVVTYzVNIiwieSI6IlhzS3dqVHJlLTg5WWdpU3pKaG9CN1haeUttTU05OTl3V2ZaSVl0bi01Q3MifX0.XhjFpZgcSa7G2lHy0OCYTvaZFRo5Hyx6b7g7oYyusLC7C_73AJ4_BxEZQVYJXAtDuGvb3dXSkHEKxREP9Q6Qeg"
ap_netaccesskey = "30770201010420ceba752db2ad5200fa7bc565b9c05c69b7eb006751b0b329b0279de1c19ca67ca00a06082a8648ce3d030107a14403420004da6368e6f9c507d94bef0515a1722578e73430703902f267ce97af4fe51273935ec2b08d3adefbcf588224b3261a01ed76722a630cf7df7059f64862d9fee42b"
params = {"ssid": "test",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": ap_connector,
"dpp_csign": csign_pub,
"dpp_netaccesskey": ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
if ap_version:
hapd.set("dpp_version_override", str(ap_version))
except:
raise HwsimSkip("DPP not supported")
if sta_version:
dev[0].set("dpp_version_override", str(sta_version))
if sta1_version:
dev[1].set("dpp_version_override", str(sta1_version))
conf_id = dev[1].dpp_configurator_add(key=csign)
dev[0].set("dpp_config_processing", str(processing))
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", configurator=conf_id)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
if processing == 1:
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
hwsim_utils.test_connectivity(dev[0], hapd)
def test_dpp_auto_connect_legacy(dev, apdev):
"""DPP and auto connect (legacy)"""
try:
run_dpp_auto_connect_legacy(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_ssid_charset(dev, apdev):
"""DPP and auto connect (legacy, ssid_charset)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, ssid_charset=12345)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_sae_1(dev, apdev):
"""DPP and auto connect (legacy SAE)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, conf='sta-sae', psk_sae=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_sae_2(dev, apdev):
"""DPP and auto connect (legacy SAE)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, conf='sta-sae', sae_only=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_psk_sae_1(dev, apdev):
"""DPP and auto connect (legacy PSK+SAE)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae',
psk_sae=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_psk_sae_2(dev, apdev):
"""DPP and auto connect (legacy PSK+SAE)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae',
sae_only=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_auto_connect_legacy_psk_sae_3(dev, apdev):
"""DPP and auto connect (legacy PSK+SAE)"""
try:
run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae')
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk',
ssid_charset=None,
psk_sae=False, sae_only=False):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = hostapd.wpa2_params(ssid="dpp-legacy",
passphrase="secret passphrase")
if sae_only:
params['wpa_key_mgmt'] = 'SAE'
params['ieee80211w'] = '2'
elif psk_sae:
params['wpa_key_mgmt'] = 'WPA-PSK SAE'
params['ieee80211w'] = '1'
params['sae_require_mfp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].request("SET sae_groups ")
dev[0].set("dpp_config_processing", "2")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid="dpp-legacy",
ssid_charset=ssid_charset,
passphrase="secret passphrase")
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
if ssid_charset:
ev = dev[0].wait_event(["DPP-CONFOBJ-SSID-CHARSET"], timeout=1)
if ev is None:
raise Exception("ssid_charset not reported")
charset = ev.split(' ')[1]
if charset != str(ssid_charset):
raise Exception("Incorrect ssid_charset reported: " + ev)
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
dev[0].wait_connected()
def test_dpp_auto_connect_legacy_pmf_required(dev, apdev):
"""DPP and auto connect (legacy, PMF required)"""
try:
run_dpp_auto_connect_legacy_pmf_required(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_auto_connect_legacy_pmf_required(dev, apdev):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = hostapd.wpa2_params(ssid="dpp-legacy",
passphrase="secret passphrase")
params['wpa_key_mgmt'] = "WPA-PSK-SHA256"
params['ieee80211w'] = "2"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].set("dpp_config_processing", "2")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", ssid="dpp-legacy",
passphrase="secret passphrase")
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
dev[0].wait_connected()
def test_dpp_qr_code_auth_responder_configurator(dev, apdev):
"""DPP QR Code and responder as the configurator"""
run_dpp_qr_code_auth_responder_configurator(dev, apdev, "")
def test_dpp_qr_code_auth_responder_configurator_group_id(dev, apdev):
"""DPP QR Code and responder as the configurator with group_id)"""
run_dpp_qr_code_auth_responder_configurator(dev, apdev,
" group_id=test-group")
def run_dpp_qr_code_auth_responder_configurator(dev, apdev, extra):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf_id = dev[0].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
" conf=sta-dpp configurator=%d%s" % (conf_id, extra))
dev[0].dpp_listen(2412, role="configurator")
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
stop_responder=True)
def test_dpp_qr_code_auth_enrollee_init_netrole(dev, apdev):
"""DPP QR Code and enrollee initiating with netrole specified"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf_id = dev[0].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
" conf=configurator configurator=%d" % conf_id)
dev[0].dpp_listen(2412, role="configurator")
dev[1].dpp_auth_init(uri=uri0, role="enrollee", netrole="configurator")
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
stop_responder=True)
dev[0].dump_monitor()
dev[1].dump_monitor()
# verify that netrole resets back to sta, if not explicitly stated
dev[0].set("dpp_configurator_params",
"conf=sta-dpp configurator=%d" % conf_id)
dev[0].dpp_listen(2412, role="configurator")
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
stop_responder=True)
def test_dpp_qr_code_hostapd_init(dev, apdev):
"""DPP QR Code and hostapd as initiator"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
conf_id = dev[0].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
" conf=ap-dpp configurator=%d" % conf_id)
dev[0].dpp_listen(2437, role="configurator")
hapd.dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
stop_responder=True)
def test_dpp_qr_code_hostapd_init_offchannel(dev, apdev):
"""DPP QR Code and hostapd as initiator (offchannel)"""
run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, None)
def test_dpp_qr_code_hostapd_init_offchannel_neg_freq(dev, apdev):
"""DPP QR Code and hostapd as initiator (offchannel, neg_freq)"""
run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, "neg_freq=2437")
def run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, extra):
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
conf_id = dev[0].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1,81/11", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
" conf=ap-dpp configurator=%d" % conf_id)
dev[0].dpp_listen(2462, role="configurator")
hapd.dpp_auth_init(uri=uri0, role="enrollee", extra=extra)
wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
stop_responder=True)
def test_dpp_qr_code_hostapd_ignore_mismatch(dev, apdev):
"""DPP QR Code and hostapd ignoring netaccessKey mismatch"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
conf_id = dev[0].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
"conf=ap-dpp configurator=%d" % conf_id)
conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiYVRGNEpFR0lQS1NaMFh2OXpkQ01qbS10bjVYcE1zWUlWWjl3eVNBejFnSSIsInkiOiJRR2NIV0FfNnJiVTlYRFhBenRvWC1NNVEzc3VUbk1hcUVoVUx0bjdTU1h3In19._sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A","csign":{"kty":"EC","crv":"P-256","x":"W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s","y":"Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}}}'
dev[0].set("dpp_config_obj_override", conf)
dev[0].dpp_listen(2437, role="configurator")
hapd.set("dpp_ignore_netaccesskey_mismatch", "1")
hapd.dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
stop_responder=True)
def test_dpp_test_vector_p_256(dev, apdev):
"""DPP P-256 test vector (mutual auth)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
# Responder bootstrapping key
priv = "54ce181a98525f217216f59b245f60e9df30ac7f6b26c939418cfc3c42d1afa0"
id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True, key="30310201010420" + priv + "a00a06082a8648ce3d030107")
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
# Responder protocol keypair override
priv = "f798ed2e19286f6a6efe210b1863badb99af2a14b497634dbfd2a97394fb5aa5"
dev[0].set("dpp_protocol_key_override",
"30310201010420" + priv + "a00a06082a8648ce3d030107")
dev[0].set("dpp_nonce_override", "3d0cfb011ca916d796f7029ff0b43393")
# Initiator bootstrapping key
priv = "15b2a83c5a0a38b61f2aa8200ee4994b8afdc01c58507d10d0a38f7eedf051bb"
id1 = dev[1].dpp_bootstrap_gen(key="30310201010420" + priv + "a00a06082a8648ce3d030107")
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
# Initiator protocol keypair override
priv = "a87de9afbb406c96e5f79a3df895ecac3ad406f95da66314c8cb3165e0c61783"
dev[1].set("dpp_protocol_key_override",
"30310201010420" + priv + "a00a06082a8648ce3d030107")
dev[1].set("dpp_nonce_override", "13f4602a16daeb69712263b9c46cba31")
dev[0].dpp_qr_code(uri1)
dev[0].dpp_listen(2462, qr="mutual")
dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
wait_auth_success(dev[0], dev[1])
def test_dpp_test_vector_p_256_b(dev, apdev):
"""DPP P-256 test vector (Responder-only auth)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
# Responder bootstrapping key
priv = "54ce181a98525f217216f59b245f60e9df30ac7f6b26c939418cfc3c42d1afa0"
id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True, key="30310201010420" + priv + "a00a06082a8648ce3d030107")
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
# Responder protocol keypair override
priv = "f798ed2e19286f6a6efe210b1863badb99af2a14b497634dbfd2a97394fb5aa5"
dev[0].set("dpp_protocol_key_override",
"30310201010420" + priv + "a00a06082a8648ce3d030107")
dev[0].set("dpp_nonce_override", "3d0cfb011ca916d796f7029ff0b43393")
# Initiator bootstrapping key
priv = "15b2a83c5a0a38b61f2aa8200ee4994b8afdc01c58507d10d0a38f7eedf051bb"
id1 = dev[1].dpp_bootstrap_gen(key="30310201010420" + priv + "a00a06082a8648ce3d030107")
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
# Initiator protocol keypair override
priv = "a87de9afbb406c96e5f79a3df895ecac3ad406f95da66314c8cb3165e0c61783"
dev[1].set("dpp_protocol_key_override",
"30310201010420" + priv + "a00a06082a8648ce3d030107")
dev[1].set("dpp_nonce_override", "13f4602a16daeb69712263b9c46cba31")
dev[0].dpp_listen(2462)
dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
wait_auth_success(dev[0], dev[1])
def der_priv_key_p_521(priv):
if len(priv) != 2 * 66:
raise Exception("Unexpected der_priv_key_p_521 parameter: " + priv)
der_prefix = "3081500201010442"
der_postfix = "a00706052b81040023"
return der_prefix + priv + der_postfix
def test_dpp_test_vector_p_521(dev, apdev):
"""DPP P-521 test vector (mutual auth)"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
# Responder bootstrapping key
priv = "0061e54f518cdf859735da3dd64c6f72c2f086f41a6fd52915152ea2fe0f24ddaecd8883730c9c9fd82cf7c043a41021696388cf5190b731dd83638bcd56d8b6c743"
id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True,
key=der_priv_key_p_521(priv))
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
# Responder protocol keypair override
priv = "01d8b7b17cd1b0a33f7c66fb4220999329cdaf4f8b44b2ffadde8ab8ed8abffa9f5358c5b1caae26709ca4fb78e52a4d08f2e4f24111a36a6f440d20a0000ff51597"
dev[0].set("dpp_protocol_key_override", der_priv_key_p_521(priv))
dev[0].set("dpp_nonce_override",
"d749a782012eb0a8595af30b2dfc8d0880d004ebddb55ecc5afbdef18c400e01")
# Initiator bootstrapping key
priv = "0060c10df14af5ef27f6e362d31bdd9eeb44be77a323ba64b08f3f03d58b92cbfe05c182a91660caa081ca344243c47b5aa088bcdf738840eb35f0218b9f26881e02"
id1 = dev[1].dpp_bootstrap_gen(key=der_priv_key_p_521(priv))
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
# Initiator protocol keypair override
priv = "019c1c08caaeec38fb931894699b095bc3ab8c1ec7ef0622d2e3eba821477c8c6fca41774f21166ad98aebda37c067d9aa08a8a2e1b5c44c61f2bae02a61f85d9661"
dev[1].set("dpp_protocol_key_override", der_priv_key_p_521(priv))
dev[1].set("dpp_nonce_override",
"de972af3847bec3ba2aedd9f5c21cfdec7bf0bc5fe8b276cbcd0267807fb15b0")
dev[0].dpp_qr_code(uri1)
dev[0].dpp_listen(2462, qr="mutual")
dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
wait_auth_success(dev[0], dev[1])
def test_dpp_pkex(dev, apdev):
"""DPP and PKEX"""
run_dpp_pkex(dev, apdev)
def test_dpp_pkex_p256(dev, apdev):
"""DPP and PKEX (P-256)"""
run_dpp_pkex(dev, apdev, "P-256")
def test_dpp_pkex_p384(dev, apdev):
"""DPP and PKEX (P-384)"""
run_dpp_pkex(dev, apdev, "P-384")
def test_dpp_pkex_p521(dev, apdev):
"""DPP and PKEX (P-521)"""
run_dpp_pkex(dev, apdev, "P-521")
def test_dpp_pkex_bp256(dev, apdev):
"""DPP and PKEX (BP-256)"""
run_dpp_pkex(dev, apdev, "brainpoolP256r1")
def test_dpp_pkex_bp384(dev, apdev):
"""DPP and PKEX (BP-384)"""
run_dpp_pkex(dev, apdev, "brainpoolP384r1")
def test_dpp_pkex_bp512(dev, apdev):
"""DPP and PKEX (BP-512)"""
run_dpp_pkex(dev, apdev, "brainpoolP512r1")
def test_dpp_pkex_config(dev, apdev):
"""DPP and PKEX with initiator as the configurator"""
check_dpp_capab(dev[1])
conf_id = dev[1].dpp_configurator_add()
run_dpp_pkex(dev, apdev,
init_extra="conf=sta-dpp configurator=%d" % (conf_id),
check_config=True)
def test_dpp_pkex_no_identifier(dev, apdev):
"""DPP and PKEX without identifier"""
run_dpp_pkex(dev, apdev, identifier_i=None, identifier_r=None)
def test_dpp_pkex_identifier_mismatch(dev, apdev):
"""DPP and PKEX with different identifiers"""
run_dpp_pkex(dev, apdev, identifier_i="foo", identifier_r="bar",
expect_no_resp=True)
def test_dpp_pkex_identifier_mismatch2(dev, apdev):
"""DPP and PKEX with initiator using identifier and the responder not"""
run_dpp_pkex(dev, apdev, identifier_i="foo", identifier_r=None,
expect_no_resp=True)
def test_dpp_pkex_identifier_mismatch3(dev, apdev):
"""DPP and PKEX with responder using identifier and the initiator not"""
run_dpp_pkex(dev, apdev, identifier_i=None, identifier_r="bar",
expect_no_resp=True)
def run_dpp_pkex(dev, apdev, curve=None, init_extra=None, check_config=False,
identifier_i="test", identifier_r="test",
expect_no_resp=False):
check_dpp_capab(dev[0], curve and "brainpool" in curve)
check_dpp_capab(dev[1], curve and "brainpool" in curve)
dev[0].dpp_pkex_resp(2437, identifier=identifier_r, code="secret",
curve=curve)
dev[1].dpp_pkex_init(identifier=identifier_i, code="secret", curve=curve,
extra=init_extra)
if expect_no_resp:
ev = dev[0].wait_event(["DPP-RX"], timeout=10)
if ev is None:
raise Exception("DPP PKEX frame not received")
ev = dev[1].wait_event(["DPP-AUTH-SUCCESS"], timeout=1)
if ev is not None:
raise Exception("DPP authentication succeeded")
ev = dev[0].wait_event(["DPP-AUTH-SUCCESS"], timeout=0.1)
if ev is not None:
raise Exception("DPP authentication succeeded")
return
wait_auth_success(dev[0], dev[1],
configurator=dev[1] if check_config else None,
enrollee=dev[0] if check_config else None)
def test_dpp_pkex_5ghz(dev, apdev):
"""DPP and PKEX on 5 GHz"""
try:
dev[0].request("SET country US")
dev[1].request("SET country US")
ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
if ev is None:
ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"],
timeout=1)
run_dpp_pkex_5ghz(dev, apdev)
finally:
dev[0].request("SET country 00")
dev[1].request("SET country 00")
subprocess.call(['iw', 'reg', 'set', '00'])
time.sleep(0.1)
def run_dpp_pkex_5ghz(dev, apdev):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(5745, identifier="test", code="secret")
dev[1].dpp_pkex_init(identifier="test", code="secret")
wait_auth_success(dev[0], dev[1], timeout=20)
def test_dpp_pkex_test_vector(dev, apdev):
"""DPP and PKEX (P-256) test vector"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
init_addr = "ac:64:91:f4:52:07"
resp_addr = "6e:5e:ce:6e:f3:dd"
identifier = "joes_key"
code = "thisisreallysecret"
# Initiator bootstrapping private key
init_priv = "5941b51acfc702cdc1c347264beb2920db88eb1a0bf03a211868b1632233c269"
# Responder bootstrapping private key
resp_priv = "2ae8956293f49986b6d0b8169a86805d9232babb5f6813fdfe96f19d59536c60"
# Initiator x/X keypair override
init_x_priv = "8365c5ed93d751bef2d92b410dc6adfd95670889183fac1bd66759ad85c3187a"
# Responder y/Y keypair override
resp_y_priv = "d98faa24d7dd3f592665d71a95c862bfd02c4c48acb0c515a41cbc6e929675ea"
p256_prefix = "30310201010420"
p256_postfix = "a00a06082a8648ce3d030107"
dev[0].set("dpp_pkex_own_mac_override", resp_addr)
dev[0].set("dpp_pkex_peer_mac_override", init_addr)
dev[1].set("dpp_pkex_own_mac_override", init_addr)
dev[1].set("dpp_pkex_peer_mac_override", resp_addr)
# Responder y/Y keypair override
dev[0].set("dpp_pkex_ephemeral_key_override",
p256_prefix + resp_y_priv + p256_postfix)
# Initiator x/X keypair override
dev[1].set("dpp_pkex_ephemeral_key_override",
p256_prefix + init_x_priv + p256_postfix)
dev[0].dpp_pkex_resp(2437, identifier=identifier, code=code,
key=p256_prefix + resp_priv + p256_postfix)
dev[1].dpp_pkex_init(identifier=identifier, code=code,
key=p256_prefix + init_priv + p256_postfix)
wait_auth_success(dev[0], dev[1])
def test_dpp_pkex_code_mismatch(dev, apdev):
"""DPP and PKEX with mismatching code"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
id1 = dev[1].dpp_pkex_init(identifier="test", code="unknown")
wait_dpp_fail(dev[0], "possible PKEX code mismatch")
dev[0].dump_monitor()
dev[1].dump_monitor()
dev[1].dpp_pkex_init(identifier="test", code="secret", use_id=id1)
wait_auth_success(dev[0], dev[1])
def test_dpp_pkex_code_mismatch_limit(dev, apdev):
"""DPP and PKEX with mismatching code limit"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
id1 = None
for i in range(5):
dev[0].dump_monitor()
dev[1].dump_monitor()
id1 = dev[1].dpp_pkex_init(identifier="test", code="unknown",
use_id=id1)
wait_dpp_fail(dev[0], "possible PKEX code mismatch")
ev = dev[0].wait_event(["DPP-PKEX-T-LIMIT"], timeout=1)
if ev is None:
raise Exception("PKEX t limit not reported")
def test_dpp_pkex_curve_mismatch(dev, apdev):
"""DPP and PKEX with mismatching curve"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve="P-256")
dev[1].dpp_pkex_init(identifier="test", code="secret", curve="P-384")
wait_dpp_fail(dev[0], "Mismatching PKEX curve: peer=20 own=19")
wait_dpp_fail(dev[1], "Peer indicated mismatching PKEX group - proposed 19")
def test_dpp_pkex_curve_mismatch_failure(dev, apdev):
"""DPP and PKEX with mismatching curve (local failure)"""
run_dpp_pkex_curve_mismatch_failure(dev, apdev, "=dpp_pkex_rx_exchange_req")
def test_dpp_pkex_curve_mismatch_failure2(dev, apdev):
"""DPP and PKEX with mismatching curve (local failure 2)"""
run_dpp_pkex_curve_mismatch_failure(dev, apdev,
"dpp_pkex_build_exchange_resp")
def run_dpp_pkex_curve_mismatch_failure(dev, apdev, func):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve="P-256")
with alloc_fail(dev[0], 1, func):
dev[1].dpp_pkex_init(identifier="test", code="secret", curve="P-384")
ev = dev[0].wait_event(["DPP-FAIL"], timeout=5)
if ev is None:
raise Exception("Failure not reported (dev 0)")
if "Mismatching PKEX curve: peer=20 own=19" not in ev:
raise Exception("Unexpected result: " + ev)
wait_dpp_fail(dev[0], "Mismatching PKEX curve: peer=20 own=19")
def test_dpp_pkex_exchange_resp_processing_failure(dev, apdev):
"""DPP and PKEX with local failure in processing Exchange Resp"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
with fail_test(dev[1], 1, "dpp_pkex_derive_Qr;dpp_pkex_rx_exchange_resp"):
dev[1].dpp_pkex_init(identifier="test", code="secret")
wait_fail_trigger(dev[1], "GET_FAIL")
def test_dpp_pkex_commit_reveal_req_processing_failure(dev, apdev):
"""DPP and PKEX with local failure in processing Commit Reveal Req"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
with alloc_fail(dev[0], 1,
"dpp_get_pubkey_point;dpp_pkex_rx_commit_reveal_req"):
dev[1].dpp_pkex_init(identifier="test", code="secret")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
def test_dpp_pkex_config2(dev, apdev):
"""DPP and PKEX with responder as the configurator"""
check_dpp_capab(dev[0])
conf_id = dev[0].dpp_configurator_add()
dev[0].set("dpp_configurator_params",
" conf=sta-dpp configurator=%d" % conf_id)
run_dpp_pkex2(dev, apdev)
def run_dpp_pkex2(dev, apdev, curve=None, init_extra=""):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve=curve,
listen_role="configurator")
dev[1].dpp_pkex_init(identifier="test", code="secret", role="enrollee",
curve=curve, extra=init_extra)
wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1])
def test_dpp_pkex_no_responder(dev, apdev):
"""DPP and PKEX with no responder (retry behavior)"""
check_dpp_capab(dev[0])
dev[0].dpp_pkex_init(identifier="test", code="secret")
for i in range(15):
ev = dev[0].wait_event(["DPP-TX ", "DPP-FAIL"], timeout=5)
if ev is None:
raise Exception("DPP PKEX failure not reported")
if "DPP-FAIL" not in ev:
continue
if "No response from PKEX peer" not in ev:
raise Exception("Unexpected failure reason: " + ev)
break
def test_dpp_pkex_after_retry(dev, apdev):
"""DPP and PKEX completing after retry"""
check_dpp_capab(dev[0])
dev[0].dpp_pkex_init(identifier="test", code="secret")
time.sleep(0.1)
dev[1].dpp_pkex_resp(2437, identifier="test", code="secret")
wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1],
allow_enrollee_failure=True)
def test_dpp_pkex_hostapd_responder(dev, apdev):
"""DPP PKEX with hostapd as responder"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
hapd.dpp_pkex_resp(2437, identifier="test", code="secret")
conf_id = dev[0].dpp_configurator_add()
dev[0].dpp_pkex_init(identifier="test", code="secret",
extra="conf=ap-dpp configurator=%d" % conf_id)
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
stop_initiator=True)
def test_dpp_pkex_hostapd_initiator(dev, apdev):
"""DPP PKEX with hostapd as initiator"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
conf_id = dev[0].dpp_configurator_add()
dev[0].set("dpp_configurator_params",
" conf=ap-dpp configurator=%d" % conf_id)
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
listen_role="configurator")
hapd.dpp_pkex_init(identifier="test", code="secret", role="enrollee")
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
stop_initiator=True)
def test_dpp_pkex_hostapd_errors(dev, apdev):
"""DPP PKEX errors with hostapd"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
id0 = hapd.dpp_bootstrap_gen(type="pkex")
tests = ["own=%d" % id0,
"own=%d identifier=foo" % id0,
""]
for t in tests:
if "FAIL" not in hapd.request("DPP_PKEX_ADD " + t):
raise Exception("Invalid DPP_PKEX_ADD accepted: " + t)
res = hapd.request("DPP_PKEX_ADD own=%d code=foo" % id0)
if "FAIL" in res:
raise Exception("Failed to add PKEX responder")
if "OK" not in hapd.request("DPP_PKEX_REMOVE " + res):
raise Exception("Failed to remove PKEX responder")
if "FAIL" not in hapd.request("DPP_PKEX_REMOVE " + res):
raise Exception("Unknown PKEX responder removal accepted")
res = hapd.request("DPP_PKEX_ADD own=%d code=foo" % id0)
if "FAIL" in res:
raise Exception("Failed to add PKEX responder")
if "OK" not in hapd.request("DPP_PKEX_REMOVE *"):
raise Exception("Failed to flush PKEX responders")
hapd.request("DPP_PKEX_REMOVE *")
def test_dpp_hostapd_configurator(dev, apdev):
"""DPP with hostapd as configurator/initiator"""
+ run_dpp_hostapd_configurator(dev, apdev)
+
+def test_dpp_hostapd_configurator_enrollee_v1(dev, apdev):
+ """DPP with hostapd as configurator/initiator with v1 enrollee"""
+ dev[0].set("dpp_version_override", "1")
+ run_dpp_hostapd_configurator(dev, apdev)
+
+def run_dpp_hostapd_configurator(dev, apdev):
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "1"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
id1 = hapd.dpp_qr_code(uri0)
res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
if "FAIL" in res:
raise Exception("DPP_BOOTSTRAP_INFO failed")
if "type=QRCODE" not in res:
raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
if "mac_addr=" + dev[0].own_addr() not in res:
raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
dev[0].dpp_listen(2412)
hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
stop_responder=True)
def test_dpp_hostapd_configurator_responder(dev, apdev):
"""DPP with hostapd as configurator/responder"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "1"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
hapd.set("dpp_configurator_params",
" conf=sta-dpp configurator=%d" % conf_id)
id0 = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_auth_init(uri=uri0, role="enrollee")
wait_auth_success(hapd, dev[0], configurator=hapd, enrollee=dev[0],
stop_initiator=True)
def test_dpp_hostapd_configurator_fragmentation(dev, apdev):
"""DPP with hostapd as configurator/initiator requiring fragmentation"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "1"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
id1 = hapd.dpp_qr_code(uri0)
res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
if "FAIL" in res:
raise Exception("DPP_BOOTSTRAP_INFO failed")
if "type=QRCODE" not in res:
raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
if "mac_addr=" + dev[0].own_addr() not in res:
raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
dev[0].dpp_listen(2412)
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
hapd.set("dpp_config_obj_override", conf)
hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
stop_responder=True)
+def test_dpp_hostapd_enrollee_fragmentation(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS fragmentation"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ " conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
+ stop_responder=True)
+
+def test_dpp_hostapd_enrollee_gas_timeout(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS timeout"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0])
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+def test_dpp_hostapd_enrollee_gas_timeout_comeback(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS timeout during comeback"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=4)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+def process_dpp_frames(dev, count=3):
+ for i in range(count):
+ msg = dev.mgmt_rx()
+ cmd = "MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())
+ if "OK" not in dev.request(cmd):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_dpp_hostapd_enrollee_gas_errors(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS query local errors"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+
+ # GAS without comeback
+ tests = [(1, "gas_query_append;gas_query_rx_initial", 3, True),
+ (1, "gas_query_rx_initial", 3, True),
+ (1, "gas_query_tx_initial_req", 2, True),
+ (1, "gas_query_ap_req", 2, False)]
+ for count, func, frame_count, wait_ev in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].dpp_listen(2437, role="configurator")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ with alloc_fail(hapd, count, func):
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=frame_count)
+ if wait_ev:
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=INTERNAL_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+
+ # GAS with comeback
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+
+ tests = [(1, "gas_query_append;gas_query_rx_comeback", 4),
+ (1, "wpabuf_alloc;gas_query_tx_comeback_req", 3),
+ (1, "hostapd_drv_send_action;gas_query_tx_comeback_req", 3)]
+ for count, func, frame_count in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].dpp_listen(2437, role="configurator")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ with alloc_fail(hapd, count, func):
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=frame_count)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=INTERNAL_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+
+def test_dpp_hostapd_enrollee_gas_proto(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS query protocol testing"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ bssid = hapd.own_addr()
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ # GAS: Advertisement Protocol changed between initial and comeback response from 02:00:00:00:00:00
+ adv_proto = "6c087fdd05506f9a1a02"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Another comeback delay
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 1)
+ adv_proto = "6c087fdd05506f9a1a01"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ msg = dev[0].mgmt_rx()
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 1)
+ # GAS: Invalid comeback response with non-zero frag_id and comeback_delay from 02:00:00:00:00:00
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Valid comeback response
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ msg = dev[0].mgmt_rx()
+ # GAS: Drop frame as possible retry of previous fragment
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Unexpected frag_id in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x82, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # GAS: Unexpected initial response from 02:00:00:00:00:00 dialog token 3 when waiting for comeback response
+ hdr = struct.pack('<BBBHBH', 4, 11, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Allow non-zero status for outstanding comeback response
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 95, 0x80, 0)
+ # GAS: Ignore 1 octets of extra data after Query Response from 02:00:00:00:00:00
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001" + "ff"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No pending query found for 02:00:00:00:00:00 dialog token 4
+ hdr = struct.pack('<BBBHBH', 4, 13, (dialog_token + 1) % 256, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Truncated Query Response in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "0010"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No room for GAS Response Length
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "03"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Unexpected Advertisement Protocol element ID 0 in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ adv_proto_broken = "0000"
+ action = binascii.hexlify(hdr).decode() + adv_proto_broken + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No room for Advertisement Protocol element in the response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ adv_proto_broken = "00ff"
+ action = binascii.hexlify(hdr).decode() + adv_proto_broken + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # No room for Comeback Delay
+ hdr = struct.pack('<BBBHBB', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode()
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # No room for frag_id
+ hdr = struct.pack('<BBBH', 4, 13, dialog_token, 0)
+ action = binascii.hexlify(hdr).decode()
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Query to 02:00:00:00:00:00 dialog token 3 failed - status code 1
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 1, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=FAILURE" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=2)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Unexpected comeback delay
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ adv_proto = "6c087fdd05506f9a1a01"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Query to 02:00:00:00:00:00 dialog token 3 failed - status code 1
+ hdr = struct.pack('<BBBHBH', 4, 11, dialog_token, 1, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=FAILURE" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+def test_dpp_hostapd_enrollee_gas_tx_status_errors(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS TX status errors"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ # GAS: TX status for unexpected destination
+ frame = "d0003a01" + "222222222222"
+ frame += hapd.own_addr().replace(':', '') + "ffffffffffff"
+ frame += "5000" + "040a"
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=1 buf=" + frame)
+
+ # GAS: No ACK to GAS request
+ frame = "d0003a01" + dev[0].own_addr().replace(':', '')
+ frame += hapd.own_addr().replace(':', '') + "ffffffffffff"
+ frame += "5000" + "040a"
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=0 buf=" + frame)
+
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+ # GAS: Unexpected TX status: dst=02:00:00:00:00:00 ok=1 - no query in progress
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=1 buf=" + frame)
+ hapd.set("ext_mgmt_frame_handling", "0")
+
def test_dpp_hostapd_configurator_override_objects(dev, apdev):
"""DPP with hostapd as configurator and override objects"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "1"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
id1 = hapd.dpp_qr_code(uri0)
res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
if "FAIL" in res:
raise Exception("DPP_BOOTSTRAP_INFO failed")
dev[0].dpp_listen(2412)
discovery = '{\n"ssid":"mywifi"\n}'
groups = '[\n {"groupId":"home","netRole":"sta"},\n {"groupId":"cottage","netRole":"sta"}\n]'
hapd.set("dpp_discovery_override", discovery)
hapd.set("dpp_groups_override", groups)
hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
stop_responder=True)
def test_dpp_own_config(dev, apdev):
"""DPP configurator signing own connector"""
try:
run_dpp_own_config(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_own_config_group_id(dev, apdev):
"""DPP configurator signing own connector"""
try:
run_dpp_own_config(dev, apdev, extra=" group_id=test-group")
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_own_config_curve_mismatch(dev, apdev):
"""DPP configurator signing own connector using mismatching curve"""
try:
run_dpp_own_config(dev, apdev, own_curve="BP-384", expect_failure=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_own_config(dev, apdev, own_curve=None, expect_failure=False,
extra=None):
check_dpp_capab(dev[0], own_curve and "BP" in own_curve)
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
conf_id = dev[0].dpp_configurator_add()
dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id,
extra=extra)
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
update_hapd_config(hapd)
dev[0].set("dpp_config_processing", "1")
cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d%s" % (conf_id, extra)
if own_curve:
cmd += " curve=" + own_curve
res = dev[0].request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate own configuration")
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
dev[0].select_network(id, freq="2412")
if expect_failure:
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected connection")
dev[0].request("DISCONNECT")
else:
dev[0].wait_connected()
def test_dpp_own_config_ap(dev, apdev):
"""DPP configurator (AP) signing own connector"""
try:
run_dpp_own_config_ap(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_own_config_ap_group_id(dev, apdev):
"""DPP configurator (AP) signing own connector (group_id)"""
try:
run_dpp_own_config_ap(dev, apdev, extra=" group_id=test-group")
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_own_config_ap_reconf(dev, apdev):
"""DPP configurator (AP) signing own connector and configurator reconf"""
try:
run_dpp_own_config_ap(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_own_config_ap(dev, apdev, reconf_configurator=False, extra=None):
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
if reconf_configurator:
csign = hapd.request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
if "FAIL" in csign or len(csign) == 0:
raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d%s" % (conf_id, extra)
res = hapd.request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate own configuration")
update_hapd_config(hapd)
if reconf_configurator:
hapd.dpp_configurator_remove(conf_id)
conf_id = hapd.dpp_configurator_add(key=csign)
id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
dev[0].set("dpp_config_processing", "2")
dev[0].dpp_listen(2412)
hapd.dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id,
extra=extra)
wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
dev[0].wait_connected()
def test_dpp_intro_mismatch(dev, apdev):
"""DPP network introduction mismatch cases"""
try:
wpas = None
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
check_dpp_capab(wpas)
run_dpp_intro_mismatch(dev, apdev, wpas)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
dev[2].set("dpp_config_processing", "0", allow_fail=True)
if wpas:
wpas.set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_intro_mismatch(dev, apdev, wpas):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
check_dpp_capab(dev[2])
logger.info("Start AP in unconfigured state")
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
logger.info("Provision AP with DPP configuration")
conf_id = dev[1].dpp_configurator_add()
dev[1].set("dpp_groups_override", '[{"groupId":"a","netRole":"ap"}]')
dev[1].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
update_hapd_config(hapd)
logger.info("Provision STA0 with DPP Connector that has mismatching groupId")
dev[0].set("dpp_config_processing", "2")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].set("dpp_groups_override", '[{"groupId":"b","netRole":"sta"}]')
dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", configurator=conf_id)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
logger.info("Provision STA2 with DPP Connector that has mismatching C-sign-key")
dev[2].set("dpp_config_processing", "2")
id2 = dev[2].dpp_bootstrap_gen(chan="81/1", mac=True)
uri2 = dev[2].request("DPP_BOOTSTRAP_GET_URI %d" % id2)
dev[2].dpp_listen(2412)
conf_id_2 = dev[1].dpp_configurator_add()
dev[1].set("dpp_groups_override", '')
dev[1].dpp_auth_init(uri=uri2, conf="sta-dpp", configurator=conf_id_2)
wait_auth_success(dev[2], dev[1], configurator=dev[1], enrollee=dev[2])
logger.info("Provision STA5 with DPP Connector that has mismatching netAccessKey EC group")
wpas.set("dpp_config_processing", "2")
id5 = wpas.dpp_bootstrap_gen(chan="81/1", mac=True, curve="P-521")
uri5 = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id5)
wpas.dpp_listen(2412)
dev[1].set("dpp_groups_override", '')
dev[1].dpp_auth_init(uri=uri5, conf="sta-dpp", configurator=conf_id)
wait_auth_success(wpas, dev[1], configurator=dev[1], enrollee=wpas)
logger.info("Verify network introduction results")
ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
if ev is None:
raise Exception("DPP network introduction result not seen on STA0")
if "status=8" not in ev:
raise Exception("Unexpected network introduction result on STA0: " + ev)
ev = dev[2].wait_event(["DPP-INTRO"], timeout=5)
if ev is None:
raise Exception("DPP network introduction result not seen on STA2")
if "status=8" not in ev:
raise Exception("Unexpected network introduction result on STA2: " + ev)
ev = wpas.wait_event(["DPP-INTRO"], timeout=10)
if ev is None:
raise Exception("DPP network introduction result not seen on STA5")
if "status=7" not in ev:
raise Exception("Unexpected network introduction result on STA5: " + ev)
def run_dpp_proto_init(dev, test_dev, test, mutual=False, unicast=True,
listen=True, chan="81/1", init_enrollee=False,
incompatible_roles=False):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[test_dev].set("dpp_test", str(test))
if init_enrollee:
conf_id = dev[0].dpp_configurator_add()
else:
conf_id = dev[1].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan=chan, mac=unicast)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
if mutual:
id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
id0b = dev[0].dpp_qr_code(uri1b)
qr = "mutual"
else:
qr = None
if init_enrollee:
if incompatible_roles:
role = "enrollee"
else:
role = "configurator"
dev[0].set("dpp_configurator_params",
" conf=sta-dpp configurator=%d" % conf_id)
elif incompatible_roles:
role = "enrollee"
else:
role = None
if listen:
dev[0].dpp_listen(2412, qr=qr, role=role)
role = None
configurator = None
conf = None
own = None
if init_enrollee:
role="enrollee"
else:
configurator=conf_id
conf="sta-dpp"
if incompatible_roles:
role="enrollee"
if mutual:
own = id1b
dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator,
conf=conf, own=own)
return uri0, role, configurator, conf, own
def test_dpp_proto_after_wrapped_data_auth_req(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in Auth Req"""
run_dpp_proto_init(dev, 1, 1)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Request not seen")
if "type=0" not in ev or "ignore=invalid-attributes" not in ev:
raise Exception("Unexpected RX info: " + ev)
ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_auth_req_stop_after_ack(dev, apdev):
"""DPP initiator stopping after ACK, but no response"""
run_dpp_proto_init(dev, 1, 1, listen=True)
ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
if ev is None:
raise Exception("Authentication failure not reported")
def test_dpp_auth_req_retries(dev, apdev):
"""DPP initiator retries with no ACK"""
check_dpp_capab(dev[1])
dev[1].set("dpp_init_max_tries", "3")
dev[1].set("dpp_init_retry_time", "1000")
dev[1].set("dpp_resp_wait_time", "100")
run_dpp_proto_init(dev, 1, 1, unicast=False, listen=False)
for i in range(3):
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("Auth Req not sent (%d)" % i)
ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
if ev is None:
raise Exception("Authentication failure not reported")
def test_dpp_auth_req_retries_multi_chan(dev, apdev):
"""DPP initiator retries with no ACK and multiple channels"""
check_dpp_capab(dev[1])
dev[1].set("dpp_init_max_tries", "3")
dev[1].set("dpp_init_retry_time", "1000")
dev[1].set("dpp_resp_wait_time", "100")
run_dpp_proto_init(dev, 1, 1, unicast=False, listen=False,
chan="81/1,81/6,81/11")
for i in range(3 * 3):
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("Auth Req not sent (%d)" % i)
ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
if ev is None:
raise Exception("Authentication failure not reported")
def test_dpp_proto_after_wrapped_data_auth_resp(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in Auth Resp"""
run_dpp_proto_init(dev, 0, 2)
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Response not seen")
if "type=1" not in ev or "ignore=invalid-attributes" not in ev:
raise Exception("Unexpected RX info: " + ev)
ev = dev[0].wait_event(["DPP-RX"], timeout=1)
if ev is None or "type=0" not in ev:
raise Exception("DPP Authentication Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_proto_after_wrapped_data_auth_conf(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in Auth Conf"""
run_dpp_proto_init(dev, 1, 3)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "type=0" not in ev:
raise Exception("DPP Authentication Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Authentication Confirm not seen")
if "type=2" not in ev or "ignore=invalid-attributes" not in ev:
raise Exception("Unexpected RX info: " + ev)
def test_dpp_proto_after_wrapped_data_conf_req(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in Conf Req"""
run_dpp_proto_init(dev, 0, 6)
ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=10)
if ev is None:
raise Exception("DPP Configuration failure not seen")
def test_dpp_proto_after_wrapped_data_conf_resp(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in Conf Resp"""
run_dpp_proto_init(dev, 1, 7)
ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=10)
if ev is None:
raise Exception("DPP Configuration failure not seen")
def test_dpp_proto_zero_i_capab(dev, apdev):
"""DPP protocol testing - zero I-capability in Auth Req"""
run_dpp_proto_init(dev, 1, 8)
wait_dpp_fail(dev[0], "Invalid role in I-capabilities 0x00")
ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_proto_zero_r_capab(dev, apdev):
"""DPP protocol testing - zero R-capability in Auth Resp"""
run_dpp_proto_init(dev, 0, 9)
wait_dpp_fail(dev[1], "Unexpected role in R-capabilities 0x00")
ev = dev[0].wait_event(["DPP-RX"], timeout=1)
if ev is None or "type=0" not in ev:
raise Exception("DPP Authentication Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def run_dpp_proto_auth_req_missing(dev, test, reason, mutual=False):
run_dpp_proto_init(dev, 1, test, mutual=mutual)
wait_dpp_fail(dev[0], reason)
ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_proto_auth_req_no_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - no R-bootstrap key in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 10, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
def test_dpp_proto_auth_req_invalid_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid R-bootstrap key in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 68, "No matching own bootstrapping key found - ignore message")
def test_dpp_proto_auth_req_no_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - no I-bootstrap key in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 11, "Missing or invalid required Initiator Bootstrapping Key Hash attribute")
def test_dpp_proto_auth_req_invalid_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid I-bootstrap key in Auth Req"""
run_dpp_proto_init(dev, 1, 69, mutual=True)
ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("DPP scan request not seen")
ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("DPP response pending indivation not seen")
def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
"""DPP protocol testing - no I-proto key in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 12, "Missing required Initiator Protocol Key attribute")
def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
"""DPP protocol testing - invalid I-proto key in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
"""DPP protocol testing - no I-nonce in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 13, "Missing or invalid I-nonce")
def test_dpp_proto_auth_req_invalid_i_nonce(dev, apdev):
"""DPP protocol testing - invalid I-nonce in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 81, "Missing or invalid I-nonce")
def test_dpp_proto_auth_req_no_i_capab(dev, apdev):
"""DPP protocol testing - no I-capab in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 14, "Missing or invalid I-capab")
def test_dpp_proto_auth_req_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in Auth Req"""
run_dpp_proto_auth_req_missing(dev, 15, "Missing or invalid required Wrapped Data attribute")
def run_dpp_proto_auth_resp_missing(dev, test, reason,
incompatible_roles=False):
run_dpp_proto_init(dev, 0, test, mutual=True,
incompatible_roles=incompatible_roles)
if reason is None:
if incompatible_roles:
ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
if ev is None:
raise Exception("DPP-NOT-COMPATIBLE not reported")
time.sleep(0.1)
return
wait_dpp_fail(dev[1], reason)
ev = dev[0].wait_event(["DPP-RX"], timeout=1)
if ev is None or "type=0" not in ev:
raise Exception("DPP Authentication Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_proto_auth_resp_no_status(dev, apdev):
"""DPP protocol testing - no Status in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 16, "Missing or invalid required DPP Status attribute")
def test_dpp_proto_auth_resp_status_no_status(dev, apdev):
"""DPP protocol testing - no Status in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 16,
"Missing or invalid required DPP Status attribute",
incompatible_roles=True)
def test_dpp_proto_auth_resp_invalid_status(dev, apdev):
"""DPP protocol testing - invalid Status in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 74, "Responder reported failure")
def test_dpp_proto_auth_resp_no_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - no R-bootstrap key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 17, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
def test_dpp_proto_auth_resp_status_no_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - no R-bootstrap key in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 17,
"Missing or invalid required Responder Bootstrapping Key Hash attribute",
incompatible_roles=True)
def test_dpp_proto_auth_resp_invalid_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid R-bootstrap key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 70, "Unexpected Responder Bootstrapping Key Hash value")
def test_dpp_proto_auth_resp_status_invalid_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid R-bootstrap key in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 70,
"Unexpected Responder Bootstrapping Key Hash value",
incompatible_roles=True)
def test_dpp_proto_auth_resp_no_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - no I-bootstrap key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 18, None)
def test_dpp_proto_auth_resp_status_no_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - no I-bootstrap key in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 18, None, incompatible_roles=True)
def test_dpp_proto_auth_resp_invalid_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid I-bootstrap key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 71, "Initiator Bootstrapping Key Hash attribute did not match")
def test_dpp_proto_auth_resp_status_invalid_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid I-bootstrap key in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 71,
"Initiator Bootstrapping Key Hash attribute did not match",
incompatible_roles=True)
def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
"""DPP protocol testing - no R-Proto Key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 19, "Missing required Responder Protocol Key attribute")
def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
"""DPP protocol testing - invalid R-Proto Key in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
"""DPP protocol testing - no R-nonce in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 20, "Missing or invalid R-nonce")
def test_dpp_proto_auth_resp_no_i_nonce(dev, apdev):
"""DPP protocol testing - no I-nonce in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 21, "Missing or invalid I-nonce")
def test_dpp_proto_auth_resp_status_no_i_nonce(dev, apdev):
"""DPP protocol testing - no I-nonce in Auth Resp(status)"""
run_dpp_proto_auth_resp_missing(dev, 21, "Missing or invalid I-nonce",
incompatible_roles=True)
def test_dpp_proto_auth_resp_no_r_capab(dev, apdev):
"""DPP protocol testing - no R-capab in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 22, "Missing or invalid R-capabilities")
def test_dpp_proto_auth_resp_no_r_auth(dev, apdev):
"""DPP protocol testing - no R-auth in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 23, "Missing or invalid Secondary Wrapped Data")
def test_dpp_proto_auth_resp_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in Auth Resp"""
run_dpp_proto_auth_resp_missing(dev, 24, "Missing or invalid required Wrapped Data attribute")
def test_dpp_proto_auth_resp_i_nonce_mismatch(dev, apdev):
"""DPP protocol testing - I-nonce mismatch in Auth Resp"""
run_dpp_proto_init(dev, 0, 30, mutual=True)
wait_dpp_fail(dev[1], "I-nonce mismatch")
ev = dev[0].wait_event(["DPP-RX"], timeout=1)
if ev is None or "type=0" not in ev:
raise Exception("DPP Authentication Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected DPP message seen")
def test_dpp_proto_auth_resp_incompatible_r_capab(dev, apdev):
"""DPP protocol testing - Incompatible R-capab in Auth Resp"""
run_dpp_proto_init(dev, 0, 31, mutual=True)
wait_dpp_fail(dev[1], "Unexpected role in R-capabilities 0x02")
wait_dpp_fail(dev[0], "Peer reported incompatible R-capab role")
def test_dpp_proto_auth_resp_r_auth_mismatch(dev, apdev):
"""DPP protocol testing - R-auth mismatch in Auth Resp"""
run_dpp_proto_init(dev, 0, 32, mutual=True)
wait_dpp_fail(dev[1], "Mismatching Responder Authenticating Tag")
wait_dpp_fail(dev[0], "Peer reported authentication failure")
def test_dpp_proto_auth_resp_r_auth_mismatch_failure(dev, apdev):
"""DPP protocol testing - Auth Conf RX processing failure"""
with alloc_fail(dev[0], 1, "dpp_auth_conf_rx_failure"):
run_dpp_proto_init(dev, 0, 32, mutual=True)
wait_dpp_fail(dev[0], "Authentication failed")
def test_dpp_proto_auth_resp_r_auth_mismatch_failure2(dev, apdev):
"""DPP protocol testing - Auth Conf RX processing failure 2"""
with fail_test(dev[0], 1, "dpp_auth_conf_rx_failure"):
run_dpp_proto_init(dev, 0, 32, mutual=True)
wait_dpp_fail(dev[0], "AES-SIV decryption failed")
def run_dpp_proto_auth_conf_missing(dev, test, reason):
run_dpp_proto_init(dev, 1, test, mutual=True)
if reason is None:
time.sleep(0.1)
return
wait_dpp_fail(dev[0], reason)
def test_dpp_proto_auth_conf_no_status(dev, apdev):
"""DPP protocol testing - no Status in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 25, "Missing or invalid required DPP Status attribute")
def test_dpp_proto_auth_conf_invalid_status(dev, apdev):
"""DPP protocol testing - invalid Status in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 75, "Authentication failed")
def test_dpp_proto_auth_conf_no_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - no R-bootstrap key in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 26, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
def test_dpp_proto_auth_conf_invalid_r_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid R-bootstrap key in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 72, "Responder Bootstrapping Key Hash mismatch")
def test_dpp_proto_auth_conf_no_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - no I-bootstrap key in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 27, "Missing Initiator Bootstrapping Key Hash attribute")
def test_dpp_proto_auth_conf_invalid_i_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid I-bootstrap key in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 73, "Initiator Bootstrapping Key Hash mismatch")
def test_dpp_proto_auth_conf_no_i_auth(dev, apdev):
"""DPP protocol testing - no I-Auth in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 28, "Missing or invalid Initiator Authenticating Tag")
def test_dpp_proto_auth_conf_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in Auth Conf"""
run_dpp_proto_auth_conf_missing(dev, 29, "Missing or invalid required Wrapped Data attribute")
def test_dpp_proto_auth_conf_i_auth_mismatch(dev, apdev):
"""DPP protocol testing - I-auth mismatch in Auth Conf"""
run_dpp_proto_init(dev, 1, 33, mutual=True)
wait_dpp_fail(dev[0], "Mismatching Initiator Authenticating Tag")
def test_dpp_proto_auth_conf_replaced_by_resp(dev, apdev):
"""DPP protocol testing - Auth Conf replaced by Resp"""
run_dpp_proto_init(dev, 1, 65, mutual=True)
wait_dpp_fail(dev[0], "Unexpected Authentication Response")
def run_dpp_proto_conf_req_missing(dev, test, reason):
run_dpp_proto_init(dev, 0, test)
wait_dpp_fail(dev[1], reason)
def test_dpp_proto_conf_req_no_e_nonce(dev, apdev):
"""DPP protocol testing - no E-nonce in Conf Req"""
run_dpp_proto_conf_req_missing(dev, 51,
"Missing or invalid Enrollee Nonce attribute")
def test_dpp_proto_conf_req_invalid_e_nonce(dev, apdev):
"""DPP protocol testing - invalid E-nonce in Conf Req"""
run_dpp_proto_conf_req_missing(dev, 83,
"Missing or invalid Enrollee Nonce attribute")
def test_dpp_proto_conf_req_no_config_attr_obj(dev, apdev):
"""DPP protocol testing - no Config Attr Obj in Conf Req"""
run_dpp_proto_conf_req_missing(dev, 52,
"Missing or invalid Config Attributes attribute")
def test_dpp_proto_conf_req_invalid_config_attr_obj(dev, apdev):
"""DPP protocol testing - invalid Config Attr Obj in Conf Req"""
run_dpp_proto_conf_req_missing(dev, 76,
"Unsupported wi-fi_tech")
def test_dpp_proto_conf_req_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in Conf Req"""
run_dpp_proto_conf_req_missing(dev, 53,
"Missing or invalid required Wrapped Data attribute")
def run_dpp_proto_conf_resp_missing(dev, test, reason):
run_dpp_proto_init(dev, 1, test)
wait_dpp_fail(dev[0], reason)
def test_dpp_proto_conf_resp_no_e_nonce(dev, apdev):
"""DPP protocol testing - no E-nonce in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 54,
"Missing or invalid Enrollee Nonce attribute")
def test_dpp_proto_conf_resp_no_config_obj(dev, apdev):
"""DPP protocol testing - no Config Object in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 55,
"Missing required Configuration Object attribute")
def test_dpp_proto_conf_resp_no_status(dev, apdev):
"""DPP protocol testing - no Status in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 56,
"Missing or invalid required DPP Status attribute")
def test_dpp_proto_conf_resp_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 57,
"Missing or invalid required Wrapped Data attribute")
def test_dpp_proto_conf_resp_invalid_status(dev, apdev):
"""DPP protocol testing - invalid Status in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 58,
"Configurator rejected configuration")
def test_dpp_proto_conf_resp_e_nonce_mismatch(dev, apdev):
"""DPP protocol testing - E-nonce mismatch in Conf Resp"""
run_dpp_proto_conf_resp_missing(dev, 59,
"Enrollee Nonce mismatch")
def test_dpp_proto_stop_at_auth_req(dev, apdev):
"""DPP protocol testing - stop when receiving Auth Req"""
run_dpp_proto_init(dev, 0, 87)
ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
if ev is None:
raise Exception("Authentication init failure not reported")
def test_dpp_proto_stop_at_auth_resp(dev, apdev):
"""DPP protocol testing - stop when receiving Auth Resp"""
uri0, role, configurator, conf, own = run_dpp_proto_init(dev, 1, 88)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("Auth Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("Auth Resp TX not seen")
ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
if ev is not None:
raise Exception("Unexpected Auth Conf TX")
ev = dev[0].wait_event(["DPP-FAIL"], timeout=2)
if ev is None or "No Auth Confirm received" not in ev:
raise Exception("DPP-FAIL for missing Auth Confirm not reported")
time.sleep(0.1)
# Try again without special testing behavior to confirm Responder is able
# to accept a new provisioning attempt.
dev[1].set("dpp_test", "0")
dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator,
conf=conf, own=own)
wait_auth_success(dev[0], dev[1])
def test_dpp_proto_stop_at_auth_conf(dev, apdev):
"""DPP protocol testing - stop when receiving Auth Conf"""
run_dpp_proto_init(dev, 0, 89, init_enrollee=True)
ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=10)
if ev is None:
raise Exception("Enrollee did not start GAS")
ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("Enrollee did not time out GAS")
if "result=TIMEOUT" not in ev:
raise Exception("Unexpected GAS result: " + ev)
def test_dpp_proto_stop_at_auth_conf_tx(dev, apdev):
"""DPP protocol testing - stop when transmitting Auth Conf (Registrar)"""
run_dpp_proto_init(dev, 1, 89, init_enrollee=True)
wait_auth_success(dev[0], dev[1], timeout=10)
ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected GAS query")
# There is currently no timeout on GAS server side, so no event to wait for
# in this case.
def test_dpp_proto_stop_at_auth_conf_tx2(dev, apdev):
"""DPP protocol testing - stop when transmitting Auth Conf (Enrollee)"""
run_dpp_proto_init(dev, 1, 89)
wait_auth_success(dev[0], dev[1], timeout=10)
ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
if ev is None or "result=TIMEOUT" not in ev:
raise Exception("GAS query did not time out")
def test_dpp_proto_stop_at_conf_req(dev, apdev):
"""DPP protocol testing - stop when receiving Auth Req"""
run_dpp_proto_init(dev, 1, 90)
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=10)
if ev is None:
raise Exception("Enrollee did not start GAS")
ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("Enrollee did not time out GAS")
if "result=TIMEOUT" not in ev:
raise Exception("Unexpected GAS result: " + ev)
def run_dpp_proto_init_pkex(dev, test_dev, test):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[test_dev].set("dpp_test", str(test))
dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
dev[1].dpp_pkex_init(identifier="test", code="secret")
def test_dpp_proto_after_wrapped_data_pkex_cr_req(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in PKEX CR Req"""
run_dpp_proto_init_pkex(dev, 1, 4)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "type=7" not in ev:
raise Exception("PKEX Exchange Request not seen")
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "type=9" not in ev:
raise Exception("PKEX Commit-Reveal Request not seen")
if "ignore=invalid-attributes" not in ev:
raise Exception("Unexpected RX info: " + ev)
def test_dpp_proto_after_wrapped_data_pkex_cr_resp(dev, apdev):
"""DPP protocol testing - attribute after Wrapped Data in PKEX CR Resp"""
run_dpp_proto_init_pkex(dev, 0, 5)
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None or "type=8" not in ev:
raise Exception("PKEX Exchange Response not seen")
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None or "type=10" not in ev:
raise Exception("PKEX Commit-Reveal Response not seen")
if "ignore=invalid-attributes" not in ev:
raise Exception("Unexpected RX info: " + ev)
def run_dpp_proto_pkex_req_missing(dev, test, reason):
run_dpp_proto_init_pkex(dev, 1, test)
wait_dpp_fail(dev[0], reason)
def run_dpp_proto_pkex_resp_missing(dev, test, reason):
run_dpp_proto_init_pkex(dev, 0, test)
wait_dpp_fail(dev[1], reason)
def test_dpp_proto_pkex_exchange_req_no_finite_cyclic_group(dev, apdev):
"""DPP protocol testing - no Finite Cyclic Group in PKEX Exchange Request"""
run_dpp_proto_pkex_req_missing(dev, 34,
"Missing or invalid Finite Cyclic Group attribute")
def test_dpp_proto_pkex_exchange_req_no_encrypted_key(dev, apdev):
"""DPP protocol testing - no Encrypted Key in PKEX Exchange Request"""
run_dpp_proto_pkex_req_missing(dev, 35,
"Missing Encrypted Key attribute")
def test_dpp_proto_pkex_exchange_resp_no_status(dev, apdev):
"""DPP protocol testing - no Status in PKEX Exchange Response"""
run_dpp_proto_pkex_resp_missing(dev, 36, "No DPP Status attribute")
def test_dpp_proto_pkex_exchange_resp_no_encrypted_key(dev, apdev):
"""DPP protocol testing - no Encrypted Key in PKEX Exchange Response"""
run_dpp_proto_pkex_resp_missing(dev, 37, "Missing Encrypted Key attribute")
def test_dpp_proto_pkex_cr_req_no_bootstrap_key(dev, apdev):
"""DPP protocol testing - no Bootstrap Key in PKEX Commit-Reveal Request"""
run_dpp_proto_pkex_req_missing(dev, 38,
"No valid peer bootstrapping key found")
def test_dpp_proto_pkex_cr_req_no_i_auth_tag(dev, apdev):
"""DPP protocol testing - no I-Auth Tag in PKEX Commit-Reveal Request"""
run_dpp_proto_pkex_req_missing(dev, 39, "No valid u (I-Auth tag) found")
def test_dpp_proto_pkex_cr_req_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in PKEX Commit-Reveal Request"""
run_dpp_proto_pkex_req_missing(dev, 40, "Missing or invalid required Wrapped Data attribute")
def test_dpp_proto_pkex_cr_resp_no_bootstrap_key(dev, apdev):
"""DPP protocol testing - no Bootstrap Key in PKEX Commit-Reveal Response"""
run_dpp_proto_pkex_resp_missing(dev, 41,
"No valid peer bootstrapping key found")
def test_dpp_proto_pkex_cr_resp_no_r_auth_tag(dev, apdev):
"""DPP protocol testing - no R-Auth Tag in PKEX Commit-Reveal Response"""
run_dpp_proto_pkex_resp_missing(dev, 42, "No valid v (R-Auth tag) found")
def test_dpp_proto_pkex_cr_resp_no_wrapped_data(dev, apdev):
"""DPP protocol testing - no Wrapped Data in PKEX Commit-Reveal Response"""
run_dpp_proto_pkex_resp_missing(dev, 43, "Missing or invalid required Wrapped Data attribute")
def test_dpp_proto_pkex_exchange_req_invalid_encrypted_key(dev, apdev):
"""DPP protocol testing - invalid Encrypted Key in PKEX Exchange Request"""
run_dpp_proto_pkex_req_missing(dev, 44,
"Invalid Encrypted Key value")
def test_dpp_proto_pkex_exchange_resp_invalid_encrypted_key(dev, apdev):
"""DPP protocol testing - invalid Encrypted Key in PKEX Exchange Response"""
run_dpp_proto_pkex_resp_missing(dev, 45,
"Invalid Encrypted Key value")
def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
"""DPP protocol testing - invalid Status in PKEX Exchange Response"""
run_dpp_proto_pkex_resp_missing(dev, 46,
"PKEX failed (peer indicated failure)")
def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
run_dpp_proto_pkex_req_missing(dev, 47,
"Peer bootstrapping key is invalid")
def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
"""DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
run_dpp_proto_pkex_resp_missing(dev, 48,
"Peer bootstrapping key is invalid")
def test_dpp_proto_pkex_cr_req_i_auth_tag_mismatch(dev, apdev):
"""DPP protocol testing - I-auth tag mismatch in PKEX Commit-Reveal Request"""
run_dpp_proto_pkex_req_missing(dev, 49, "No valid u (I-Auth tag) found")
def test_dpp_proto_pkex_cr_resp_r_auth_tag_mismatch(dev, apdev):
"""DPP protocol testing - R-auth tag mismatch in PKEX Commit-Reveal Response"""
run_dpp_proto_pkex_resp_missing(dev, 50, "No valid v (R-Auth tag) found")
def test_dpp_proto_stop_at_pkex_exchange_resp(dev, apdev):
"""DPP protocol testing - stop when receiving PKEX Exchange Response"""
run_dpp_proto_init_pkex(dev, 1, 84)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Resp not seen")
ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
if ev is not None:
raise Exception("Unexpected PKEX CR Req TX")
def test_dpp_proto_stop_at_pkex_cr_req(dev, apdev):
"""DPP protocol testing - stop when receiving PKEX CR Request"""
run_dpp_proto_init_pkex(dev, 0, 85)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Resp not seen")
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX CR Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=0.1)
if ev is not None:
raise Exception("Unexpected PKEX CR Resp TX")
def test_dpp_proto_stop_at_pkex_cr_resp(dev, apdev):
"""DPP protocol testing - stop when receiving PKEX CR Response"""
run_dpp_proto_init_pkex(dev, 1, 86)
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX Exchange Resp not seen")
ev = dev[1].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX CR Req TX not seen")
ev = dev[0].wait_event(["DPP-TX "], timeout=5)
if ev is None:
raise Exception("PKEX CR Resp TX not seen")
ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
if ev is not None:
raise Exception("Unexpected Auth Req TX")
def test_dpp_proto_network_introduction(dev, apdev):
"""DPP protocol testing - network introduction"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
for test in [60, 61, 80, 82]:
dev[0].set("dpp_test", str(test))
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-TX "], timeout=10)
if ev is None or "type=5" not in ev:
raise Exception("Peer Discovery Request TX not reported")
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=2)
if ev is None or "result=SUCCESS" not in ev:
raise Exception("Peer Discovery Request TX status not reported")
ev = hapd.wait_event(["DPP-RX"], timeout=10)
if ev is None or "type=5" not in ev:
raise Exception("Peer Discovery Request RX not reported")
if test == 80:
ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
if ev is None:
raise Exception("DPP-INTRO not reported for test 80")
if "status=7" not in ev:
raise Exception("Unexpected result in test 80: " + ev)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
hapd.dump_monitor()
dev[0].set("dpp_test", "0")
for test in [62, 63, 64, 77, 78, 79]:
hapd.set("dpp_test", str(test))
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
if ev is None:
raise Exception("Peer introduction result not reported (test %d)" % test)
if test == 77:
if "fail=transaction_id_mismatch" not in ev:
raise Exception("Connector validation failure not reported")
elif test == 78:
if "status=254" not in ev:
raise Exception("Invalid status value not reported")
elif test == 79:
if "fail=peer_connector_validation_failed" not in ev:
raise Exception("Connector validation failure not reported")
elif "status=" in ev:
raise Exception("Unexpected peer introduction result (test %d): " % test + ev)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
hapd.dump_monitor()
hapd.set("dpp_test", "0")
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
dpp_csign=params1_csign, dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey)
def test_dpp_hostapd_auth_conf_timeout(dev, apdev):
"""DPP Authentication Confirm timeout in hostapd"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
hapd.dpp_listen(2412)
dev[0].set("dpp_test", "88")
dev[0].dpp_auth_init(uri=uri_h)
ev = hapd.wait_event(["DPP-FAIL"], timeout=10)
if ev is None:
raise Exception("DPP-FAIL not reported")
if "No Auth Confirm received" not in ev:
raise Exception("Unexpected failure reason: " + ev)
def test_dpp_hostapd_auth_resp_retries(dev, apdev):
"""DPP Authentication Response retries in hostapd"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
hapd.set("dpp_resp_max_tries", "3")
hapd.set("dpp_resp_retry_time", "100")
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
id0b = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0b = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0b)
hapd.dpp_listen(2412, qr="mutual")
dev[0].dpp_auth_init(uri=uri_h, own=id0b)
ev = dev[0].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
ev = hapd.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
# Stop Initiator from listening to frames to force retransmission of the
# DPP Authentication Response frame with Status=0
dev[0].request("DPP_STOP_LISTEN")
hapd.dump_monitor()
dev[0].dump_monitor()
id0b = hapd.dpp_qr_code(uri0b)
ev = hapd.wait_event(["DPP-TX "], timeout=5)
if ev is None or "type=1" not in ev:
raise Exception("DPP Authentication Response not sent")
ev = hapd.wait_event(["DPP-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("TX status for DPP Authentication Response not reported")
if "result=FAILED" not in ev:
raise Exception("Unexpected TX status for Authentication Response: " + ev)
ev = hapd.wait_event(["DPP-TX "], timeout=15)
if ev is None or "type=1" not in ev:
raise Exception("DPP Authentication Response retransmission not sent")
def test_dpp_qr_code_no_chan_list_unicast(dev, apdev):
"""DPP QR Code and no channel list (unicast)"""
run_dpp_qr_code_chan_list(dev, apdev, True, 2417, None)
def test_dpp_qr_code_chan_list_unicast(dev, apdev):
"""DPP QR Code and 2.4 GHz channels (unicast)"""
run_dpp_qr_code_chan_list(dev, apdev, True, 2417,
"81/1,81/2,81/3,81/4,81/5,81/6,81/7,81/8,81/9,81/10,81/11,81/12,81/13")
def test_dpp_qr_code_chan_list_unicast2(dev, apdev):
"""DPP QR Code and 2.4 GHz channels (unicast 2)"""
run_dpp_qr_code_chan_list(dev, apdev, True, 2417,
"81/1,2,3,4,5,6,7,8,9,10,11,12,13")
def test_dpp_qr_code_chan_list_no_peer_unicast(dev, apdev):
"""DPP QR Code and channel list and no peer (unicast)"""
run_dpp_qr_code_chan_list(dev, apdev, True, 2417, "81/1,81/6,81/11",
no_wait=True)
ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
if ev is None:
raise Exception("Initiation failure not reported")
def test_dpp_qr_code_no_chan_list_broadcast(dev, apdev):
"""DPP QR Code and no channel list (broadcast)"""
run_dpp_qr_code_chan_list(dev, apdev, False, 2412, None)
def test_dpp_qr_code_chan_list_broadcast(dev, apdev):
"""DPP QR Code and some 2.4 GHz channels (broadcast)"""
run_dpp_qr_code_chan_list(dev, apdev, False, 2412, "81/1,81/6,81/11",
timeout=10)
def run_dpp_qr_code_chan_list(dev, apdev, unicast, listen_freq, chanlist,
no_wait=False, timeout=5):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[1].set("dpp_init_max_tries", "3")
dev[1].set("dpp_init_retry_time", "100")
dev[1].set("dpp_resp_wait_time", "1000")
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan=chanlist, mac=unicast)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(listen_freq)
dev[1].dpp_auth_init(uri=uri0)
if no_wait:
return
wait_auth_success(dev[0], dev[1], timeout=timeout, configurator=dev[1],
enrollee=dev[0], allow_enrollee_failure=True,
stop_responder=True)
def test_dpp_qr_code_chan_list_no_match(dev, apdev):
"""DPP QR Code and no matching supported channel"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="123/123")
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[1].dpp_auth_init(uri=uri0, expect_fail=True)
def test_dpp_pkex_alloc_fail(dev, apdev):
"""DPP/PKEX and memory allocation failures"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
tests = [(1, "=dpp_keygen_configurator"),
(1, "base64_gen_encode;dpp_keygen_configurator")]
for count, func in tests:
with alloc_fail(dev[1], count, func):
cmd = "DPP_CONFIGURATOR_ADD"
res = dev[1].request(cmd)
if "FAIL" not in res:
raise Exception("Unexpected DPP_CONFIGURATOR_ADD success")
conf_id = dev[1].dpp_configurator_add()
id0 = None
id1 = None
# Local error cases on the Initiator
tests = [(1, "dpp_get_pubkey_point"),
(1, "dpp_alloc_msg;dpp_pkex_build_exchange_req"),
(1, "dpp_alloc_msg;dpp_pkex_build_commit_reveal_req"),
(1, "dpp_alloc_msg;dpp_auth_build_req"),
(1, "dpp_alloc_msg;dpp_auth_build_conf"),
(1, "dpp_bootstrap_key_hash"),
(1, "dpp_auth_init"),
(1, "dpp_alloc_auth"),
(1, "=dpp_auth_resp_rx"),
(1, "dpp_build_conf_start"),
(1, "dpp_build_conf_obj_dpp"),
(2, "dpp_build_conf_obj_dpp"),
(3, "dpp_build_conf_obj_dpp"),
(4, "dpp_build_conf_obj_dpp"),
(5, "dpp_build_conf_obj_dpp"),
(6, "dpp_build_conf_obj_dpp"),
(7, "dpp_build_conf_obj_dpp"),
(8, "dpp_build_conf_obj_dpp"),
(1, "dpp_conf_req_rx"),
(2, "dpp_conf_req_rx"),
(3, "dpp_conf_req_rx"),
(4, "dpp_conf_req_rx"),
(5, "dpp_conf_req_rx"),
(6, "dpp_conf_req_rx"),
(7, "dpp_conf_req_rx"),
(1, "dpp_pkex_init"),
(2, "dpp_pkex_init"),
(3, "dpp_pkex_init"),
(1, "dpp_pkex_derive_z"),
(1, "=dpp_pkex_rx_commit_reveal_resp"),
(1, "dpp_get_pubkey_point;dpp_build_jwk"),
(2, "dpp_get_pubkey_point;dpp_build_jwk"),
(1, "dpp_get_pubkey_point;dpp_auth_init")]
for count, func in tests:
dev[0].request("DPP_STOP_LISTEN")
dev[1].request("DPP_STOP_LISTEN")
dev[0].dump_monitor()
dev[1].dump_monitor()
id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
use_id=id0)
with alloc_fail(dev[1], count, func):
id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
use_id=id1,
extra="conf=sta-dpp configurator=%d" % conf_id,
allow_fail=True)
wait_fail_trigger(dev[1], "GET_ALLOC_FAIL", max_iter=100)
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
if ev:
dev[0].request("DPP_STOP_LISTEN")
dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
# Local error cases on the Responder
tests = [(1, "dpp_get_pubkey_point"),
(1, "dpp_alloc_msg;dpp_pkex_build_exchange_resp"),
(1, "dpp_alloc_msg;dpp_pkex_build_commit_reveal_resp"),
(1, "dpp_alloc_msg;dpp_auth_build_resp"),
(1, "dpp_get_pubkey_point;dpp_auth_build_resp_ok"),
(1, "dpp_alloc_auth"),
(1, "=dpp_auth_req_rx"),
(1, "=dpp_auth_conf_rx"),
(1, "json_parse;dpp_parse_jws_prot_hdr"),
(1, "json_get_member_base64url;dpp_parse_jws_prot_hdr"),
(1, "json_get_member_base64url;dpp_parse_jwk"),
(2, "json_get_member_base64url;dpp_parse_jwk"),
(1, "json_parse;dpp_parse_connector"),
(1, "dpp_parse_jwk;dpp_parse_connector"),
(1, "dpp_parse_jwk;dpp_parse_cred_dpp"),
(1, "dpp_get_pubkey_point;dpp_check_pubkey_match"),
(1, "base64_gen_decode;dpp_process_signed_connector"),
(1, "dpp_parse_jws_prot_hdr;dpp_process_signed_connector"),
(2, "base64_gen_decode;dpp_process_signed_connector"),
(3, "base64_gen_decode;dpp_process_signed_connector"),
(4, "base64_gen_decode;dpp_process_signed_connector"),
(1, "json_parse;dpp_parse_conf_obj"),
(1, "dpp_conf_resp_rx"),
(1, "=dpp_pkex_derive_z"),
(1, "=dpp_pkex_rx_exchange_req"),
(2, "=dpp_pkex_rx_exchange_req"),
(3, "=dpp_pkex_rx_exchange_req"),
(1, "=dpp_pkex_rx_commit_reveal_req"),
(1, "dpp_get_pubkey_point;dpp_pkex_rx_commit_reveal_req"),
(1, "dpp_bootstrap_key_hash")]
for count, func in tests:
dev[0].request("DPP_STOP_LISTEN")
dev[1].request("DPP_STOP_LISTEN")
dev[0].dump_monitor()
dev[1].dump_monitor()
id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
use_id=id0)
with alloc_fail(dev[0], count, func):
id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
use_id=id1,
extra="conf=sta-dpp configurator=%d" % conf_id)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL", max_iter=100)
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
if ev:
dev[0].request("DPP_STOP_LISTEN")
dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
def test_dpp_pkex_test_fail(dev, apdev):
"""DPP/PKEX and local failures"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
tests = [(1, "dpp_keygen_configurator")]
for count, func in tests:
with fail_test(dev[1], count, func):
cmd = "DPP_CONFIGURATOR_ADD"
res = dev[1].request(cmd)
if "FAIL" not in res:
raise Exception("Unexpected DPP_CONFIGURATOR_ADD success")
tests = [(1, "dpp_keygen")]
for count, func in tests:
with fail_test(dev[1], count, func):
cmd = "DPP_BOOTSTRAP_GEN type=pkex"
res = dev[1].request(cmd)
if "FAIL" not in res:
raise Exception("Unexpected DPP_BOOTSTRAP_GEN success")
conf_id = dev[1].dpp_configurator_add()
id0 = None
id1 = None
# Local error cases on the Initiator
tests = [(1, "aes_siv_encrypt;dpp_auth_build_req"),
(1, "os_get_random;dpp_auth_init"),
(1, "dpp_derive_k1;dpp_auth_init"),
(1, "dpp_hkdf_expand;dpp_derive_k1;dpp_auth_init"),
(1, "dpp_gen_i_auth;dpp_auth_build_conf"),
(1, "aes_siv_encrypt;dpp_auth_build_conf"),
(1, "dpp_derive_k2;dpp_auth_resp_rx"),
(1, "dpp_hkdf_expand;dpp_derive_k2;dpp_auth_resp_rx"),
(1, "dpp_derive_bk_ke;dpp_auth_resp_rx"),
(1, "dpp_hkdf_expand;dpp_derive_bk_ke;dpp_auth_resp_rx"),
(1, "dpp_gen_r_auth;dpp_auth_resp_rx"),
(1, "aes_siv_encrypt;dpp_build_conf_resp"),
(1, "dpp_pkex_derive_Qi;dpp_pkex_build_exchange_req"),
(1, "aes_siv_encrypt;dpp_pkex_build_commit_reveal_req"),
(1, "hmac_sha256_vector;dpp_pkex_rx_exchange_resp"),
(1, "aes_siv_decrypt;dpp_pkex_rx_commit_reveal_resp"),
(1, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_resp"),
(1, "dpp_bootstrap_key_hash")]
for count, func in tests:
dev[0].request("DPP_STOP_LISTEN")
dev[1].request("DPP_STOP_LISTEN")
dev[0].dump_monitor()
dev[1].dump_monitor()
id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
use_id=id0)
with fail_test(dev[1], count, func):
id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
use_id=id1,
extra="conf=sta-dpp configurator=%d" % conf_id,
allow_fail=True)
wait_fail_trigger(dev[1], "GET_FAIL", max_iter=100)
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
if ev:
dev[0].request("DPP_STOP_LISTEN")
dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
# Local error cases on the Responder
tests = [(1, "aes_siv_encrypt;dpp_auth_build_resp"),
(1, "aes_siv_encrypt;dpp_auth_build_resp;dpp_auth_build_resp_ok"),
(1, "os_get_random;dpp_build_conf_req"),
(1, "aes_siv_encrypt;dpp_build_conf_req"),
(1, "os_get_random;dpp_auth_build_resp_ok"),
(1, "dpp_derive_k2;dpp_auth_build_resp_ok"),
(1, "dpp_derive_bk_ke;dpp_auth_build_resp_ok"),
(1, "dpp_gen_r_auth;dpp_auth_build_resp_ok"),
(1, "aes_siv_encrypt;dpp_auth_build_resp_ok"),
(1, "dpp_derive_k1;dpp_auth_req_rx"),
(1, "aes_siv_decrypt;dpp_auth_req_rx"),
(1, "aes_siv_decrypt;dpp_auth_conf_rx"),
(1, "dpp_gen_i_auth;dpp_auth_conf_rx"),
(1, "dpp_check_pubkey_match"),
(1, "aes_siv_decrypt;dpp_conf_resp_rx"),
(1, "hmac_sha256_kdf;dpp_pkex_derive_z"),
(1, "dpp_pkex_derive_Qi;dpp_pkex_rx_exchange_req"),
(1, "dpp_pkex_derive_Qr;dpp_pkex_rx_exchange_req"),
(1, "aes_siv_encrypt;dpp_pkex_build_commit_reveal_resp"),
(1, "aes_siv_decrypt;dpp_pkex_rx_commit_reveal_req"),
(1, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_req"),
(2, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_req")]
for count, func in tests:
dev[0].request("DPP_STOP_LISTEN")
dev[1].request("DPP_STOP_LISTEN")
dev[0].dump_monitor()
dev[1].dump_monitor()
id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
use_id=id0)
with fail_test(dev[0], count, func):
id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
use_id=id1,
extra="conf=sta-dpp configurator=%d" % conf_id)
wait_fail_trigger(dev[0], "GET_FAIL", max_iter=100)
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
if ev:
dev[0].request("DPP_STOP_LISTEN")
dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
def test_dpp_keygen_configurator_error(dev, apdev):
"""DPP Configurator keygen error case"""
check_dpp_capab(dev[0])
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD curve=unknown"):
raise Exception("Unexpected success of invalid DPP_CONFIGURATOR_ADD")
def rx_process_frame(dev):
msg = dev.mgmt_rx()
if msg is None:
raise Exception("No management frame RX reported")
if "OK" not in dev.request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
raise Exception("MGMT_RX_PROCESS failed")
return msg
def wait_auth_success(responder, initiator, configurator=None, enrollee=None,
allow_enrollee_failure=False,
allow_configurator_failure=False,
require_configurator_failure=False,
timeout=5, stop_responder=False, stop_initiator=False):
res = {}
ev = responder.wait_event(["DPP-AUTH-SUCCESS", "DPP-FAIL"], timeout=timeout)
if ev is None or "DPP-AUTH-SUCCESS" not in ev:
raise Exception("DPP authentication did not succeed (Responder)")
ev = initiator.wait_event(["DPP-AUTH-SUCCESS", "DPP-FAIL"], timeout=5)
if ev is None or "DPP-AUTH-SUCCESS" not in ev:
raise Exception("DPP authentication did not succeed (Initiator)")
if configurator:
ev = configurator.wait_event(["DPP-CONF-SENT",
"DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Configurator)")
if "DPP-CONF-FAILED" in ev and not allow_configurator_failure:
raise Exception("DPP configuration did not succeed (Configurator)")
if "DPP-CONF-SENT" in ev and require_configurator_failure:
raise Exception("DPP configuration succeeded (Configurator)")
if "DPP-CONF-SENT" in ev and "wait_conn_status=1" in ev:
res['wait_conn_status'] = True
if enrollee:
ev = enrollee.wait_event(["DPP-CONF-RECEIVED",
"DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
if "DPP-CONF-FAILED" in ev and not allow_enrollee_failure:
raise Exception("DPP configuration did not succeed (Enrollee)")
if stop_responder:
responder.request("DPP_STOP_LISTEN")
if stop_initiator:
initiator.request("DPP_STOP_LISTEN")
return res
def wait_conf_completion(configurator, enrollee):
ev = configurator.wait_event(["DPP-CONF-SENT"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Configurator)")
ev = enrollee.wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
def start_dpp(dev):
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
dev[0].set("dpp_config_obj_override", conf)
dev[0].set("ext_mgmt_frame_handling", "1")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
def test_dpp_gas_timeout_handling(dev, apdev):
"""DPP and GAS timeout handling"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
start_dpp(dev)
# DPP Authentication Request
rx_process_frame(dev[0])
# DPP Authentication Confirmation
rx_process_frame(dev[0])
wait_auth_success(dev[0], dev[1])
# DPP Configuration Request (GAS Initial Request frame)
rx_process_frame(dev[0])
# DPP Configuration Request (GAS Comeback Request frame)
rx_process_frame(dev[0])
# Wait for GAS timeout
ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
def test_dpp_gas_comeback_after_failure(dev, apdev):
"""DPP and GAS comeback after failure"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
start_dpp(dev)
# DPP Authentication Request
rx_process_frame(dev[0])
# DPP Authentication Confirmation
rx_process_frame(dev[0])
wait_auth_success(dev[0], dev[1])
# DPP Configuration Request (GAS Initial Request frame)
rx_process_frame(dev[0])
# DPP Configuration Request (GAS Comeback Request frame)
msg = dev[0].mgmt_rx()
frame = binascii.hexlify(msg['frame']).decode()
with alloc_fail(dev[0], 1, "gas_build_comeback_resp;gas_server_handle_rx_comeback_req"):
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
# Try the same frame again - this is expected to fail since the response has
# already been freed.
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
# DPP Configuration Request (GAS Comeback Request frame retry)
msg = dev[0].mgmt_rx()
def test_dpp_gas(dev, apdev):
"""DPP and GAS protocol testing"""
ver0 = check_dpp_capab(dev[0])
ver1 = check_dpp_capab(dev[1])
start_dpp(dev)
# DPP Authentication Request
rx_process_frame(dev[0])
# DPP Authentication Confirmation
rx_process_frame(dev[0])
wait_auth_success(dev[0], dev[1])
# DPP Configuration Request (GAS Initial Request frame)
msg = dev[0].mgmt_rx()
# Protected Dual of GAS Initial Request frame (dropped by GAS server)
if msg == None:
raise Exception("MGMT_RX_PROCESS failed. <Please retry>")
frame = binascii.hexlify(msg['frame'])
frame = frame[0:48] + b"09" + frame[50:]
frame = frame.decode()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
with alloc_fail(dev[0], 1, "gas_server_send_resp"):
frame = binascii.hexlify(msg['frame']).decode()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
with alloc_fail(dev[0], 1, "gas_build_initial_resp;gas_server_send_resp"):
frame = binascii.hexlify(msg['frame']).decode()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
# Add extra data after Query Request field to trigger
# "GAS: Ignored extra data after Query Request field"
frame = binascii.hexlify(msg['frame']).decode() + "00"
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
# DPP Configuration Request (GAS Comeback Request frame)
rx_process_frame(dev[0])
# DPP Configuration Request (GAS Comeback Request frame)
rx_process_frame(dev[0])
# DPP Configuration Request (GAS Comeback Request frame)
rx_process_frame(dev[0])
if ver0 >= 2 and ver1 >= 2:
# DPP Configuration Result
rx_process_frame(dev[0])
wait_conf_completion(dev[0], dev[1])
def test_dpp_truncated_attr(dev, apdev):
"""DPP and truncated attribute"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
start_dpp(dev)
# DPP Authentication Request
msg = dev[0].mgmt_rx()
frame = msg['frame']
# DPP: Truncated message - not enough room for the attribute - dropped
frame1 = binascii.hexlify(frame[0:36]).decode()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame1)):
raise Exception("MGMT_RX_PROCESS failed")
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "ignore=invalid-attributes" not in ev:
raise Exception("Invalid attribute error not reported")
# DPP: Unexpected octets (3) after the last attribute
frame2 = binascii.hexlify(frame).decode() + "000000"
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
raise Exception("MGMT_RX_PROCESS failed")
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "ignore=invalid-attributes" not in ev:
raise Exception("Invalid attribute error not reported")
def test_dpp_bootstrap_key_autogen_issues(dev, apdev):
"""DPP bootstrap key autogen issues"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code")
id1 = dev[1].dpp_qr_code(uri0)
logger.info("dev1 initiates DPP Authentication")
dev[0].dpp_listen(2412)
with alloc_fail(dev[1], 1, "dpp_autogen_bootstrap_key"):
dev[1].dpp_auth_init(peer=id1, expect_fail=True)
with alloc_fail(dev[1], 1, "dpp_gen_uri;dpp_autogen_bootstrap_key"):
dev[1].dpp_auth_init(peer=id1, expect_fail=True)
with fail_test(dev[1], 1, "dpp_keygen;dpp_autogen_bootstrap_key"):
dev[1].dpp_auth_init(peer=id1, expect_fail=True)
dev[0].request("DPP_STOP_LISTEN")
def test_dpp_auth_resp_status_failure(dev, apdev):
"""DPP and Auth Resp(status) build failure"""
with alloc_fail(dev[0], 1, "dpp_auth_build_resp"):
run_dpp_proto_auth_resp_missing(dev, 99999, None,
incompatible_roles=True)
def test_dpp_auth_resp_aes_siv_issue(dev, apdev):
"""DPP Auth Resp AES-SIV issue"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
logger.info("dev0 displays QR Code")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("dev1 scans QR Code and initiates DPP Authentication")
dev[0].dpp_listen(2412)
with fail_test(dev[1], 1, "aes_siv_decrypt;dpp_auth_resp_rx"):
dev[1].dpp_auth_init(uri=uri0)
wait_dpp_fail(dev[1], "AES-SIV decryption failed")
dev[0].request("DPP_STOP_LISTEN")
def test_dpp_invalid_legacy_params(dev, apdev):
"""DPP invalid legacy parameters"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
# No pass/psk
dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", ssid="dpp-legacy",
expect_fail=True)
def test_dpp_invalid_legacy_params2(dev, apdev):
"""DPP invalid legacy parameters 2"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("dpp_configurator_params",
" conf=sta-psk ssid=%s" % (binascii.hexlify(b"dpp-legacy").decode()))
dev[0].dpp_listen(2412, role="configurator")
dev[1].dpp_auth_init(uri=uri0, role="enrollee")
# No pass/psk
ev = dev[0].wait_event(["DPP: Failed to set configurator parameters"],
timeout=5)
if ev is None:
raise Exception("DPP configuration failure not reported")
def test_dpp_legacy_params_failure(dev, apdev):
"""DPP legacy parameters local failure"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
with alloc_fail(dev[1], 1, "dpp_build_conf_obj_legacy"):
dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", passphrase="passphrase",
ssid="dpp-legacy")
ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration failure not reported")
def test_dpp_invalid_configurator_key(dev, apdev):
"""DPP invalid configurator key"""
check_dpp_capab(dev[0])
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=aa"):
raise Exception("Invalid key accepted")
with alloc_fail(dev[0], 1, "dpp_keygen_configurator"):
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
raise Exception("Error not reported")
with alloc_fail(dev[0], 1, "dpp_get_pubkey_point;dpp_keygen_configurator"):
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
raise Exception("Error not reported")
with alloc_fail(dev[0], 1, "base64_gen_encode;dpp_keygen_configurator"):
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
raise Exception("Error not reported")
with fail_test(dev[0], 1, "dpp_keygen_configurator"):
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
raise Exception("Error not reported")
def test_dpp_own_config_sign_fail(dev, apdev):
"""DPP own config signing failure"""
check_dpp_capab(dev[0])
conf_id = dev[0].dpp_configurator_add()
tests = ["",
" ",
" conf=sta-dpp",
" configurator=%d" % conf_id,
" conf=sta-dpp configurator=%d curve=unsupported" % conf_id]
for t in tests:
if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_SIGN " + t):
raise Exception("Invalid command accepted: " + t)
def test_dpp_peer_intro_failures(dev, apdev):
"""DPP peer introduction failures"""
try:
run_dpp_peer_intro_failures(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_peer_intro_failures(dev, apdev):
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add(key=dpp_key_p256)
csign = hapd.request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
if "FAIL" in csign or len(csign) == 0:
raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
conf_id2 = dev[0].dpp_configurator_add(key=csign)
csign2 = dev[0].request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id2)
if csign != csign2:
raise Exception("Unexpected difference in configurator key")
cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d" % conf_id
res = hapd.request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate own configuration")
update_hapd_config(hapd)
dev[0].set("dpp_config_processing", "1")
cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % conf_id
res = dev[0].request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate own configuration")
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
tests = ["eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOltdLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJiVmFMRGlBT09OQmFjcVFVN1pYamFBVEtEMVhhbDVlUExqOUZFZUl3VkN3IiwieSI6Il95c25JR1hTYjBvNEsyMWg0anZmSkZxMHdVNnlPNWp1VUFPd3FuM0dHVHMifX0.WgzZBOJaisWBRxvtXPbVYPXU7OIZxs6sZD-cPOLmJVTIYZKdMkSOMvP5b6si_j61FIrjhm43tmGq1P6cpoxB_g",
"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7fV0sIm5ldEFjY2Vzc0tleSI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IkJhY3BWSDNpNDBrZklNS0RHa1FFRzhCODBCaEk4cEFmTWpLbzM5NlFZT2ciLCJ5IjoiMjBDYjhDNjRsSjFzQzV2NXlKMnBFZXRRempxMjI4YVV2cHMxNmQ0M3EwQSJ9fQ.dG2y8VvZQJ5hfob8E5F2FAeR7Nd700qstYkxDgA2QfARaNMZ0_SfKfoG-yKXsIZNM-TvGBfACgfhagG9Oaw_Xw",
"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJkc2VmcmJWWlhad0RMWHRpLWlObDBBYkFIOXpqeFFKd0R1SUd5NzNuZGU0IiwieSI6IjZFQnExN3cwYW1fZlh1OUQ4UGxWYk9XZ2I3b19DcTUxWHlmSG8wcHJyeDQifX0.caBvdDUtXrhnS61-juVZ_2FQdprepv0yZjC04G4ERvLUpeX7cgu0Hp-A1aFDogP1PEFGpkaEdcAWRQnSSRiIKQ"]
for t in tests:
dev[0].set_network_quoted(id, "dpp_connector", t)
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["DPP-INTRO"], timeout=5)
if ev is None or "status=8" not in ev:
raise Exception("Introduction failure not reported")
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
def test_dpp_peer_intro_local_failures(dev, apdev):
"""DPP peer introduction local failures"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
tests = ["dpp_derive_pmk",
"dpp_hkdf_expand;dpp_derive_pmk",
"dpp_derive_pmkid"]
for func in tests:
with fail_test(dev[0], 1, func):
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
if ev is None or "fail=peer_connector_validation_failed" not in ev:
raise Exception("Introduction failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [(1, "base64_gen_decode;dpp_peer_intro"),
(1, "json_parse;dpp_peer_intro"),
(50, "json_parse;dpp_peer_intro"),
(1, "=dpp_check_signed_connector;dpp_peer_intro"),
(1, "dpp_parse_jwk")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
if ev is None or "fail=peer_connector_validation_failed" not in ev:
raise Exception("Introduction failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
parts = params1_ap_connector.split('.')
for ap_connector in ['.'.join(parts[0:2]), '.'.join(parts[0:1])]:
hapd.set("dpp_connector", ap_connector)
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
if ev is None:
raise Exception("No TX status reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
hapd.set("dpp_netaccesskey", "00")
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
if ev is None:
raise Exception("No TX status reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
hapd.set("dpp_csign", "00")
dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
ieee80211w="2",
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
wait_connect=False)
ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
if ev is None:
raise Exception("No TX status reported")
dev[0].request("REMOVE_NETWORK all")
def run_dpp_configurator_id_unknown(dev):
check_dpp_capab(dev)
conf_id = dev.dpp_configurator_add()
if "FAIL" not in dev.request("DPP_CONFIGURATOR_GET_KEY %d" % (conf_id + 1)):
raise Exception("DPP_CONFIGURATOR_GET_KEY with incorrect id accepted")
cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % (conf_id + 1)
if "FAIL" not in dev.request(cmd):
raise Exception("DPP_CONFIGURATOR_SIGN with incorrect id accepted")
def test_dpp_configurator_id_unknown(dev, apdev):
"""DPP and unknown configurator id"""
run_dpp_configurator_id_unknown(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
run_dpp_configurator_id_unknown(hapd)
def run_dpp_bootstrap_gen_failures(dev):
check_dpp_capab(dev)
tests = ["type=unsupported",
"type=qrcode chan=-1",
"type=qrcode mac=a",
"type=qrcode key=qq",
"type=qrcode key=",
"type=qrcode info=abc\tdef"]
for t in tests:
if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN " + t):
raise Exception("Command accepted unexpectedly")
id = dev.dpp_bootstrap_gen()
uri = dev.request("DPP_BOOTSTRAP_GET_URI %d" % id)
if not uri.startswith("DPP:"):
raise Exception("Could not get URI")
if "FAIL" not in dev.request("DPP_BOOTSTRAP_GET_URI 0"):
raise Exception("Failure not reported")
info = dev.request("DPP_BOOTSTRAP_INFO %d" % id)
if not info.startswith("type=QRCODE"):
raise Exception("Could not get info")
if "FAIL" not in dev.request("DPP_BOOTSTRAP_REMOVE 0"):
raise Exception("Failure not reported")
if "FAIL" in dev.request("DPP_BOOTSTRAP_REMOVE *"):
raise Exception("Failed to remove bootstrap info")
if "FAIL" not in dev.request("DPP_BOOTSTRAP_GET_URI %d" % id):
raise Exception("Failure not reported")
if "FAIL" not in dev.request("DPP_BOOTSTRAP_INFO %d" % id):
raise Exception("Failure not reported")
func = "dpp_bootstrap_gen"
with alloc_fail(dev, 1, "=" + func):
if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN type=qrcode"):
raise Exception("Command accepted unexpectedly")
with alloc_fail(dev, 1, "dpp_gen_uri;dpp_bootstrap_gen"):
if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN type=qrcode"):
raise Exception("Command accepted unexpectedly")
with alloc_fail(dev, 1, "get_param"):
dev.request("DPP_BOOTSTRAP_GEN type=qrcode curve=foo")
def test_dpp_bootstrap_gen_failures(dev, apdev):
"""DPP_BOOTSTRAP_GEN/REMOVE/GET_URI/INFO error cases"""
run_dpp_bootstrap_gen_failures(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
run_dpp_bootstrap_gen_failures(hapd)
def test_dpp_listen_continue(dev, apdev):
"""DPP and continue listen state"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
dev[0].dpp_listen(2412)
time.sleep(5.1)
dev[1].dpp_auth_init(uri=uri)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True, stop_responder=True,
stop_initiator=True)
def test_dpp_network_addition_failure(dev, apdev):
"""DPP network addition failure"""
try:
run_dpp_network_addition_failure(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_network_addition_failure(dev, apdev):
check_dpp_capab(dev[0])
conf_id = dev[0].dpp_configurator_add()
dev[0].set("dpp_config_processing", "1")
cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % conf_id
tests = [(1, "=wpas_dpp_add_network"),
(2, "=wpas_dpp_add_network"),
(3, "=wpas_dpp_add_network"),
(4, "=wpas_dpp_add_network"),
(1, "wpa_config_add_network;wpas_dpp_add_network")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
res = dev[0].request(cmd)
if "OK" in res:
ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=2)
if ev is None:
raise Exception("Config object not processed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].dump_monitor()
cmd = "DPP_CONFIGURATOR_SIGN conf=sta-psk pass=%s configurator=%d" % (binascii.hexlify(b"passphrase").decode(), conf_id)
tests = [(1, "wpa_config_set_quoted;wpas_dpp_add_network")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
res = dev[0].request(cmd)
if "OK" in res:
ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=2)
if ev is None:
raise Exception("Config object not processed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].dump_monitor()
def test_dpp_two_initiators(dev, apdev):
"""DPP and two initiators"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
check_dpp_capab(dev[2])
id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exeption("No DPP Authentication Request seen")
dev[2].dpp_auth_init(uri=uri)
wait_dpp_fail(dev[0],
"DPP-FAIL Already in DPP authentication exchange - ignore new one")
ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=2)
if ev is None:
raise Exception("DPP configuration result not seen (Enrollee)")
ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=2)
if ev is None:
raise Exception("DPP configuration result not seen (Responder)")
dev[0].request("DPP_STOP_LISTEN")
dev[1].request("DPP_STOP_LISTEN")
dev[2].request("DPP_STOP_LISTEN")
def test_dpp_conf_file_update(dev, apdev, params):
"""DPP provisioning updating wpa_supplicant configuration file"""
config = os.path.join(params['logdir'], 'dpp_conf_file_update.conf')
with open(config, "w") as f:
f.write("update_config=1\n")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", config=config)
check_dpp_capab(wpas)
wpas.set("dpp_config_processing", "1")
run_dpp_qr_code_auth_unicast([wpas, dev[1]], apdev, None,
init_extra="conf=sta-dpp",
require_conf_success=True,
configurator=True)
wpas.interface_remove("wlan5")
with open(config, "r") as f:
res = f.read()
for i in ["network={", "dpp_connector=", "key_mgmt=DPP", "ieee80211w=2",
"dpp_netaccesskey=", "dpp_csign="]:
if i not in res:
raise Exception("Configuration file missing '%s'" % i)
wpas.interface_add("wlan5", config=config)
if len(wpas.list_networks()) != 1:
raise Exception("Unexpected number of networks")
def test_dpp_duplicated_auth_resp(dev, apdev):
"""DPP and duplicated Authentication Response"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("ext_mgmt_frame_handling", "1")
dev[1].set("ext_mgmt_frame_handling", "1")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0)
# DPP Authentication Request
rx_process_frame(dev[0])
# DPP Authentication Response
msg = rx_process_frame(dev[1])
frame = binascii.hexlify(msg['frame']).decode()
# Duplicated frame
if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
raise Exception("MGMT_RX_PROCESS failed")
# Modified frame - nonzero status
if frame[2*32:2*37] != "0010010000":
raise Exception("Could not find Status attribute")
frame2 = frame[0:2*32] + "0010010001" + frame[2*37:]
if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
raise Exception("MGMT_RX_PROCESS failed")
frame2 = frame[0:2*32] + "00100100ff" + frame[2*37:]
if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
raise Exception("MGMT_RX_PROCESS failed")
# DPP Authentication Confirmation
rx_process_frame(dev[0])
wait_auth_success(dev[0], dev[1])
# DPP Configuration Request
rx_process_frame(dev[1])
# DPP Configuration Response
rx_process_frame(dev[0])
wait_conf_completion(dev[1], dev[0])
def test_dpp_duplicated_auth_conf(dev, apdev):
"""DPP and duplicated Authentication Confirmation"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].set("ext_mgmt_frame_handling", "1")
dev[1].set("ext_mgmt_frame_handling", "1")
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0)
# DPP Authentication Request
rx_process_frame(dev[0])
# DPP Authentication Response
rx_process_frame(dev[1])
# DPP Authentication Confirmation
msg = rx_process_frame(dev[0])
# Duplicated frame
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
raise Exception("MGMT_RX_PROCESS failed")
wait_auth_success(dev[0], dev[1])
# DPP Configuration Request
rx_process_frame(dev[1])
# DPP Configuration Response
rx_process_frame(dev[0])
wait_conf_completion(dev[1], dev[0])
def test_dpp_enrollee_reject_config(dev, apdev):
"""DPP and Enrollee rejecting Config Object"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
dev[0].set("dpp_test", "91")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf="sta-sae", ssid="dpp-legacy",
passphrase="secret passphrase")
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True,
allow_configurator_failure=True)
def test_dpp_enrollee_ap_reject_config(dev, apdev):
"""DPP and Enrollee AP rejecting Config Object"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
hapd.set("dpp_test", "91")
conf_id = dev[0].dpp_configurator_add()
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
allow_enrollee_failure=True,
allow_configurator_failure=True)
def test_dpp_legacy_and_dpp_akm(dev, apdev):
"""DPP and provisoning DPP and legacy AKMs"""
try:
run_dpp_legacy_and_dpp_akm(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_legacy_and_dpp_akm(dev, apdev):
check_dpp_capab(dev[0], min_ver=2)
check_dpp_capab(dev[1], min_ver=2)
csign = "30770201010420768240a3fc89d6662d9782f120527fe7fb9edc6366ab0b9c7dde96125cfd250fa00a06082a8648ce3d030107a144034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
csign_pub = "3059301306072a8648ce3d020106082a8648ce3d030107034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJwYWtZbXVzd1dCdWpSYTl5OEsweDViaTVrT3VNT3dzZHRlaml2UG55ZHZzIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIybU5vNXZuRkI5bEw3d1VWb1hJbGVPYzBNSEE1QXZKbnpwZXZULVVTYzVNIiwieSI6IlhzS3dqVHJlLTg5WWdpU3pKaG9CN1haeUttTU05OTl3V2ZaSVl0bi01Q3MifX0.XhjFpZgcSa7G2lHy0OCYTvaZFRo5Hyx6b7g7oYyusLC7C_73AJ4_BxEZQVYJXAtDuGvb3dXSkHEKxREP9Q6Qeg"
ap_netaccesskey = "30770201010420ceba752db2ad5200fa7bc565b9c05c69b7eb006751b0b329b0279de1c19ca67ca00a06082a8648ce3d030107a14403420004da6368e6f9c507d94bef0515a1722578e73430703902f267ce97af4fe51273935ec2b08d3adefbcf588224b3261a01ed76722a630cf7df7059f64862d9fee42b"
ssid = "dpp-both"
passphrase = "secret passphrase"
params = {"ssid": ssid,
"wpa": "2",
"wpa_key_mgmt": "DPP WPA-PSK SAE",
"ieee80211w": "1",
"sae_require_mfp": '1',
"rsn_pairwise": "CCMP",
"wpa_passphrase": passphrase,
"dpp_connector": ap_connector,
"dpp_csign": csign_pub,
"dpp_netaccesskey": ap_netaccesskey}
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
dev[0].request("SET sae_groups ")
conf_id = dev[1].dpp_configurator_add(key=csign)
dev[0].set("dpp_config_processing", "1")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
dev[1].dpp_auth_init(uri=uri0, conf="sta-psk-sae-dpp", ssid=ssid,
passphrase=passphrase, configurator=conf_id)
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True,
allow_configurator_failure=True)
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id0 = ev.split(' ')[1]
key_mgmt = dev[0].get_network(id0, "key_mgmt").split(' ')
for m in ["SAE", "WPA-PSK", "DPP"]:
if m not in key_mgmt:
raise Exception("%s missing from key_mgmt" % m)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].select_network(id0, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
params = {"ssid": ssid,
"wpa": "2",
"wpa_key_mgmt": "WPA-PSK SAE",
"ieee80211w": "1",
"sae_require_mfp": '1',
"rsn_pairwise": "CCMP",
"wpa_passphrase": passphrase}
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(hapd2.own_addr(), freq=2412, force_scan=True,
only_new=True)
dev[0].select_network(id0, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_dpp_controller_relay(dev, apdev, params):
"""DPP Controller/Relay"""
try:
run_dpp_controller_relay(dev, apdev, params)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
dev[1].request("DPP_CONTROLLER_STOP")
def test_dpp_controller_relay_chirp(dev, apdev, params):
"""DPP Controller/Relay with chirping"""
try:
run_dpp_controller_relay(dev, apdev, params, chirp=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
dev[1].request("DPP_CONTROLLER_STOP")
def run_dpp_controller_relay(dev, apdev, params, chirp=False):
check_dpp_capab(dev[0], min_ver=2)
check_dpp_capab(dev[1], min_ver=2)
prefix = "dpp_controller_relay"
if chirp:
prefix += "_chirp"
cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
wt = WlantestCapture('lo', cap_lo)
# Controller
conf_id = dev[1].dpp_configurator_add()
dev[1].set("dpp_configurator_params",
"conf=sta-dpp configurator=%d" % conf_id)
id_c = dev[1].dpp_bootstrap_gen()
uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
res = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_c)
pkhash = None
for line in res.splitlines():
name, value = line.split('=')
if name == "pkhash":
pkhash = value
break
if not pkhash:
raise Exception("Could not fetch public key hash from Controller")
if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
raise Exception("Failed to start Controller")
# Relay
params = {"ssid": "unconfigured",
"channel": "6",
"dpp_controller": "ipaddr=127.0.0.1 pkhash=" + pkhash}
if chirp:
params["channel"] = "11"
params["dpp_configurator_connectivity"] = "1"
relay = hostapd.add_ap(apdev[1], params)
check_dpp_capab(relay)
# Enroll Relay to the network
# TODO: Do this over TCP once direct Enrollee-over-TCP case is supported
if chirp:
id_h = relay.dpp_bootstrap_gen(chan="81/11", mac=True)
else:
id_h = relay.dpp_bootstrap_gen(chan="81/6", mac=True)
uri_r = relay.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
dev[1].dpp_auth_init(uri=uri_r, conf="ap-dpp", configurator=conf_id)
wait_auth_success(relay, dev[1], configurator=dev[1], enrollee=relay)
update_hapd_config(relay)
# Initiate from Enrollee with broadcast DPP Authentication Request or
# using chirping
dev[0].set("dpp_config_processing", "2")
if chirp:
id1 = dev[0].dpp_bootstrap_gen()
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
idc = dev[1].dpp_qr_code(uri)
dev[1].dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=5" % id1):
raise Exception("DPP_CHIRP failed")
ev = relay.wait_event(["DPP-RX"], timeout=10)
if ev is None:
raise Exception("Presence Announcement not seen")
if "type=13" not in ev:
raise Exception("Unexpected DPP frame received: " + ev)
else:
dev[0].dpp_auth_init(uri=uri_c, role="enrollee")
wait_auth_success(dev[1], dev[0], configurator=dev[1], enrollee=dev[0],
allow_enrollee_failure=True,
allow_configurator_failure=True)
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network id not reported")
network = int(ev.split(' ')[1])
dev[0].wait_connected()
dev[0].dump_monitor()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
if "OK" not in dev[0].request("DPP_RECONFIG %s" % network):
raise Exception("Failed to start reconfiguration")
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=15)
if ev is None:
raise Exception("DPP network id not reported for reconfiguration")
network2 = int(ev.split(' ')[1])
if network == network2:
raise Exception("Network ID did not change")
dev[0].wait_connected()
time.sleep(0.5)
wt.close()
def test_dpp_tcp(dev, apdev, params):
"""DPP over TCP"""
prefix = "dpp_tcp"
cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
try:
run_dpp_tcp(dev[0], dev[1], cap_lo)
finally:
dev[1].request("DPP_CONTROLLER_STOP")
def test_dpp_tcp_port(dev, apdev, params):
"""DPP over TCP and specified port"""
prefix = "dpp_tcp_port"
cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
try:
run_dpp_tcp(dev[0], dev[1], cap_lo, port="23456")
finally:
dev[1].request("DPP_CONTROLLER_STOP")
def test_dpp_tcp_mutual(dev, apdev, params):
"""DPP over TCP (mutual)"""
cap_lo = os.path.join(params['prefix'], ".lo.pcap")
try:
run_dpp_tcp(dev[0], dev[1], cap_lo, mutual=True)
finally:
dev[1].request("DPP_CONTROLLER_STOP")
def test_dpp_tcp_mutual_hostapd_conf(dev, apdev, params):
"""DPP over TCP (mutual, hostapd as Configurator)"""
cap_lo = os.path.join(params['prefix'], ".lo.pcap")
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
run_dpp_tcp(dev[0], hapd, cap_lo, mutual=True)
def run_dpp_tcp(dev0, dev1, cap_lo, port=None, mutual=False):
check_dpp_capab(dev0)
check_dpp_capab(dev1)
wt = WlantestCapture('lo', cap_lo)
time.sleep(1)
# Controller
conf_id = dev1.dpp_configurator_add()
dev1.set("dpp_configurator_params",
" conf=sta-dpp configurator=%d" % conf_id)
id_c = dev1.dpp_bootstrap_gen()
uri_c = dev1.request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
res = dev1.request("DPP_BOOTSTRAP_INFO %d" % id_c)
pkhash = None
for line in res.splitlines():
name, value = line.split('=')
if name == "pkhash":
pkhash = value
break
if not pkhash:
raise Exception("Could not fetch public key hash from Controller")
req = "DPP_CONTROLLER_START"
if port:
req += " tcp_port=" + port
if mutual:
req += " qr=mutual"
id0 = dev0.dpp_bootstrap_gen()
uri0 = dev0.request("DPP_BOOTSTRAP_GET_URI %d" % id0)
own = id0
else:
own = None
if "OK" not in dev1.request(req):
raise Exception("Failed to start Controller")
# Initiate from Enrollee with broadcast DPP Authentication Request
dev0.dpp_auth_init(uri=uri_c, own=own, role="enrollee",
tcp_addr="127.0.0.1", tcp_port=port)
if mutual:
ev = dev0.wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
if ev is None:
raise Exception("Pending response not reported")
ev = dev1.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
if ev is None:
raise Exception("QR Code scan for mutual authentication not requested")
id1 = dev1.dpp_qr_code(uri0)
ev = dev0.wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
if ev is None:
raise Exception("DPP authentication direction not indicated (Initiator)")
if "mutual=1" not in ev:
raise Exception("Mutual authentication not used")
wait_auth_success(dev1, dev0, configurator=dev1, enrollee=dev0,
allow_enrollee_failure=True,
allow_configurator_failure=True)
time.sleep(0.5)
wt.close()
def test_dpp_tcp_conf_init(dev, apdev, params):
"""DPP over TCP (Configurator initiates)"""
cap_lo = os.path.join(params['prefix'], ".lo.pcap")
try:
run_dpp_tcp_conf_init(dev[0], dev[1], cap_lo)
finally:
dev[1].request("DPP_CONTROLLER_STOP")
def test_dpp_tcp_conf_init_hostapd_enrollee(dev, apdev, params):
"""DPP over TCP (Configurator initiates, hostapd as Enrollee)"""
cap_lo = os.path.join(params['prefix'], ".lo.pcap")
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
run_dpp_tcp_conf_init(dev[0], hapd, cap_lo, conf="ap-dpp")
def run_dpp_tcp_conf_init(dev0, dev1, cap_lo, port=None, conf="sta-dpp"):
check_dpp_capab(dev0, min_ver=2)
check_dpp_capab(dev1, min_ver=2)
wt = WlantestCapture('lo', cap_lo)
time.sleep(1)
id_c = dev1.dpp_bootstrap_gen()
uri_c = dev1.request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
res = dev1.request("DPP_BOOTSTRAP_INFO %d" % id_c)
req = "DPP_CONTROLLER_START role=enrollee"
if port:
req += " tcp_port=" + port
if "OK" not in dev1.request(req):
raise Exception("Failed to start Controller")
conf_id = dev0.dpp_configurator_add()
dev0.dpp_auth_init(uri=uri_c, role="configurator", conf=conf,
configurator=conf_id,
tcp_addr="127.0.0.1", tcp_port=port)
wait_auth_success(dev1, dev0, configurator=dev0, enrollee=dev1,
allow_enrollee_failure=True,
allow_configurator_failure=True)
time.sleep(0.5)
wt.close()
def test_dpp_tcp_controller_management_hostapd(dev, apdev, params):
"""DPP Controller management in hostapd"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
conf_id = hapd.dpp_configurator_add()
if "OK" not in hapd.request("DPP_CONTROLLER_START"):
raise Exception("Failed to start Controller")
if "FAIL" not in hapd.request("DPP_CONTROLLER_START"):
raise Exception("DPP_CONTROLLER_START succeeded while already running Controller")
hapd.request("DPP_CONTROLLER_STOP")
hapd.dpp_configurator_remove(conf_id)
if "FAIL" not in hapd.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id):
raise Exception("Removal of unknown Configurator accepted")
def test_dpp_tcp_controller_start_failure(dev, apdev, params):
"""DPP Controller startup failure"""
check_dpp_capab(dev[0])
try:
if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
raise Exception("Could not start Controller")
if "OK" in dev[0].request("DPP_CONTROLLER_START"):
raise Exception("Second Controller start not rejected")
finally:
dev[0].request("DPP_CONTROLLER_STOP")
tests = ["dpp_controller_start",
"eloop_sock_table_add_sock;?eloop_register_sock;dpp_controller_start"]
for func in tests:
with alloc_fail(dev[0], 1, func):
if "FAIL" not in dev[0].request("DPP_CONTROLLER_START"):
raise Exception("Failure not reported during OOM")
def test_dpp_tcp_init_failure(dev, apdev, params):
"""DPP TCP init failure"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id_c = dev[1].dpp_bootstrap_gen()
uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
peer = dev[0].dpp_qr_code(uri_c)
tests = ["dpp_tcp_init",
"eloop_sock_table_add_sock;?eloop_register_sock;dpp_tcp_init",
"dpp_tcp_encaps"]
cmd = "DPP_AUTH_INIT peer=%d tcp_addr=127.0.0.1" % peer
for func in tests:
with alloc_fail(dev[0], 1, func):
if "FAIL" not in dev[0].request(cmd):
raise Exception("DPP_AUTH_INIT accepted during OOM")
def test_dpp_controller_rx_failure(dev, apdev, params):
"""DPP Controller RX failure"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
try:
run_dpp_controller_rx_failure(dev, apdev)
finally:
dev[0].request("DPP_CONTROLLER_STOP")
def run_dpp_controller_rx_failure(dev, apdev):
if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
raise Exception("Could not start Controller")
id_c = dev[0].dpp_bootstrap_gen()
uri_c = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
peer = dev[1].dpp_qr_code(uri_c)
tests = ["dpp_controller_tcp_cb",
"eloop_sock_table_add_sock;?eloop_register_sock;dpp_controller_tcp_cb",
"dpp_controller_rx",
"dpp_controller_rx_auth_req",
"wpabuf_alloc;=dpp_tcp_send_msg;dpp_controller_rx_auth_req"]
cmd = "DPP_AUTH_INIT peer=%d tcp_addr=127.0.0.1" % peer
for func in tests:
with alloc_fail(dev[0], 1, func):
if "OK" not in dev[1].request(cmd):
raise Exception("Failed to initiate TCP connection")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
def test_dpp_controller_rx_errors(dev, apdev, params):
"""DPP Controller RX error cases"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
try:
run_dpp_controller_rx_errors(dev, apdev)
finally:
dev[0].request("DPP_CONTROLLER_STOP")
def run_dpp_controller_rx_errors(dev, apdev):
if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
raise Exception("Could not start Controller")
addr = ("127.0.0.1", 8908)
tests = [b"abc",
b"abcd",
b"\x00\x00\x00\x00",
b"\x00\x00\x00\x01",
b"\x00\x00\x00\x01\x09",
b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\xff\xff",
b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\x01\xff",
b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\x01\x00",
b"\x00\x00\x00\x08\x09\x50\x6f\x9a\x1a\x01\x00\xff",
b"\x00\x00\x00\x01\x0a",
b"\x00\x00\x00\x04\x0a\xff\xff\xff",
b"\x00\x00\x00\x01\x0b",
b"\x00\x00\x00\x08\x0b\xff\xff\xff\xff\xff\xff\xff",
b"\x00\x00\x00\x08\x0b\xff\x00\x00\xff\xff\xff\xff",
b"\x00\x00\x00\x08\x0b\xff\x00\x00\xff\xff\x6c\x00",
b"\x00\x00\x00\x0a\x0b\xff\x00\x00\xff\xff\x6c\x02\xff\xff",
b"\x00\x00\x00\x10\x0b\xff\x00\x00\xff\xff\x6c\x08\xff\xdd\x05\x50\x6f\x9a\x1a\x01",
b"\x00\x00\x00\x12\x0b\xff\x00\x00\xff\xff\x6c\x08\xff\xdd\x05\x50\x6f\x9a\x1a\x01\x00\x00",
b"\x00\x00\x00\x01\xff",
b"\x00\x00\x00\x01\xff\xee"]
#define WLAN_PA_GAS_INITIAL_REQ 10
#define WLAN_PA_GAS_INITIAL_RESP 11
for t in tests:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
socket.IPPROTO_TCP)
sock.settimeout(0.1)
sock.connect(addr)
sock.send(t)
sock.shutdown(1)
try:
sock.recv(10)
except socket.timeout:
pass
sock.close()
def test_dpp_conn_status_success(dev, apdev):
"""DPP connection status - success"""
try:
run_dpp_conn_status(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_conn_status_wrong_passphrase(dev, apdev):
"""DPP connection status - wrong passphrase"""
try:
run_dpp_conn_status(dev, apdev, result=2)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_conn_status_no_ap(dev, apdev):
"""DPP connection status - no AP"""
try:
run_dpp_conn_status(dev, apdev, result=10)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_conn_status_connector_mismatch(dev, apdev):
"""DPP connection status - invalid Connector"""
try:
run_dpp_conn_status(dev, apdev, result=8)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_conn_status_assoc_reject(dev, apdev):
"""DPP connection status - association rejection"""
try:
dev[0].request("TEST_ASSOC_IE 30020000")
run_dpp_conn_status(dev, apdev, assoc_reject=True)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_conn_status(dev, apdev, result=0, assoc_reject=False):
check_dpp_capab(dev[0], min_ver=2)
check_dpp_capab(dev[1], min_ver=2)
if result != 10:
if result == 7 or result == 8:
params = {"ssid": "dpp-status",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
else:
if result == 2:
passphrase = "wrong passphrase"
else:
passphrase = "secret passphrase"
params = hostapd.wpa2_params(ssid="dpp-status",
passphrase=passphrase)
try:
hapd = hostapd.add_ap(apdev[0], params)
except:
raise HwsimSkip("DPP not supported")
dev[0].request("SET sae_groups ")
dev[0].set("dpp_config_processing", "2")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
if result == 7 or result == 8:
conf = 'sta-dpp'
passphrase = None
configurator = dev[1].dpp_configurator_add()
else:
conf = 'sta-psk'
passphrase = "secret passphrase"
configurator = None
dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid="dpp-status",
passphrase=passphrase, configurator=configurator,
conn_status=True)
res = wait_auth_success(dev[0], dev[1], configurator=dev[1],
enrollee=dev[0])
if 'wait_conn_status' not in res:
raise Exception("Configurator did not request connection status")
if assoc_reject and result == 0:
result = 2
ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
if ev is None:
raise Exception("No connection status reported")
if "timeout" in ev:
raise Exception("Connection status result timeout")
if "result=%d" % result not in ev:
raise Exception("Unexpected connection status result: " + ev)
if "ssid=dpp-status" not in ev:
raise Exception("SSID not reported")
if result == 0:
dev[0].wait_connected()
if result == 10 and "channel_list=" not in ev:
raise Exception("Channel list not reported for no-AP")
+def test_dpp_conn_status_success_hostapd_configurator(dev, apdev):
+ """DPP connection status - success with hostapd as Configurator"""
+ try:
+ run_dpp_conn_status_hostapd_configurator(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_conn_status_hostapd_configurator(dev, apdev):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d" % conf_id
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ update_hapd_config(hapd)
+
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ id1 = hapd.dpp_qr_code(uri0)
+ res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
+ if "FAIL" in res:
+ raise Exception("DPP_BOOTSTRAP_INFO failed")
+ if "type=QRCODE" not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
+ if "mac_addr=" + dev[0].own_addr() not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
+ dev[0].set("dpp_config_processing", "2")
+ dev[0].dpp_listen(2412)
+ hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp",
+ conn_status=True)
+ res = wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
+ if 'wait_conn_status' not in res:
+ raise Exception("Configurator did not request connection status")
+ ev = hapd.wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+ if "result=0" not in ev:
+ raise Exception("Unexpected connection status: " + ev)
+
def test_dpp_mud_url(dev, apdev):
"""DPP MUD URL"""
check_dpp_capab(dev[0])
try:
dev[0].set("dpp_name", "Test Enrollee")
dev[0].set("dpp_mud_url", "https://example.com/mud")
run_dpp_qr_code_auth_unicast(dev, apdev, None)
finally:
dev[0].set("dpp_mud_url", "")
dev[0].set("dpp_name", "Test")
def test_dpp_mud_url_hostapd(dev, apdev):
"""DPP MUD URL from hostapd"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
params = {"ssid": "unconfigured",
"dpp_name": "AP Enrollee",
"dpp_mud_url": "https://example.com/mud"}
hapd = hostapd.add_ap(apdev[0], params)
check_dpp_capab(hapd)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
conf_id = dev[0].dpp_configurator_add()
dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
update_hapd_config(hapd)
def test_dpp_config_save(dev, apdev, params):
"""DPP configuration saving"""
config = os.path.join(params['logdir'], 'dpp_config_save.conf')
run_dpp_config_save(dev, apdev, config, "test", '"test"')
def test_dpp_config_save2(dev, apdev, params):
"""DPP configuration saving (2)"""
config = os.path.join(params['logdir'], 'dpp_config_save2.conf')
run_dpp_config_save(dev, apdev, config, "\\u0001*", '012a')
def test_dpp_config_save3(dev, apdev, params):
"""DPP configuration saving (3)"""
config = os.path.join(params['logdir'], 'dpp_config_save3.conf')
run_dpp_config_save(dev, apdev, config, "\\u0001*\\u00c2\\u00bc\\u00c3\\u009e\\u00c3\\u00bf", '012ac2bcc39ec3bf')
def run_dpp_config_save(dev, apdev, config, conf_ssid, exp_ssid):
check_dpp_capab(dev[1])
with open(config, "w") as f:
f.write("update_config=1\n" +
"dpp_config_processing=1\n")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", config=config)
check_dpp_capab(wpas)
conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"' + conf_ssid + '"},"cred":{"akm":"psk","pass":"secret passphrase"}}'
dev[1].set("dpp_config_obj_override", conf)
dpp_dev = [wpas, dev[1]]
run_dpp_qr_code_auth_unicast(dpp_dev, apdev, "prime256v1",
require_conf_success=True)
if "OK" not in wpas.request("SAVE_CONFIG"):
raise Exception("Failed to save configuration file")
with open(config, "r") as f:
data = f.read()
logger.info("Saved configuration:\n" + data)
if 'ssid=' + exp_ssid + '\n' not in data:
raise Exception("SSID not saved")
if 'psk="secret passphrase"' not in data:
raise Exception("Passphtase not saved")
def test_dpp_nfc_uri(dev, apdev):
"""DPP bootstrapping via NFC URI record"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
logger.info("Generated URI: " + uri)
info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
logger.info("Bootstrapping info:\n" + info)
if "type=NFC-URI" not in info:
raise Exception("Unexpected bootstrapping info contents")
dev[0].dpp_listen(2412)
conf_id = dev[1].dpp_configurator_add()
dev[1].dpp_auth_init(nfc_uri=uri, configurator=conf_id, conf="sta-dpp")
wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
def test_dpp_nfc_uri_hostapd(dev, apdev):
"""DPP bootstrapping via NFC URI record (hostapd)"""
check_dpp_capab(dev[0])
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd)
id = hapd.dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id)
logger.info("Generated URI: " + uri)
info = hapd.request("DPP_BOOTSTRAP_INFO %d" % id)
logger.info("Bootstrapping info:\n" + info)
if "type=NFC-URI" not in info:
raise Exception("Unexpected bootstrapping info contents")
hapd.dpp_listen(2412)
conf_id = dev[0].dpp_configurator_add()
dev[0].dpp_auth_init(nfc_uri=uri, configurator=conf_id, conf="ap-dpp")
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
+def test_dpp_nfc_uri_hostapd_tag_read(dev, apdev):
+ """DPP bootstrapping via NFC URI record (hostapd reading tag)"""
+ check_dpp_capab(dev[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ id = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2412)
+
+ hapd.dpp_auth_init(nfc_uri=uri, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd)
+
def test_dpp_nfc_negotiated_handover(dev, apdev):
"""DPP bootstrapping via NFC negotiated handover"""
run_dpp_nfc_negotiated_handover(dev)
def test_dpp_nfc_negotiated_handover_diff_curve(dev, apdev):
"""DPP bootstrapping via NFC negotiated handover (different curve)"""
run_dpp_nfc_negotiated_handover(dev, curve0="prime256v1",
curve1="secp384r1")
def test_dpp_nfc_negotiated_handover_hostapd_sel(dev, apdev):
"""DPP bootstrapping via NFC negotiated handover (hostapd as selector)"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
run_dpp_nfc_negotiated_handover([dev[0], hapd], conf="ap-dpp")
def test_dpp_nfc_negotiated_handover_hostapd_req(dev, apdev):
"""DPP bootstrapping via NFC negotiated handover (hostapd as requestor)"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"channel": "6"})
check_dpp_capab(hapd)
run_dpp_nfc_negotiated_handover([hapd, dev[0]])
def run_dpp_nfc_negotiated_handover(dev, curve0=None, curve1=None,
conf="sta-dpp"):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id0 = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/6,11", mac=True,
curve=curve0)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
logger.info("Generated URI[0]: " + uri0)
id1 = dev[1].dpp_bootstrap_gen(type="nfc-uri", chan="81/1,6,11", mac=True,
curve=curve1)
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
logger.info("Generated URI[1]: " + uri1)
# dev[0] acting as NFC Handover Requestor
# dev[1] acting as NFC Handover Selector
res = dev[1].request("DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (id1, uri0))
if "FAIL" in res:
raise Exception("Failed to process NFC Handover Request")
info = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id1)
logger.info("Updated local bootstrapping info:\n" + info)
freq = None
for line in info.splitlines():
if line.startswith("use_freq="):
freq = int(line.split('=')[1])
if freq is None:
raise Exception("Selected channel not indicated")
uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
logger.info("Updated URI[1]: " + uri1)
dev[1].dpp_listen(freq)
res = dev[0].request("DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (id0, uri1))
if "FAIL" in res:
raise Exception("Failed to process NFC Handover Select")
peer = int(res)
conf_id = dev[0].dpp_configurator_add()
dev[0].dpp_auth_init(peer=peer, own=id0, configurator=conf_id,
conf=conf)
wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1])
+def test_dpp_nfc_errors_hostapd(dev, apdev):
+ """DPP NFC operation failures in hostapd"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id0 = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/11", mac=True,
+ curve="secp384r1")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+
+ id_h = hapd.dpp_bootstrap_gen(type="nfc-uri", chan="81/6", mac=True)
+ uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ tests = ["",
+ "own=123456789",
+ "own=%d" % id_h,
+ "own=%d uri=%s" % (id_h, "foo")]
+ for t in tests:
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_REQ " + t):
+ raise Exception("Invalid DPP_NFC_HANDOVER_REQ accepted")
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_SEL " + t):
+ raise Exception("Invalid DPP_NFC_HANDOVER_SEL accepted")
+
+ # DPP: Peer (NFC Handover Selector) used different curve
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (id_h, uri0)):
+ raise Exception("Invalid DPP_NFC_HANDOVER_SEL accepted")
+
+ # DPP: No common channel found
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (id_h, uri0)):
+ raise Exception("DPP_NFC_HANDOVER_REQ with local error accepted")
+
def test_dpp_with_p2p_device(dev, apdev):
"""DPP exchange when driver uses a separate P2P Device interface"""
check_dpp_capab(dev[0])
with HWSimRadio(use_p2p_device=True) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
check_dpp_capab(wpas)
id1 = wpas.dpp_bootstrap_gen(chan="81/1", mac=True)
uri1 = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id1)
wpas.dpp_listen(2412)
time.sleep(7)
dev[0].dpp_auth_init(uri=uri1)
wait_auth_success(wpas, dev[0], configurator=dev[0], enrollee=wpas,
allow_enrollee_failure=True)
@long_duration_test
def test_dpp_chirp(dev, apdev):
"""DPP chirp"""
check_dpp_capab(dev[0])
dev[0].flush_scan_cache()
params = {"ssid": "dpp",
"channel": "11"}
hapd = hostapd.add_ap(apdev[0], params)
check_dpp_capab(hapd)
dpp_cc = False
id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=5" % id1):
raise Exception("DPP_CHIRP failed")
chan1 = 0
chan6 = 0
chan11 = 0
for i in range(30):
ev = dev[0].wait_event(["DPP-CHIRP-STOPPED",
"DPP-TX "], timeout=60)
if ev is None:
raise Exception("DPP chirp stop not reported")
if "DPP-CHIRP-STOPPED" in ev:
break
if "type=13" not in ev:
continue
freq = int(ev.split(' ')[2].split('=')[1])
if freq == 2412:
chan1 += 1
elif freq == 2437:
chan6 += 1
elif freq == 2462:
chan11 += 1
if not dpp_cc:
hapd.set("dpp_configurator_connectivity", "1")
if "OK" not in hapd.request("UPDATE_BEACON"):
raise Exception("UPDATE_BEACON failed")
dpp_cc = True
if chan1 != 5 or chan6 != 5 or chan11 != 1:
raise Exception("Unexpected number of presence announcements sent: %d %d %d" % (chan1, chan6, chan11))
ev = hapd.wait_event(["DPP-CHIRP-RX"], timeout=1)
if ev is None:
raise Exception("No chirp received on the AP")
if "freq=2462" not in ev:
raise Exception("Chirp reception reported on unexpected channel: " + ev)
if "src=" + dev[0].own_addr() not in ev:
raise Exception("Unexpected chirp source reported: " + ev)
@long_duration_test
def test_dpp_chirp_listen(dev, apdev):
"""DPP chirp with listen"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id1 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2 listen=2412" % id1):
raise Exception("DPP_CHIRP failed")
for i in range(30):
ev = dev[0].wait_event(["DPP-CHIRP-STOPPED",
"DPP-TX "], timeout=60)
if ev is None:
raise Exception("DPP chirp stop not reported")
if "DPP-CHIRP-STOPPED" in ev:
break
def test_dpp_chirp_configurator(dev, apdev):
"""DPP chirp with a standalone Configurator"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
conf_id = dev[1].dpp_configurator_add()
idc = dev[1].dpp_qr_code(uri)
dev[1].dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
dev[1].dpp_listen(2437)
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2" % id1):
raise Exception("DPP_CHIRP failed")
ev = dev[1].wait_event(["DPP-RX"], timeout=10)
if ev is None:
raise Exception("Presence Announcement not seen")
if "type=13" not in ev:
raise Exception("Unexpected DPP frame received: " + ev)
ev = dev[1].wait_event(["DPP-TX"], timeout=10)
if ev is None:
raise Exception("Authentication Request TX not seen")
if "type=0" not in ev:
raise Exception("Unexpected DPP frame TX: " + ev)
if "dst=" + dev[0].own_addr() not in ev:
raise Exception("Unexpected Authentication Request destination: " + ev)
wait_auth_success(dev[0], dev[1], dev[1], dev[0])
def test_dpp_chirp_ap_as_configurator(dev, apdev):
"""DPP chirp with an AP as a standalone Configurator"""
check_dpp_capab(dev[0], min_ver=2)
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd, min_ver=2)
id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
conf_id = hapd.dpp_configurator_add()
idc = hapd.dpp_qr_code(uri)
hapd.dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
hapd.dpp_listen(2412)
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2" % id1):
raise Exception("DPP_CHIRP failed")
wait_auth_success(dev[0], hapd, hapd, dev[0])
def test_dpp_chirp_configurator_inits(dev, apdev):
"""DPP chirp with a standalone Configurator initiating"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
id1 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
conf_id = dev[1].dpp_configurator_add()
idc = dev[1].dpp_qr_code(uri)
if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2 listen=2412" % id1):
raise Exception("DPP_CHIRP failed")
for i in range(2):
ev = dev[0].wait_event(["DPP-TX "], timeout=10)
if ev is None or "type=13" not in ev:
raise Exception("Presence Announcement not sent")
dev[1].dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id)
wait_auth_success(dev[0], dev[1], dev[1], dev[0])
def test_dpp_chirp_ap(dev, apdev):
"""DPP chirp by an AP"""
check_dpp_capab(dev[0], min_ver=2)
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"start_disabled": "1"})
check_dpp_capab(hapd, min_ver=2)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
conf_id = dev[0].dpp_configurator_add()
idc = dev[0].dpp_qr_code(uri)
dev[0].dpp_bootstrap_set(idc, conf="ap-dpp", configurator=conf_id)
dev[0].dpp_listen(2437)
if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
raise Exception("DPP_CHIRP failed")
wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
timeout=20)
update_hapd_config(hapd)
+@long_duration_test
+def test_dpp_chirp_ap_5g(dev, apdev):
+ """DPP chirp by an AP on 5 GHz"""
+ check_dpp_capab(dev[0], min_ver=2)
+
+ try:
+ hapd = None
+ hapd2 = None
+
+ params = {"ssid": "unconfigured",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "dpp_configurator_connectivity": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ check_dpp_capab(hapd2, min_ver=2)
+
+ params = {"ssid": "unconfigured",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "start_disabled": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_dpp_capab(hapd, min_ver=2)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ # First, check chirping iteration and timeout
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=2" % id_h):
+ raise Exception("DPP_CHIRP failed")
+ chan1 = 0
+ chan6 = 0
+ chan40 = 0
+ chan149 = 0
+ for i in range(30):
+ ev = hapd.wait_event(["DPP-CHIRP-STOPPED", "DPP-TX "], timeout=60)
+ if ev is None:
+ raise Exception("DPP chirp stop not reported")
+ if "DPP-CHIRP-STOPPED" in ev:
+ break
+ if "type=13" not in ev:
+ continue
+ freq = int(ev.split(' ')[2].split('=')[1])
+ if freq == 2412:
+ chan1 += 1
+ elif freq == 2437:
+ chan6 += 1
+ elif freq == 5200:
+ chan40 += 1
+ elif freq == 5745:
+ chan149 += 1
+ if not chan1 or not chan6 or not chan40 or not chan149:
+ raise Exception("Chirp not sent on all channels")
+
+ # Then, check successful chirping
+ conf_id = dev[0].dpp_configurator_add()
+ idc = dev[0].dpp_qr_code(uri)
+ dev[0].dpp_bootstrap_set(idc, conf="ap-dpp", configurator=conf_id)
+ dev[0].dpp_listen(5200)
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
+ raise Exception("DPP_CHIRP failed")
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ timeout=20)
+ update_hapd_config(hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
def test_dpp_chirp_ap_errors(dev, apdev):
"""DPP chirp errors in hostapd"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
"start_disabled": "1"})
check_dpp_capab(hapd, min_ver=2)
id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
tests = ["",
"own=%d" % (id_h + 1),
"own=%d iter=-1" % id_h,
"own=%d listen=0" % id_h]
for t in tests:
if "FAIL" not in hapd.request("DPP_CHIRP " + t):
raise Exception("Invalid DPP_CHIRP accepted: " + t)
if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
raise Exception("DPP_CHIRP failed")
hapd.request("DPP_STOP_CHIRP")
def start_dpp_pfs_ap(apdev, pfs, sae=False):
params = {"ssid": "dpp",
"wpa": "2",
"wpa_key_mgmt": "DPP",
"dpp_pfs": str(pfs),
"ieee80211w": "2",
"rsn_pairwise": "CCMP",
"dpp_connector": params1_ap_connector,
"dpp_csign": params1_csign,
"dpp_netaccesskey": params1_ap_netaccesskey}
if sae:
params["wpa_key_mgmt"] = "DPP SAE"
params["sae_password"] = "sae-password"
try:
hapd = hostapd.add_ap(apdev, params)
except:
raise HwsimSkip("DPP not supported")
return hapd
def run_dpp_pfs_sta(dev, pfs, fail=False, pfs_expected=None, sae=False):
key_mgmt = "DPP SAE" if sae else "DPP"
psk = "sae-password" if sae else None
dev.connect("dpp", key_mgmt=key_mgmt, scan_freq="2412",
ieee80211w="2", dpp_pfs=str(pfs),
dpp_csign=params1_csign,
dpp_connector=params1_sta_connector,
dpp_netaccesskey=params1_sta_netaccesskey,
psk=psk,
wait_connect=not fail)
if fail:
for i in range(2):
ev = dev.wait_event(["CTRL-EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection result not reported")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
dev.request("REMOVE_NETWORK all")
else:
if pfs_expected is not None:
res = dev.get_status_field("dpp_pfs")
pfs_used = res == "1"
if pfs_expected != pfs_used:
raise Exception("Unexpected PFS negotiation result")
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
def test_dpp_pfs_ap_0(dev, apdev):
"""DPP PFS AP default"""
check_dpp_capab(dev[0])
hapd = start_dpp_pfs_ap(apdev[0], 0)
run_dpp_pfs_sta(dev[0], 0, pfs_expected=True)
run_dpp_pfs_sta(dev[0], 1, pfs_expected=True)
run_dpp_pfs_sta(dev[0], 2, pfs_expected=False)
def test_dpp_pfs_ap_1(dev, apdev):
"""DPP PFS AP required"""
check_dpp_capab(dev[0])
hapd = start_dpp_pfs_ap(apdev[0], 1)
run_dpp_pfs_sta(dev[0], 0, pfs_expected=True)
run_dpp_pfs_sta(dev[0], 1, pfs_expected=True)
run_dpp_pfs_sta(dev[0], 2, fail=True)
def test_dpp_pfs_ap_2(dev, apdev):
"""DPP PFS AP not allowed"""
check_dpp_capab(dev[0])
hapd = start_dpp_pfs_ap(apdev[0], 2)
run_dpp_pfs_sta(dev[0], 0, pfs_expected=False)
run_dpp_pfs_sta(dev[0], 1, fail=True)
run_dpp_pfs_sta(dev[0], 2, pfs_expected=False)
def test_dpp_pfs_connect_cmd(dev, apdev):
"""DPP PFS and cfg80211 connect command"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
check_dpp_capab(wpas)
hapd = start_dpp_pfs_ap(apdev[0], 0)
run_dpp_pfs_sta(wpas, 0, pfs_expected=True)
run_dpp_pfs_sta(wpas, 1, pfs_expected=True)
run_dpp_pfs_sta(wpas, 2, pfs_expected=False)
def test_dpp_pfs_connect_cmd_ap_2(dev, apdev):
"""DPP PFS and cfg80211 connect command (PFS not allowed by AP)"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
check_dpp_capab(wpas)
hapd = start_dpp_pfs_ap(apdev[0], 2)
run_dpp_pfs_sta(wpas, 0, pfs_expected=False)
run_dpp_pfs_sta(wpas, 1, fail=True)
run_dpp_pfs_sta(wpas, 2, pfs_expected=False)
def test_dpp_pfs_connect_cmd_ap_2_sae(dev, apdev):
"""DPP PFS and cfg80211 connect command (PFS not allowed by AP; SAE enabled)"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
check_dpp_capab(wpas)
if "SAE" not in wpas.get_capability("auth_alg"):
raise HwsimSkip("SAE not supported")
hapd = start_dpp_pfs_ap(apdev[0], 2, sae=True)
run_dpp_pfs_sta(wpas, 0, pfs_expected=False, sae=True)
run_dpp_pfs_sta(wpas, 1, fail=True, sae=True)
run_dpp_pfs_sta(wpas, 2, pfs_expected=False, sae=True)
def test_dpp_pfs_ap_0_sta_ver1(dev, apdev):
"""DPP PFS AP default with version 1 STA"""
check_dpp_capab(dev[0])
dev[0].set("dpp_version_override", "1")
hapd = start_dpp_pfs_ap(apdev[0], 0)
run_dpp_pfs_sta(dev[0], 0, pfs_expected=False)
+def test_dpp_pfs_errors(dev, apdev):
+ """DPP PFS error cases"""
+ check_dpp_capab(dev[0], min_ver=2)
+ hapd = start_dpp_pfs_ap(apdev[0], 1)
+ tests = [(1, "dpp_pfs_init"),
+ (1, "crypto_ecdh_init;dpp_pfs_init"),
+ (1, "wpabuf_alloc;dpp_pfs_init")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2", dpp_pfs="1",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
def test_dpp_reconfig_connector(dev, apdev):
"""DPP reconfiguration connector"""
try:
run_dpp_reconfig_connector(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def test_dpp_reconfig_connector_different_groups(dev, apdev):
"""DPP reconfiguration connector with different groups"""
try:
run_dpp_reconfig_connector(dev, apdev, conf_curve="secp384r1")
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
@long_duration_test
def test_dpp_reconfig_retries(dev, apdev):
"""DPP reconfiguration retries"""
try:
run_dpp_reconfig_connector(dev, apdev, test_retries=True)
for i in range(4):
ev = dev[0].wait_event(["DPP-TX "], timeout=120)
if ev is None or "type=14" not in ev:
raise Exception("Reconfig Announcement not sent")
dev[0].request("DPP_STOP_LISTEN")
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_reconfig_connector(dev, apdev, conf_curve=None,
test_retries=False):
check_dpp_capab(dev[0], min_ver=2)
check_dpp_capab(dev[1], min_ver=2)
ssid = "reconfig"
passphrase = "secret passphrase"
passphrase2 = "another secret passphrase"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].set("dpp_config_processing", "2")
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
configurator = dev[1].dpp_configurator_add(curve=conf_curve)
conf = 'sta-psk'
dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid=ssid,
passphrase=passphrase, configurator=configurator,
conn_status=True)
res = wait_auth_success(dev[0], dev[1], configurator=dev[1],
enrollee=dev[0])
if 'wait_conn_status' not in res:
raise Exception("Configurator did not request connection status")
ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
if ev is None:
raise Exception("No connection status reported")
dev[1].dump_monitor()
ev = dev[0].wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
if ev is None:
raise Exception("SSID not reported")
res_ssid = ev.split(' ')[1]
if res_ssid != ssid:
raise Exception("Unexpected SSID value")
ev = dev[0].wait_event(["DPP-CONNECTOR"], timeout=1)
if ev is None:
raise Exception("Connector not reported")
connector = ev.split(' ')[1]
ev = dev[0].wait_event(["DPP-C-SIGN-KEY"], timeout=1)
if ev is None:
raise Exception("C-sign-key not reported")
p = ev.split(' ')
csign = p[1]
ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
if ev is None:
raise Exception("netAccessKey not reported")
p = ev.split(' ')
net_access_key = p[1]
net_access_key_expiry = p[2] if len(p) > 2 else None
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
dev[0].wait_connected()
n_key_mgmt = dev[0].get_network(id, "key_mgmt")
if n_key_mgmt != "WPA-PSK FT-PSK WPA-PSK-SHA256":
raise Exception("Unexpected key_mgmt: " + n_key_mgmt)
n_connector = dev[0].get_network(id, "dpp_connector")
if n_connector.strip('"') != connector:
raise Exception("Connector mismatch: %s %s" % (n_connector, connector))
n_csign = dev[0].get_network(id, "dpp_csign")
if n_csign.strip('"') != csign:
raise Exception("csign mismatch: %s %s" % (n_csign, csign))
n_net_access_key = dev[0].get_network(id, "dpp_netaccesskey")
if n_net_access_key.strip('"') != net_access_key:
raise Exception("net_access_key mismatch: %s %s" % (n_net_access_key,
net_access_key))
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
hapd.set("wpa_passphrase", passphrase2)
hapd.enable()
time.sleep(0.1)
dev[0].dump_monitor()
dev[1].dump_monitor()
if test_retries:
dev[1].request("DPP_STOP_LISTEN")
if "OK" not in dev[0].request("DPP_RECONFIG %s iter=10" % id):
raise Exception("Failed to start reconfiguration")
return
dev[1].set("dpp_configurator_params",
"conf=sta-psk ssid=%s pass=%s conn_status=1" % (binascii.hexlify(ssid.encode()).decode(), binascii.hexlify(passphrase2.encode()).decode()))
dev[1].dpp_listen(2437)
if "OK" not in dev[0].request("DPP_RECONFIG %s" % id):
raise Exception("Failed to start reconfiguration")
ev = dev[0].wait_event(["DPP-TX "], timeout=10)
if ev is None or "type=14" not in ev:
raise Exception("Reconfig Announcement not sent")
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None:
raise Exception("DPP Reconfig Announcement not received")
if "freq=2437 type=14" not in ev:
raise Exception("Unexpected RX data for Reconfig Announcement: " + ev)
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "freq=2437 type=15" not in ev:
raise Exception("DPP Reconfig Authentication Request not received")
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None or "freq=2437 type=16" not in ev:
raise Exception("DPP Reconfig Authentication Response not received")
ev = dev[0].wait_event(["DPP-RX"], timeout=5)
if ev is None or "freq=2437 type=17" not in ev:
raise Exception("DPP Reconfig Authentication Confirm not received")
ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
if ev is None or "freq=2437" not in ev:
raise Exception("DPP Config Request (GAS) not transmitted")
ev = dev[1].wait_event(["DPP-CONF-REQ-RX"], timeout=5)
if ev is None:
raise Exception("DPP Config Request (GAS) not received")
ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
if ev is None or "freq=2437" not in ev:
raise Exception("DPP Config Response (GAS) not received")
ev = dev[1].wait_event(["DPP-RX"], timeout=5)
if ev is None or "freq=2437 type=11" not in ev:
raise Exception("DPP Config Result not received")
ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=5)
if ev is None:
raise Exception("DPP Config Response (GAS) not transmitted")
ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP config response reception result not indicated")
if "DPP-CONF-RECEIVED" not in ev:
raise Exception("Reconfiguration failed")
dev[0].wait_connected()
ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
if ev is None:
raise Exception("No connection status reported")
def test_dpp_reconfig_hostapd_configurator(dev, apdev):
"""DPP reconfiguration with hostapd as configurator"""
try:
run_dpp_reconfig_hostapd_configurator(dev, apdev)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_reconfig_hostapd_configurator(dev, apdev):
ssid = "reconfig-ap"
check_dpp_capab(dev[0], min_ver=2)
hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
check_dpp_capab(hapd, min_ver=2)
conf_id = hapd.dpp_configurator_add()
cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d ssid=%s" % (conf_id, binascii.hexlify(ssid.encode()).decode())
res = hapd.request(cmd)
if "FAIL" in res:
raise Exception("Failed to generate own configuration")
hapd.set("dpp_configurator_connectivity", "1")
update_hapd_config(hapd)
id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
dev[0].set("dpp_config_processing", "2")
dev[0].dpp_listen(2412)
hapd.dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id,
extra="expiry=%d" % (time.time() + 10), ssid=ssid)
wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network id not reported")
network = int(ev.split(' ')[1])
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
time.sleep(10)
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["DPP-MISSING-CONNECTOR", "CTRL-EVENT-CONNECTED"],
timeout=15)
if ev is None or "DPP-MISSING-CONNECTOR" not in ev:
raise Exception("Missing Connector not reported")
if "netAccessKey expired" not in ev:
raise Exception("netAccessKey expiry not indicated")
dev[0].request("DISCONNECT")
dev[0].dump_monitor()
hapd.set("dpp_configurator_params",
"conf=sta-dpp configurator=%d ssid=%s" % (conf_id, binascii.hexlify(ssid.encode()).decode()))
if "OK" not in dev[0].request("DPP_RECONFIG %s" % network):
raise Exception("Failed to start reconfiguration")
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=15)
if ev is None:
raise Exception("DPP network id not reported for reconfiguration")
network2 = int(ev.split(' ')[1])
if network == network2:
raise Exception("Network ID did not change")
dev[0].wait_connected()
def test_dpp_qr_code_auth_rand_mac_addr(dev, apdev):
"""DPP QR Code and authentication exchange (rand_mac_addr=1)"""
flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
if flags & 0x0000400000000000 == 0:
raise HwsimSkip("Driver does not support random GAS TA")
try:
dev[0].set("gas_rand_mac_addr", "1")
run_dpp_qr_code_auth_unicast(dev, apdev, None)
finally:
dev[0].set("gas_rand_mac_addr", "0")
def dpp_sign_cert(cacert, cakey, csr_der):
csr = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_ASN1,
csr_der)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(12345)
cert.gmtime_adj_notBefore(-10)
cert.gmtime_adj_notAfter(100000)
cert.set_pubkey(csr.get_pubkey())
dn = csr.get_subject()
cert.set_subject(dn)
cert.set_version(2)
cert.add_extensions([
OpenSSL.crypto.X509Extension(b"basicConstraints", True,
b"CA:FALSE"),
OpenSSL.crypto.X509Extension(b"subjectKeyIdentifier", False,
b"hash", subject=cert),
OpenSSL.crypto.X509Extension(b"authorityKeyIdentifier", False,
b"keyid:always", issuer=cacert),
])
cert.set_issuer(cacert.get_subject())
cert.sign(cakey, "sha256")
return cert
def test_dpp_enterprise(dev, apdev, params):
"""DPP and enterprise EAP-TLS provisioning"""
check_dpp_capab(dev[0], min_ver=2)
try:
dev[0].set("dpp_config_processing", "2")
run_dpp_enterprise(dev, apdev, params)
finally:
dev[0].set("dpp_config_processing", "0", allow_fail=True)
def run_dpp_enterprise(dev, apdev, params):
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
cert_file = params['prefix'] + ".cert.pem"
pkcs7_file = params['prefix'] + ".pkcs7.der"
params = {"ssid": "dpp-ent",
"wpa": "2",
"wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP",
"ieee8021x": "1",
"eap_server": "1",
"eap_user_file": "auth_serv/eap_user.conf",
"ca_cert": "auth_serv/ec-ca.pem",
"server_cert": "auth_serv/ec-server.pem",
"private_key": "auth_serv/ec-server.key"}
hapd = hostapd.add_ap(apdev[0], params)
with open("auth_serv/ec-ca.pem", "rb") as f:
res = f.read()
cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
res)
with open("auth_serv/ec-ca.key", "rb") as f:
res = f.read()
cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
conf_id = dev[1].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
csrattrs = "MAsGCSqGSIb3DQEJBw=="
id1 = dev[1].dpp_auth_init(uri=uri0, configurator=conf_id, conf="sta-dot1x",
csrattrs=csrattrs, ssid="dpp-ent")
ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
if ev is None:
raise Exception("Configurator did not receive CSR")
id1_csr = int(ev.split(' ')[1].split('=')[1])
if id1 != id1_csr:
raise Exception("Peer bootstrapping ID mismatch in CSR event")
csr = ev.split(' ')[2]
if not csr.startswith("csr="):
raise Exception("Could not parse CSR event: " + ev)
csr = csr[4:]
csr = base64.b64decode(csr.encode())
logger.info("CSR: " + binascii.hexlify(csr).decode())
cert = dpp_sign_cert(cacert, cakey, csr)
with open(cert_file, 'wb') as f:
f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
cert))
subprocess.check_call(['openssl', 'crl2pkcs7', '-nocrl',
'-certfile', cert_file,
'-certfile', 'auth_serv/ec-ca.pem',
'-outform', 'DER', '-out', pkcs7_file])
#caCert = base64.b64encode(b"TODO").decode()
#res = dev[1].request("DPP_CA_SET peer=%d name=caCert value=%s" % (id1, caCert))
#if "OK" not in res:
# raise Exception("Failed to set caCert")
name = "server.w1.fi"
res = dev[1].request("DPP_CA_SET peer=%d name=trustedEapServerName value=%s" % (id1, name))
if "OK" not in res:
raise Exception("Failed to set trustedEapServerName")
with open(pkcs7_file, 'rb') as f:
pkcs7_der = f.read()
certbag = base64.b64encode(pkcs7_der).decode()
res = dev[1].request("DPP_CA_SET peer=%d name=certBag value=%s" % (id1, certbag))
if "OK" not in res:
raise Exception("Failed to set certBag")
ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Configurator)")
if "DPP-CONF-FAILED" in ev:
raise Exception("DPP configuration did not succeed (Configurator)")
ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
timeout=1)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
if "DPP-CONF-FAILED" in ev:
raise Exception("DPP configuration did not succeed (Enrollee)")
ev = dev[0].wait_event(["DPP-CERTBAG"], timeout=1)
if ev is None:
raise Exception("DPP-CERTBAG not reported")
certbag = base64.b64decode(ev.split(' ')[1].encode())
if certbag != pkcs7_der:
raise Exception("DPP-CERTBAG mismatch")
#ev = dev[0].wait_event(["DPP-CACERT"], timeout=1)
#if ev is None:
# raise Exception("DPP-CACERT not reported")
ev = dev[0].wait_event(["DPP-SERVER-NAME"], timeout=1)
if ev is None:
raise Exception("DPP-SERVER-NAME not reported")
if ev.split(' ')[1] != name:
raise Exception("DPP-SERVER-NAME mismatch: " + ev)
ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
if ev is None:
raise Exception("DPP network profile not generated")
id = ev.split(' ')[1]
dev[0].wait_connected()
def test_dpp_enterprise_reject(dev, apdev, params):
"""DPP and enterprise EAP-TLS provisioning and CSR getting rejected"""
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
conf_id = dev[1].dpp_configurator_add()
id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
dev[0].dpp_listen(2412)
csrattrs = "MAsGCSqGSIb3DQEJBw=="
id1 = dev[1].dpp_auth_init(uri=uri0, configurator=conf_id, conf="sta-dot1x",
csrattrs=csrattrs, ssid="dpp-ent")
ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
if ev is None:
raise Exception("Configurator did not receive CSR")
res = dev[1].request("DPP_CA_SET peer=%d name=status value=5" % id1)
if "OK" not in res:
raise Exception("Failed to set status")
ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Configurator)")
if "DPP-CONF-FAILED" in ev:
raise Exception("DPP configuration did not succeed (Configurator)")
ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
timeout=1)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
if "DPP-CONF-FAILED" not in ev:
raise Exception("DPP configuration did not fail (Enrollee)")
def test_dpp_enterprise_tcp(dev, apdev, params):
"""DPP over TCP for enterprise provisioning"""
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
try:
run_dpp_enterprise_tcp(dev, apdev, params)
finally:
dev[1].request("DPP_CONTROLLER_STOP")
def run_dpp_enterprise_tcp(dev, apdev, params):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
cap_lo = params['prefix'] + ".lo.pcap"
wt = WlantestCapture('lo', cap_lo)
time.sleep(1)
# Controller
conf_id = dev[1].dpp_configurator_add()
csrattrs = "MAsGCSqGSIb3DQEJBw=="
dev[1].set("dpp_configurator_params",
"conf=sta-dot1x configurator=%d csrattrs=%s" % (conf_id, csrattrs))
id_c = dev[1].dpp_bootstrap_gen()
uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
res = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_c)
req = "DPP_CONTROLLER_START"
if "OK" not in dev[1].request(req):
raise Exception("Failed to start Controller")
dev[0].dpp_auth_init(uri=uri_c, role="enrollee", tcp_addr="127.0.0.1")
run_dpp_enterprise_tcp_end(params, dev, wt)
def run_dpp_enterprise_tcp_end(params, dev, wt):
cert_file = params['prefix'] + ".cert.pem"
pkcs7_file = params['prefix'] + ".pkcs7.der"
with open("auth_serv/ec-ca.pem", "rb") as f:
res = f.read()
cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
res)
with open("auth_serv/ec-ca.key", "rb") as f:
res = f.read()
cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
if ev is None:
raise Exception("Configurator did not receive CSR")
id1_csr = int(ev.split(' ')[1].split('=')[1])
csr = ev.split(' ')[2]
if not csr.startswith("csr="):
raise Exception("Could not parse CSR event: " + ev)
csr = csr[4:]
csr = base64.b64decode(csr.encode())
logger.info("CSR: " + binascii.hexlify(csr).decode())
cert = dpp_sign_cert(cacert, cakey, csr)
with open(cert_file, 'wb') as f:
f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
cert))
subprocess.check_call(['openssl', 'crl2pkcs7', '-nocrl',
'-certfile', cert_file,
'-certfile', 'auth_serv/ec-ca.pem',
'-outform', 'DER', '-out', pkcs7_file])
with open(pkcs7_file, 'rb') as f:
pkcs7_der = f.read()
certbag = base64.b64encode(pkcs7_der).decode()
res = dev[1].request("DPP_CA_SET peer=%d name=certBag value=%s" % (id1_csr, certbag))
if "OK" not in res:
raise Exception("Failed to set certBag")
ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
if ev is None:
raise Exception("DPP configuration not completed (Configurator)")
if "DPP-CONF-FAILED" in ev:
raise Exception("DPP configuration did not succeed (Configurator)")
ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
timeout=1)
if ev is None:
raise Exception("DPP configuration not completed (Enrollee)")
if "DPP-CONF-RECEIVED" not in ev:
raise Exception("DPP configuration did not succeed (Enrollee)")
time.sleep(0.5)
wt.close()
def test_dpp_enterprise_tcp2(dev, apdev, params):
"""DPP over TCP for enterprise provisioning (Controller initiating)"""
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
try:
run_dpp_enterprise_tcp2(dev, apdev, params)
finally:
dev[0].request("DPP_CONTROLLER_STOP")
dev[1].request("DPP_CONTROLLER_STOP")
def run_dpp_enterprise_tcp2(dev, apdev, params):
check_dpp_capab(dev[0])
check_dpp_capab(dev[1])
cap_lo = params['prefix'] + ".lo.pcap"
cert_file = params['prefix'] + ".cert.pem"
pkcs7_file = params['prefix'] + ".pkcs7.der"
with open("auth_serv/ec-ca.pem", "rb") as f:
res = f.read()
cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
res)
with open("auth_serv/ec-ca.key", "rb") as f:
res = f.read()
cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
wt = WlantestCapture('lo', cap_lo)
time.sleep(1)
# Client/Enrollee/Responder
id_e = dev[0].dpp_bootstrap_gen()
uri_e = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id_e)
req = "DPP_CONTROLLER_START"
if "OK" not in dev[0].request(req):
raise Exception("Failed to start Client/Enrollee")
# Controller/Configurator/Initiator
conf_id = dev[1].dpp_configurator_add()
csrattrs = "MAsGCSqGSIb3DQEJBw=="
dev[1].dpp_auth_init(uri=uri_e, role="configurator", configurator=conf_id,
conf="sta-dot1x", csrattrs=csrattrs,
tcp_addr="127.0.0.1")
run_dpp_enterprise_tcp_end(params, dev, wt)
diff --git a/tests/hwsim/test_eap_proto.py b/tests/hwsim/test_eap_proto.py
index dab218dc1c57..afdc45d70ee2 100644
--- a/tests/hwsim/test_eap_proto.py
+++ b/tests/hwsim/test_eap_proto.py
@@ -1,10377 +1,10377 @@
# EAP protocol tests
# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import binascii
import hashlib
import hmac
import logging
logger = logging.getLogger()
import os
import select
import struct
import threading
import time
import hostapd
from utils import *
from test_ap_eap import check_eap_capa, check_hlr_auc_gw_support, int_eap_server_params
try:
import OpenSSL
openssl_imported = True
except ImportError:
openssl_imported = False
EAP_CODE_REQUEST = 1
EAP_CODE_RESPONSE = 2
EAP_CODE_SUCCESS = 3
EAP_CODE_FAILURE = 4
EAP_CODE_INITIATE = 5
EAP_CODE_FINISH = 6
EAP_TYPE_IDENTITY = 1
EAP_TYPE_NOTIFICATION = 2
EAP_TYPE_NAK = 3
EAP_TYPE_MD5 = 4
EAP_TYPE_OTP = 5
EAP_TYPE_GTC = 6
EAP_TYPE_TLS = 13
EAP_TYPE_LEAP = 17
EAP_TYPE_SIM = 18
EAP_TYPE_TTLS = 21
EAP_TYPE_AKA = 23
EAP_TYPE_PEAP = 25
EAP_TYPE_MSCHAPV2 = 26
EAP_TYPE_TLV = 33
EAP_TYPE_TNC = 38
EAP_TYPE_FAST = 43
EAP_TYPE_PAX = 46
EAP_TYPE_PSK = 47
EAP_TYPE_SAKE = 48
EAP_TYPE_IKEV2 = 49
EAP_TYPE_AKA_PRIME = 50
EAP_TYPE_GPSK = 51
EAP_TYPE_PWD = 52
EAP_TYPE_EKE = 53
EAP_TYPE_EXPANDED = 254
# Type field in EAP-Initiate and EAP-Finish messages
EAP_ERP_TYPE_REAUTH_START = 1
EAP_ERP_TYPE_REAUTH = 2
EAP_ERP_TLV_KEYNAME_NAI = 1
EAP_ERP_TV_RRK_LIFETIME = 2
EAP_ERP_TV_RMSK_LIFETIME = 3
EAP_ERP_TLV_DOMAIN_NAME = 4
EAP_ERP_TLV_CRYPTOSUITES = 5
EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6
EAP_ERP_TLV_CALLED_STATION_ID = 128
EAP_ERP_TLV_CALLING_STATION_ID = 129
EAP_ERP_TLV_NAS_IDENTIFIER = 130
EAP_ERP_TLV_NAS_IP_ADDRESS = 131
EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132
def run_pyrad_server(srv, t_stop, eap_handler):
srv.RunWithStop(t_stop, eap_handler)
def start_radius_server(eap_handler):
try:
import pyrad.server
import pyrad.packet
import pyrad.dictionary
except ImportError:
raise HwsimSkip("No pyrad modules available")
class TestServer(pyrad.server.Server):
def _HandleAuthPacket(self, pkt):
pyrad.server.Server._HandleAuthPacket(self, pkt)
eap = b''
for p in pkt[79]:
eap += p
eap_req = self.eap_handler(self.ctx, eap)
reply = self.CreateReplyPacket(pkt)
if eap_req:
while True:
if len(eap_req) > 253:
reply.AddAttribute("EAP-Message", eap_req[0:253])
eap_req = eap_req[253:]
else:
reply.AddAttribute("EAP-Message", eap_req)
break
else:
logger.info("No EAP request available")
reply.code = pyrad.packet.AccessChallenge
hmac_obj = hmac.new(reply.secret, digestmod=hashlib.md5)
hmac_obj.update(struct.pack("B", reply.code))
hmac_obj.update(struct.pack("B", reply.id))
# reply attributes
reply.AddAttribute("Message-Authenticator", 16*b'\x00')
attrs = reply._PktEncodeAttributes()
# Length
flen = 4 + 16 + len(attrs)
hmac_obj.update(struct.pack(">H", flen))
hmac_obj.update(pkt.authenticator)
hmac_obj.update(attrs)
del reply[80]
reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
self.SendReplyPacket(pkt.fd, reply)
def RunWithStop(self, t_stop, eap_handler):
self._poll = select.poll()
self._fdmap = {}
self._PrepareSockets()
self.t_stop = t_stop
self.eap_handler = eap_handler
self.ctx = {}
while not t_stop.is_set():
for (fd, event) in self._poll.poll(200):
if event == select.POLLIN:
try:
fdo = self._fdmap[fd]
self._ProcessInput(fdo)
except pyrad.server.ServerPacketError as err:
logger.info("pyrad server dropping packet: " + str(err))
except pyrad.packet.PacketError as err:
logger.info("pyrad server received invalid packet: " + str(err))
else:
logger.error("Unexpected event in pyrad server main loop")
for fd in self.authfds + self.acctfds:
fd.close()
srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
authport=18138, acctport=18139)
srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
b"radius",
"localhost")
srv.BindToAddress("")
t_stop = threading.Event()
t = threading.Thread(target=run_pyrad_server, args=(srv, t_stop, eap_handler))
t.start()
return {'srv': srv, 'stop': t_stop, 'thread': t}
def stop_radius_server(srv):
srv['stop'].set()
srv['thread'].join()
def start_ap(ap):
params = hostapd.wpa2_eap_params(ssid="eap-test")
params['auth_server_port'] = "18138"
hapd = hostapd.add_ap(ap, params)
return hapd
def test_eap_proto(dev, apdev):
"""EAP protocol tests"""
check_eap_capa(dev[0], "MD5")
def eap_handler(ctx, req):
logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success - id off by 2")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 1, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success - id off by 3")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 2, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('A'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('B'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('C'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('D'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('E'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request (same id)")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'] - 1,
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('F'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 2, 4)
return None
srv = start_radius_server(eap_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=1)
if ev is not None:
raise Exception("Unexpected EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION A":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION B":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION C":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION D":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION E":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
if ev is None:
raise Exception("Timeout on EAP notification")
if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION F":
raise Exception("Unexpected notification contents: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
finally:
stop_radius_server(srv)
def test_eap_proto_notification_errors(dev, apdev):
"""EAP Notification errors"""
def eap_handler(ctx, req):
logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('A'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Notification/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_NOTIFICATION,
ord('A'))
return None
srv = start_radius_server(eap_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_sm_processNotify"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_msg_alloc;sm_EAP_NOTIFICATION_Enter"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
EAP_SAKE_VERSION = 2
EAP_SAKE_SUBTYPE_CHALLENGE = 1
EAP_SAKE_SUBTYPE_CONFIRM = 2
EAP_SAKE_SUBTYPE_AUTH_REJECT = 3
EAP_SAKE_SUBTYPE_IDENTITY = 4
EAP_SAKE_AT_RAND_S = 1
EAP_SAKE_AT_RAND_P = 2
EAP_SAKE_AT_MIC_S = 3
EAP_SAKE_AT_MIC_P = 4
EAP_SAKE_AT_SERVERID = 5
EAP_SAKE_AT_PEERID = 6
EAP_SAKE_AT_SPI_S = 7
EAP_SAKE_AT_SPI_P = 8
EAP_SAKE_AT_ANY_ID_REQ = 9
EAP_SAKE_AT_PERM_ID_REQ = 10
EAP_SAKE_AT_ENCR_DATA = 128
EAP_SAKE_AT_IV = 129
EAP_SAKE_AT_PADDING = 130
EAP_SAKE_AT_NEXT_TMPID = 131
EAP_SAKE_AT_MSK_LIFE = 132
def test_eap_proto_sake(dev, apdev):
"""EAP-SAKE protocol tests"""
global eap_proto_sake_test_done
eap_proto_sake_test_done = False
def sake_challenge(ctx):
logger.info("Test: Challenge subtype")
return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 18,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
def sake_handler(ctx, req):
logger.info("sake_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'], 4 + 1,
EAP_TYPE_SAKE)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype without any attributes")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype")
return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype (different session id)")
return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 1, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_PERM_ID_REQ, 4, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with too short attribute")
return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 2,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_ANY_ID_REQ, 2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with truncated attribute")
return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 2,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_ANY_ID_REQ, 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with too short attribute header")
payload = struct.pack("B", EAP_SAKE_AT_ANY_ID_REQ)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with AT_IV but not AT_ENCR_DATA")
payload = struct.pack("BB", EAP_SAKE_AT_IV, 2)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with skippable and non-skippable unknown attribute")
payload = struct.pack("BBBB", 255, 2, 127, 2)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype: AT_RAND_P with invalid payload length")
payload = struct.pack("BB", EAP_SAKE_AT_RAND_P, 2)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype: AT_MIC_P with invalid payload length")
payload = struct.pack("BB", EAP_SAKE_AT_MIC_P, 2)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype: AT_PERM_ID_REQ with invalid payload length")
payload = struct.pack("BBBBBBBBBBBBBB",
EAP_SAKE_AT_SPI_S, 2,
EAP_SAKE_AT_SPI_P, 2,
EAP_SAKE_AT_ENCR_DATA, 2,
EAP_SAKE_AT_NEXT_TMPID, 2,
EAP_SAKE_AT_PERM_ID_REQ, 4, 0, 0,
EAP_SAKE_AT_PERM_ID_REQ, 2)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype: AT_PADDING")
payload = struct.pack("BBBBBB",
EAP_SAKE_AT_PADDING, 3, 0,
EAP_SAKE_AT_PADDING, 3, 1)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype: AT_MSK_LIFE")
payload = struct.pack(">BBLBBH",
EAP_SAKE_AT_MSK_LIFE, 6, 0,
EAP_SAKE_AT_MSK_LIFE, 4, 0)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype with invalid attribute length")
payload = struct.pack("BB", EAP_SAKE_AT_ANY_ID_REQ, 0)
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + len(payload),
EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
EAP_SAKE_SUBTYPE_IDENTITY) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown subtype")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, 123)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge subtype without any attributes")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge subtype with too short AT_RAND_S")
return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 2,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
EAP_SAKE_AT_RAND_S, 2)
idx += 1
if ctx['num'] == idx:
return sake_challenge(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Identity subtype")
return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
idx += 1
if ctx['num'] == idx:
return sake_challenge(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Challenge subtype")
return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 18,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
return sake_challenge(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Confirm subtype without any attributes")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM)
idx += 1
if ctx['num'] == idx:
return sake_challenge(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Confirm subtype with too short AT_MIC_S")
return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 2,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
EAP_SAKE_AT_MIC_S, 2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Confirm subtype")
return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 18,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
return sake_challenge(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Confirm subtype with incorrect AT_MIC_S")
return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 18,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
global eap_proto_sake_test_done
if eap_proto_sake_test_done:
return sake_challenge(ctx)
logger.info("No more test responses available - test case completed")
eap_proto_sake_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(sake_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
while not eap_proto_sake_test_done:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
logger.info("Too short password")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
finally:
stop_radius_server(srv)
def test_eap_proto_sake_errors(dev, apdev):
"""EAP-SAKE local error cases"""
check_eap_capa(dev[0], "SAKE")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 3):
with alloc_fail(dev[0], i, "eap_sake_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
tests = [(1, "eap_msg_alloc;eap_sake_build_msg;eap_sake_process_challenge"),
(1, "=eap_sake_process_challenge"),
(1, "eap_sake_compute_mic;eap_sake_process_challenge"),
(1, "eap_sake_build_msg;eap_sake_process_confirm"),
(1, "eap_sake_compute_mic;eap_sake_process_confirm"),
(2, "eap_sake_compute_mic;=eap_sake_process_confirm"),
(1, "eap_sake_getKey"),
(1, "eap_sake_get_emsk"),
(1, "eap_sake_get_session_id")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user@domain",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
erp="1",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
tests = [(1, "os_get_random;eap_sake_process_challenge"),
(1, "eap_sake_derive_keys;eap_sake_process_challenge"),
(2, "eap_sake_derive_keys;eap_sake_process_challenge"),
(3, "eap_sake_derive_keys;eap_sake_process_challenge"),
(4, "eap_sake_derive_keys;eap_sake_process_challenge"),
(5, "eap_sake_derive_keys;eap_sake_process_challenge"),
(6, "eap_sake_derive_keys;eap_sake_process_challenge")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def test_eap_proto_sake_errors2(dev, apdev):
"""EAP-SAKE protocol tests (2)"""
def sake_handler(ctx, req):
logger.info("sake_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity subtype")
return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SAKE,
EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
srv = start_radius_server(sake_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sake_build_msg;eap_sake_process_identity"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
def run_eap_sake_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_sake_errors_server(dev, apdev):
"""EAP-SAKE local error cases on server"""
check_eap_capa(dev[0], "SAKE")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_sake_init"),
(1, "eap_sake_build_msg;eap_sake_build_challenge"),
(1, "eap_sake_build_msg;eap_sake_build_confirm"),
(1, "eap_sake_compute_mic;eap_sake_build_confirm"),
(1, "eap_sake_process_challenge"),
(1, "eap_sake_getKey"),
(1, "eap_sake_get_emsk"),
(1, "eap_sake_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_sake_connect(dev[0])
tests = [(1, "eap_sake_init"),
(1, "eap_sake_build_challenge"),
(1, "eap_sake_build_confirm"),
(1, "eap_sake_derive_keys;eap_sake_process_challenge"),
(1, "eap_sake_compute_mic;eap_sake_process_challenge"),
(1, "eap_sake_compute_mic;eap_sake_process_confirm"),
(1, "eap_sake_compute_mic;eap_sake_build_confirm"),
(1, "eap_sake_process_confirm")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_sake_connect(dev[0])
def start_sake_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SAKE", identity="sake user",
password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # SAKE/Challenge/Request
def stop_sake_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_sake_server(dev, apdev):
"""EAP-SAKE protocol testing for the server"""
check_eap_capa(dev[0], "SAKE")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_sake_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
proxy_msg(dev[0], hapd) # SAKE/Confirm/Response
proxy_msg(hapd, dev[0]) # EAP-Success
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
dev[0].wait_connected()
stop_sake_assoc(dev[0], hapd)
start_sake_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-SAKE header
# --> EAP-SAKE: Invalid frame
msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "300200"
tx_msg(dev[0], hapd, msg)
# Unknown version
# --> EAP-SAKE: Unknown version 1
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "30010000"
tx_msg(dev[0], hapd, msg)
# Unknown session
# --> EAP-SAKE: Session ID mismatch
sess, = struct.unpack('B', binascii.unhexlify(resp[20:22]))
sess = binascii.hexlify(struct.pack('B', sess + 1)).decode()
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "3002" + sess + "00"
tx_msg(dev[0], hapd, msg)
# Unknown subtype
# --> EAP-SAKE: Unexpected subtype=5 in state=1
msg = resp[0:22] + "05" + resp[24:]
tx_msg(dev[0], hapd, msg)
# Empty challenge
# --> EAP-SAKE: Response/Challenge did not include AT_RAND_P or AT_MIC_P
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + resp[16:24]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_sake_assoc(dev[0], hapd)
start_sake_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Invalid attribute in challenge
# --> EAP-SAKE: Too short attribute
msg = resp[0:4] + "0009" + resp[8:12] + "0009" + resp[16:26]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_sake_assoc(dev[0], hapd)
start_sake_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
resp = rx_msg(dev[0])
# Empty confirm
# --> EAP-SAKE: Response/Confirm did not include AT_MIC_P
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + resp[16:26]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_sake_assoc(dev[0], hapd)
start_sake_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
resp = rx_msg(dev[0])
# Invalid attribute in confirm
# --> EAP-SAKE: Too short attribute
msg = resp[0:4] + "0009" + resp[8:12] + "0009" + resp[16:26]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_sake_assoc(dev[0], hapd)
start_sake_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
resp = rx_msg(dev[0])
# Corrupted AT_MIC_P value
# --> EAP-SAKE: Incorrect AT_MIC_P
msg = resp[0:30] + "000000000000" + resp[42:]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_sake_assoc(dev[0], hapd)
def test_eap_proto_leap(dev, apdev):
"""EAP-LEAP protocol tests"""
check_eap_capa(dev[0], "LEAP")
def leap_handler(ctx, req):
logger.info("leap_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
if ctx['num'] == 1:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_LEAP)
if ctx['num'] == 2:
logger.info("Test: Unexpected version")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
0, 0, 0)
if ctx['num'] == 3:
logger.info("Test: Invalid challenge length")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
1, 0, 0)
if ctx['num'] == 4:
logger.info("Test: Truncated challenge")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
1, 0, 8)
if ctx['num'] == 5:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 6:
logger.info("Test: Missing payload in Response")
return struct.pack(">BBHB", EAP_CODE_RESPONSE, ctx['id'],
4 + 1,
EAP_TYPE_LEAP)
if ctx['num'] == 7:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 8:
logger.info("Test: Unexpected version in Response")
return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
0, 0, 8)
if ctx['num'] == 9:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 10:
logger.info("Test: Invalid challenge length in Response")
return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
1, 0, 0)
if ctx['num'] == 11:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 12:
logger.info("Test: Truncated challenge in Response")
return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3,
EAP_TYPE_LEAP,
1, 0, 24)
if ctx['num'] == 13:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 14:
logger.info("Test: Invalid challange value in Response")
return struct.pack(">BBHBBBB6L", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0, 0, 0, 0, 0, 0)
if ctx['num'] == 15:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 16:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
if ctx['num'] == 17:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 18:
logger.info("Test: Success")
return struct.pack(">BBHB", EAP_CODE_SUCCESS, ctx['id'],
4 + 1,
EAP_TYPE_LEAP)
# hostapd will drop the next frame in the sequence
if ctx['num'] == 19:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
if ctx['num'] == 20:
logger.info("Test: Failure")
return struct.pack(">BBHB", EAP_CODE_FAILURE, ctx['id'],
4 + 1,
EAP_TYPE_LEAP)
return None
srv = start_radius_server(leap_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 12):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
if i == 10:
logger.info("Wait for additional roundtrip")
time.sleep(1)
dev[0].request("REMOVE_NETWORK all")
finally:
stop_radius_server(srv)
def test_eap_proto_leap_errors(dev, apdev):
"""EAP-LEAP protocol tests (error paths)"""
check_eap_capa(dev[0], "LEAP")
def leap_handler2(ctx, req):
logger.info("leap_handler2 - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challange value in Response")
return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_LEAP,
1, 0, 24,
0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid challenge")
return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_LEAP,
1, 0, 8, 0, 0)
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(leap_handler2)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_leap_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_leap_process_request"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_leap_process_success"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "os_get_random;eap_leap_process_success"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "eap_leap_process_response"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "nt_password_hash;eap_leap_process_response"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "hash_nt_password_hash;eap_leap_process_response"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_leap_getKey"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "eap_leap_getKey"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "nt_password_hash;eap_leap_getKey"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "hash_nt_password_hash;eap_leap_getKey"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1,
"nt_challenge_response;eap_leap_process_request"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="LEAP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
def test_eap_proto_md5(dev, apdev):
"""EAP-MD5 protocol tests"""
check_eap_capa(dev[0], "MD5")
def md5_handler(ctx, req):
logger.info("md5_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
if ctx['num'] == 1:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_MD5)
if ctx['num'] == 2:
logger.info("Test: Zero-length challenge")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_MD5,
0)
if ctx['num'] == 3:
logger.info("Test: Truncated challenge")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_MD5,
1)
if ctx['num'] == 4:
logger.info("Test: Shortest possible challenge and name")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_MD5,
1, 0xaa, ord('n'))
return None
srv = start_radius_server(md5_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 4):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
finally:
stop_radius_server(srv)
def test_eap_proto_md5_errors(dev, apdev):
"""EAP-MD5 local error cases"""
check_eap_capa(dev[0], "MD5")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with fail_test(dev[0], 1, "chap_md5"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="phase1-user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_md5_process"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="phase1-user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
def run_eap_md5_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="phase1-user", password="password",
wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_md5_errors_server(dev, apdev):
"""EAP-MD5 local error cases on server"""
check_eap_capa(dev[0], "MD5")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_md5_init")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_md5_connect(dev[0])
tests = [(1, "os_get_random;eap_md5_buildReq"),
(1, "chap_md5;eap_md5_process")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_md5_connect(dev[0])
def start_md5_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="phase1-user", password="password",
wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # MSCHAPV2/Request
proxy_msg(dev, hapd) # NAK
proxy_msg(hapd, dev) # MD5 Request
def stop_md5_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_md5_server(dev, apdev):
"""EAP-MD5 protocol testing for the server"""
check_eap_capa(dev[0], "MD5")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_md5_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # MD5 Response
proxy_msg(hapd, dev[0]) # EAP-Success
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("No EAP-Success reported")
stop_md5_assoc(dev[0], hapd)
start_md5_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-MD5 header (no length field)
hapd.note("EAP-MD5: Invalid frame")
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "04"
tx_msg(dev[0], hapd, msg)
# Too short EAP-MD5 header (no length field)
hapd.note("EAP-MD5: Invalid response (response_len=0 payload_len=1")
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "0400"
tx_msg(dev[0], hapd, msg)
stop_md5_assoc(dev[0], hapd)
def test_eap_proto_otp(dev, apdev):
"""EAP-OTP protocol tests"""
def otp_handler(ctx, req):
logger.info("otp_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
if ctx['num'] == 1:
logger.info("Test: Empty payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_OTP)
if ctx['num'] == 2:
logger.info("Test: Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
4)
if ctx['num'] == 3:
logger.info("Test: Challenge included")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_OTP,
ord('A'))
if ctx['num'] == 4:
logger.info("Test: Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
4)
return None
srv = start_radius_server(otp_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 1):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="OTP", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="OTP", identity="user", wait_connect=False)
ev = dev[0].wait_event(["CTRL-REQ-OTP"])
if ev is None:
raise Exception("Request for password timed out")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-OTP-" + id + ":password")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"])
if ev is None:
raise Exception("Success not reported")
finally:
stop_radius_server(srv)
def test_eap_proto_otp_errors(dev, apdev):
"""EAP-OTP local error cases"""
def otp_handler2(ctx, req):
logger.info("otp_handler2 - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge included")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_OTP,
ord('A'))
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(otp_handler2)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_otp_process"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="OTP", identity="user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
EAP_GPSK_OPCODE_GPSK_1 = 1
EAP_GPSK_OPCODE_GPSK_2 = 2
EAP_GPSK_OPCODE_GPSK_3 = 3
EAP_GPSK_OPCODE_GPSK_4 = 4
EAP_GPSK_OPCODE_FAIL = 5
EAP_GPSK_OPCODE_PROTECTED_FAIL = 6
def test_eap_proto_gpsk(dev, apdev):
"""EAP-GPSK protocol tests"""
def gpsk_handler(ctx, req):
logger.info("gpsk_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_GPSK)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown opcode")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_GPSK,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected GPSK-3")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Too short GPSK-1")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Truncated ID_Server")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Missing RAND_Server")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Missing CSuite_List")
return struct.pack(">BBHBBH8L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Truncated CSuite_List")
return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Empty CSuite_List")
return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Invalid CSuite_List")
return struct.pack(">BBHBBH8LHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 1,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 No supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected GPSK-1")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite but too short key")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short GPSK-3")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Mismatch in RAND_Peer")
return struct.pack(">BBHBB8L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3,
0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Missing RAND_Server")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Mismatch in RAND_Server")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8L", 1, 1, 1, 1, 1, 1, 1, 1)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Missing ID_Server")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8L", 0, 0, 0, 0, 0, 0, 0, 0)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Truncated ID_Server")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 1)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Mismatch in ID_Server")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 3,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBHB8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 3 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 1, ord('A'),
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Mismatch in ID_Server (same length)")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 3,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[15:47]
msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Missing CSuite_Sel")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 0)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Mismatch in CSuite_Sel")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Missing len(PD_Payload_Block)")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Truncated PD_Payload_Block")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2 + 6 + 2,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHLHH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Missing MAC")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2 + 6 + 3,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHLHHB",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123)
return msg
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-1 Supported CSuite")
return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 32 + 2 + 6,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_1, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: GPSK-3 Incorrect MAC")
msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 32 + 32 + 2 + 6 + 3 + 16,
EAP_TYPE_GPSK,
EAP_GPSK_OPCODE_GPSK_3)
msg += req[14:46]
msg += struct.pack(">8LHLHHB4L",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123,
0, 0, 0, 0)
return msg
return None
srv = start_radius_server(gpsk_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 27):
if i == 12:
pw = "short"
else:
pw = "abcdefghijklmnop0123456789abcdef"
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="GPSK", identity="user", password=pw,
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
finally:
stop_radius_server(srv)
def run_eap_gpsk_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_gpsk_errors_server(dev, apdev):
"""EAP-GPSK local error cases on server"""
check_eap_capa(dev[0], "GPSK")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_gpsk_init"),
(1, "eap_msg_alloc;eap_gpsk_build_gpsk_1"),
(1, "eap_msg_alloc;eap_gpsk_build_gpsk_3"),
(1, "eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_derive_keys;eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_derive_session_id;eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_getKey"),
(1, "eap_gpsk_get_emsk"),
(1, "eap_gpsk_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_gpsk_connect(dev[0])
tests = [(1, "os_get_random;eap_gpsk_build_gpsk_1"),
(1, "eap_gpsk_compute_mic;eap_gpsk_build_gpsk_3"),
(1, "eap_gpsk_derive_keys;eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_derive_session_id;eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_compute_mic;eap_gpsk_process_gpsk_2"),
(1, "eap_gpsk_compute_mic;eap_gpsk_process_gpsk_4")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_gpsk_connect(dev[0])
def start_gpsk_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # GPSK-1
def stop_gpsk_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_gpsk_server(dev, apdev):
"""EAP-GPSK protocol testing for the server"""
check_eap_capa(dev[0], "GPSK")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_gpsk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # GPSK-2
proxy_msg(hapd, dev[0]) # GPSK-3
proxy_msg(dev[0], hapd) # GPSK-4
proxy_msg(hapd, dev[0]) # EAP-Success
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
dev[0].wait_connected()
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-GPSK header (no OP-Code)
# --> EAP-GPSK: Invalid frame
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "33"
tx_msg(dev[0], hapd, msg)
# Unknown OP-Code
# --> EAP-GPSK: Unexpected opcode=7 in state=0
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3307"
tx_msg(dev[0], hapd, msg)
# Too short GPSK-2
# --> EAP-GPSK: Too short message for ID_Peer length
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3302"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for ID_Peer
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33020001"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for ID_Server length
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33020000"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for ID_Server
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "330200000001"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# ID_Server mismatch
# --> EAP-GPSK: ID_Server in GPSK-1 and GPSK-2 did not match
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "330200000000"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for RAND_Peer
msg = resp[0:4] + "0011" + resp[8:12] + "0011" + "330200000007" + binascii.hexlify(b"hostapd").decode()
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for RAND_Server
msg = resp[0:4] + "0031" + resp[8:12] + "0031" + "330200000007" + binascii.hexlify(b"hostapd").decode() + 32*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# RAND_Server mismatch
# --> EAP-GPSK: RAND_Server in GPSK-1 and GPSK-2 did not match
msg = resp[0:4] + "0051" + resp[8:12] + "0051" + "330200000007" + binascii.hexlify(b"hostapd").decode() + 32*"00" + 32*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for CSuite_List length
msg = resp[0:4] + "005a" + resp[8:12] + "005a" + resp[16:188]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for CSuite_List
msg = resp[0:4] + "005c" + resp[8:12] + "005c" + resp[16:192]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: CSuite_List in GPSK-1 and GPSK-2 did not match
msg = resp[0:4] + "005c" + resp[8:12] + "005c" + resp[16:188] + "0000"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for CSuite_Sel
msg = resp[0:4] + "0068" + resp[8:12] + "0068" + resp[16:216]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Unsupported CSuite_Sel
# --> EAP-GPSK: Peer selected unsupported ciphersuite 0:255
msg = resp[0:4] + "006e" + resp[8:12] + "006e" + resp[16:226] + "ff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for PD_Payload_1 length
msg = resp[0:4] + "006e" + resp[8:12] + "006e" + resp[16:228]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Too short message for PD_Payload_1
msg = resp[0:4] + "0070" + resp[8:12] + "0070" + resp[16:230] + "ff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short GPSK-2
# --> EAP-GPSK: Message too short for MIC (left=0 miclen=16)
msg = resp[0:4] + "0070" + resp[8:12] + "0070" + resp[16:232]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Extra data in the end of GPSK-2
# --> EAP-GPSK: Ignored 1 bytes of extra data in the end of GPSK-2
msg = resp[0:4] + "0081" + resp[8:12] + "0081" + resp[16:264] + "00"
tx_msg(dev[0], hapd, msg)
proxy_msg(hapd, dev[0]) # GPSK-3
resp = rx_msg(dev[0])
# Too short GPSK-4
# --> EAP-GPSK: Too short message for PD_Payload_1 length
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3304"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Failure
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # GPSK-2
proxy_msg(hapd, dev[0]) # GPSK-3
resp = rx_msg(dev[0])
# Too short GPSK-4
# --> EAP-GPSK: Too short message for PD_Payload_1
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33040001"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Failure
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # GPSK-2
proxy_msg(hapd, dev[0]) # GPSK-3
resp = rx_msg(dev[0])
# Too short GPSK-4
# --> EAP-GPSK: Message too short for MIC (left=0 miclen=16)
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33040000"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Failure
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # GPSK-2
proxy_msg(hapd, dev[0]) # GPSK-3
resp = rx_msg(dev[0])
# Incorrect MIC in GPSK-4
# --> EAP-GPSK: Incorrect MIC in GPSK-4
msg = resp[0:4] + "0018" + resp[8:12] + "0018" + "33040000" + 16*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Failure
stop_gpsk_assoc(dev[0], hapd)
start_gpsk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # GPSK-2
proxy_msg(hapd, dev[0]) # GPSK-3
resp = rx_msg(dev[0])
# Incorrect MIC in GPSK-4
# --> EAP-GPSK: Ignored 1 bytes of extra data in the end of GPSK-4
msg = resp[0:4] + "0019" + resp[8:12] + "0019" + resp[16:] + "00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Success
stop_gpsk_assoc(dev[0], hapd)
EAP_EKE_ID = 1
EAP_EKE_COMMIT = 2
EAP_EKE_CONFIRM = 3
EAP_EKE_FAILURE = 4
def test_eap_proto_eke(dev, apdev):
"""EAP-EKE protocol tests"""
def eke_handler(ctx, req):
logger.info("eke_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_EKE)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown exchange")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No NumProposals in EAP-EKE-ID/Request")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: NumProposals=0 in EAP-EKE-ID/Request")
return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated Proposals list in EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4,
EAP_TYPE_EKE,
EAP_EKE_ID,
2, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported proposals in EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4B4B4B4B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 * 4,
EAP_TYPE_EKE,
EAP_EKE_ID,
4, 0,
0, 0, 0, 0,
3, 0, 0, 0,
3, 1, 0, 0,
3, 1, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing IDType/Identity in EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4B4B4B4B4B",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 5 * 4,
EAP_TYPE_EKE,
EAP_EKE_ID,
5, 0,
0, 0, 0, 0,
3, 0, 0, 0,
3, 1, 0, 0,
3, 1, 1, 0,
3, 1, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
3, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
3, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
3, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected EAP-EKE-Confirm/Request")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_CONFIRM)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short EAP-EKE-Failure/Request")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_FAILURE)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected EAP-EKE-Commit/Request")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_COMMIT)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
3, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short EAP-EKE-Commit/Request")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_COMMIT)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
1, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
return struct.pack(">BBHBB4L32L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 128,
EAP_TYPE_EKE,
EAP_EKE_COMMIT,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short EAP-EKE-Confirm/Request")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_EKE,
EAP_EKE_CONFIRM)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid EAP-EKE-ID/Request")
return struct.pack(">BBHBBBB4BB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2 + 4 + 1,
EAP_TYPE_EKE,
EAP_EKE_ID,
1, 0,
1, 1, 1, 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
return struct.pack(">BBHBB4L32L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 128,
EAP_TYPE_EKE,
EAP_EKE_COMMIT,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid PNonce_PS and Auth_S values in EAP-EKE-Confirm/Request")
return struct.pack(">BBHBB4L8L5L5L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 2 * 16 + 20 + 20,
EAP_TYPE_EKE,
EAP_EKE_CONFIRM,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(eke_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 14):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="EKE", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
else:
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def eap_eke_test_fail(dev, phase1=None, success=False):
dev.connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="EKE", identity="eke user@domain", password="hello",
phase1=phase1, erp="1", wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
if not success and "CTRL-EVENT-EAP-FAILURE" not in ev:
raise Exception("EAP did not fail during failure test")
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
def test_eap_proto_eke_errors(dev, apdev):
"""EAP-EKE local error cases"""
check_eap_capa(dev[0], "EKE")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 3):
with alloc_fail(dev[0], i, "eap_eke_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="EKE", identity="eke user", password="hello",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_eke_dh_init", None),
(1, "eap_eke_prf_hmac_sha1", "dhgroup=3 encr=1 prf=1 mac=1"),
(1, "eap_eke_prf_hmac_sha256", "dhgroup=5 encr=1 prf=2 mac=2"),
(1, "eap_eke_prf", None),
(1, "os_get_random;eap_eke_dhcomp", None),
(1, "aes_128_cbc_encrypt;eap_eke_dhcomp", None),
(1, "aes_128_cbc_decrypt;eap_eke_shared_secret", None),
- (1, "eap_eke_prf;eap_eke_shared_secret", None),
- (1, "eap_eke_prfplus;eap_eke_derive_ke_ki", None),
- (1, "eap_eke_prfplus;eap_eke_derive_ka", None),
- (1, "eap_eke_prfplus;eap_eke_derive_msk", None),
+ (1, "hmac_sha256_vector;eap_eke_shared_secret", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_ke_ki", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_ka", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_msk", None),
(1, "os_get_random;eap_eke_prot", None),
(1, "aes_128_cbc_decrypt;eap_eke_decrypt_prot", None),
(1, "eap_eke_derive_key;eap_eke_process_commit", None),
(1, "eap_eke_dh_init;eap_eke_process_commit", None),
(1, "eap_eke_shared_secret;eap_eke_process_commit", None),
(1, "eap_eke_derive_ke_ki;eap_eke_process_commit", None),
(1, "eap_eke_dhcomp;eap_eke_process_commit", None),
(1, "os_get_random;eap_eke_process_commit", None),
(1, "os_get_random;=eap_eke_process_commit", None),
(1, "eap_eke_prot;eap_eke_process_commit", None),
(1, "eap_eke_decrypt_prot;eap_eke_process_confirm", None),
(1, "eap_eke_derive_ka;eap_eke_process_confirm", None),
(1, "eap_eke_auth;eap_eke_process_confirm", None),
(2, "eap_eke_auth;eap_eke_process_confirm", None),
(1, "eap_eke_prot;eap_eke_process_confirm", None),
(1, "aes_128_cbc_encrypt;eap_eke_prot;eap_eke_process_confirm", None),
(1, "hmac_sha256;eap_eke_prot;eap_eke_process_confirm", None),
(1, "eap_eke_derive_msk;eap_eke_process_confirm", None)]
for count, func, phase1 in tests:
with fail_test(dev[0], count, func):
eap_eke_test_fail(dev[0], phase1)
tests = [(1, "=eap_eke_derive_ke_ki", None),
(1, "=eap_eke_derive_ka", None),
(1, "=eap_eke_derive_msk", None),
(1, "eap_eke_build_msg;eap_eke_process_id", None),
(1, "wpabuf_alloc;eap_eke_process_id", None),
(1, "=eap_eke_process_id", None),
(1, "wpabuf_alloc;=eap_eke_process_id", None),
(1, "wpabuf_alloc;eap_eke_process_id", None),
(1, "eap_eke_build_msg;eap_eke_process_commit", None),
(1, "wpabuf_resize;eap_eke_process_commit", None),
(1, "eap_eke_build_msg;eap_eke_process_confirm", None)]
for count, func, phase1 in tests:
with alloc_fail(dev[0], count, func):
eap_eke_test_fail(dev[0], phase1)
tests = [(1, "eap_eke_getKey", None),
(1, "eap_eke_get_emsk", None),
(1, "eap_eke_get_session_id", None)]
for count, func, phase1 in tests:
with alloc_fail(dev[0], count, func):
eap_eke_test_fail(dev[0], phase1, success=True)
EAP_PAX_OP_STD_1 = 0x01
EAP_PAX_OP_STD_2 = 0x02
EAP_PAX_OP_STD_3 = 0x03
EAP_PAX_OP_SEC_1 = 0x11
EAP_PAX_OP_SEC_2 = 0x12
EAP_PAX_OP_SEC_3 = 0x13
EAP_PAX_OP_SEC_4 = 0x14
EAP_PAX_OP_SEC_5 = 0x15
EAP_PAX_OP_ACK = 0x21
EAP_PAX_FLAGS_MF = 0x01
EAP_PAX_FLAGS_CE = 0x02
EAP_PAX_FLAGS_AI = 0x04
EAP_PAX_MAC_HMAC_SHA1_128 = 0x01
EAP_PAX_HMAC_SHA256_128 = 0x02
EAP_PAX_DH_GROUP_NONE = 0x00
EAP_PAX_DH_GROUP_2048_MODP = 0x01
EAP_PAX_DH_GROUP_3072_MODP = 0x02
EAP_PAX_DH_GROUP_NIST_ECC_P_256 = 0x03
EAP_PAX_PUBLIC_KEY_NONE = 0x00
EAP_PAX_PUBLIC_KEY_RSAES_OAEP = 0x01
EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 = 0x02
EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC = 0x03
EAP_PAX_ADE_VENDOR_SPECIFIC = 0x01
EAP_PAX_ADE_CLIENT_CHANNEL_BINDING = 0x02
EAP_PAX_ADE_SERVER_CHANNEL_BINDING = 0x03
def test_eap_proto_pax(dev, apdev):
"""EAP-PAX protocol tests"""
def pax_std_1(ctx):
logger.info("Test: STD-1")
ctx['id'] = 10
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0x16, 0xc9, 0x08, 0x9d, 0x98, 0xa5, 0x6e, 0x1f,
0xf0, 0xac, 0xcf, 0xc4, 0x66, 0xcd, 0x2d, 0xbf)
def pax_handler(ctx, req):
logger.info("pax_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_PAX)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Minimum length payload")
return struct.pack(">BBHB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 16,
EAP_TYPE_PAX,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported MAC ID")
return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, 255, EAP_PAX_DH_GROUP_NONE,
EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported DH Group ID")
return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
255, EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported Public Key ID")
return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, 255,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: More fragments")
return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_MF,
EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid ICV")
return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid ICV in short frame")
return struct.pack(">BBHBBBBBB3L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 12,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Correct ICV - unsupported op_code")
ctx['id'] = 10
return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
255, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0x90, 0x78, 0x97, 0x38, 0x29, 0x94, 0x32, 0xd4,
0x81, 0x27, 0xe0, 0xf6, 0x3b, 0x0d, 0xb2, 0xb2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Correct ICV - CE flag in STD-1")
ctx['id'] = 10
return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_CE,
EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0x9c, 0x98, 0xb4, 0x0b, 0x94, 0x90, 0xde, 0x88,
0xb7, 0x72, 0x63, 0x44, 0x1d, 0xe3, 0x7c, 0x5c)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Correct ICV - too short STD-1 payload")
ctx['id'] = 10
return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0xda, 0xab, 0x2c, 0xe7, 0x84, 0x41, 0xb5, 0x5c,
0xee, 0xcf, 0x62, 0x03, 0xc5, 0x69, 0xcb, 0xf4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Correct ICV - incorrect A length in STD-1")
ctx['id'] = 10
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0xc4, 0xb0, 0x81, 0xe4, 0x6c, 0x8c, 0x20, 0x23,
0x60, 0x46, 0x89, 0xea, 0x94, 0x60, 0xf3, 0x2a)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Correct ICV - extra data in STD-1")
ctx['id'] = 10
return struct.pack(">BBHBBBBBBH8LB16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 1 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
1,
0x61, 0x49, 0x65, 0x37, 0x21, 0xe8, 0xd8, 0xbf,
0xf3, 0x02, 0x01, 0xe5, 0x42, 0x51, 0xd3, 0x34)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected STD-1")
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0xe5, 0x1d, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
idx += 1
if ctx['num'] == idx:
return pax_std_1(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: MAC ID changed during session")
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_HMAC_SHA256_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0xee, 0x00, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
idx += 1
if ctx['num'] == idx:
return pax_std_1(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: DH Group ID changed during session")
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_2048_MODP,
EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0xee, 0x01, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
idx += 1
if ctx['num'] == idx:
return pax_std_1(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Public Key ID changed during session")
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE,
EAP_PAX_PUBLIC_KEY_RSAES_OAEP,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0xee, 0x02, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected STD-3")
ctx['id'] = 10
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_3, 0, EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0x47, 0xbb, 0xc0, 0xf9, 0xb9, 0x69, 0xf5, 0xcb,
0x3a, 0xe8, 0xe7, 0xd6, 0x80, 0x28, 0xf2, 0x59)
idx += 1
if ctx['num'] == idx:
return pax_std_1(ctx)
idx += 1
if ctx['num'] == idx:
# TODO: MAC calculation; for now, this gets dropped due to incorrect
# ICV
logger.info("Test: STD-3 with CE flag")
return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 5 + 2 + 32 + 16,
EAP_TYPE_PAX,
EAP_PAX_OP_STD_3, EAP_PAX_FLAGS_CE,
EAP_PAX_MAC_HMAC_SHA1_128,
EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
32, 0, 0, 0, 0, 0, 0, 0, 0,
0x8a, 0xc2, 0xf9, 0xf4, 0x8b, 0x75, 0x72, 0xa2,
0x4d, 0xd3, 0x1e, 0x54, 0x77, 0x04, 0x05, 0xe2)
idx += 1
if ctx['num'] & 0x1 == idx & 0x1:
logger.info("Test: Default request")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_PAX)
else:
logger.info("Test: Default EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(pax_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 18):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="user",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
logger.info("Waiting for EAP method to start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
logger.info("Too short password")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="user",
password_hex="0123456789abcdef0123456789abcd",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
logger.info("No password")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="user",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_pax_errors(dev, apdev):
"""EAP-PAX local error cases"""
check_eap_capa(dev[0], "PAX")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 3):
with alloc_fail(dev[0], i, "eap_pax_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = ["eap_msg_alloc;eap_pax_alloc_resp;eap_pax_process_std_1",
"eap_msg_alloc;eap_pax_alloc_resp;eap_pax_process_std_3",
"eap_pax_getKey",
"eap_pax_get_emsk",
"eap_pax_get_session_id"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "os_get_random;eap_pax_process_std_1"),
(1, "eap_pax_initial_key_derivation"),
(1, "eap_pax_mac;eap_pax_process_std_3"),
(2, "eap_pax_mac;eap_pax_process_std_3"),
(1, "eap_pax_kdf;eap_pax_getKey"),
(1, "eap_pax_kdf;eap_pax_get_emsk")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def run_eap_pax_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_pax_errors_server(dev, apdev):
"""EAP-PAX local error cases on server"""
check_eap_capa(dev[0], "PAX")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_pax_init"),
(1, "eap_msg_alloc;eap_pax_build_std_1"),
(1, "eap_msg_alloc;eap_pax_build_std_3"),
(1, "=eap_pax_process_std_2"),
(1, "eap_pax_getKey"),
(1, "eap_pax_get_emsk"),
(1, "eap_pax_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_pax_connect(dev[0])
tests = [(1, "os_get_random;eap_pax_build_std_1"),
(1, "eap_pax_mac;eap_pax_build_std_1"),
(1, "eap_pax_mac;eap_pax_build_std_3"),
(2, "eap_pax_mac;=eap_pax_build_std_3"),
(1, "eap_pax_initial_key_derivation;eap_pax_process_std_2"),
(1, "eap_pax_mac;eap_pax_process_std_2"),
(2, "eap_pax_mac;=eap_pax_process_std_2"),
(1, "eap_pax_mac;eap_pax_check")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_pax_connect(dev[0])
def start_pax_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # PAX_STD-1
def stop_pax_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_pax_server(dev, apdev):
"""EAP-PAX protocol testing for the server"""
check_eap_capa(dev[0], "PAX")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_pax_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PAX_STD-2
proxy_msg(hapd, dev[0]) # PAX_STD-3
proxy_msg(dev[0], hapd) # PAX-ACK
proxy_msg(hapd, dev[0]) # EAP-Success
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
dev[0].wait_connected()
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-PAX header (no OP-Code)
hapd.note("EAP-PAX: Invalid frame")
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "2e"
tx_msg(dev[0], hapd, msg)
# Too short EAP-PAX message (no payload)
hapd.note("EAP-PAX: Invalid frame")
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "2e1100000000"
tx_msg(dev[0], hapd, msg)
# Unexpected PAX_SEC-2
hapd.note("EAP-PAX: Expected PAX_STD-2 - ignore op 17")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e1100000000" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Unexpected MAC ID
hapd.note("EAP-PAX: Expected MAC ID 0x1, received 0xff")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0200ff0000" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Unexpected DH Group ID
hapd.note("EAP-PAX: Expected DH Group ID 0x0, received 0xff")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e020001ff00" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Unexpected Public Key ID
hapd.note("EAP-PAX: Expected Public Key ID 0x0, received 0xff")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e02000100ff" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Unsupported Flags - MF
hapd.note("EAP-PAX: fragmentation not supported")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0201010000" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Unsupported Flags - CE
hapd.note("EAP-PAX: Unexpected CE flag")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0202010000" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Too short Payload in PAX_STD-2
hapd.note("EAP-PAX: Too short PAX_STD-2 (B)")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0200010000" + 16*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short Payload in PAX_STD-2
hapd.note("EAP-PAX: Too short PAX_STD-2 (CID)")
msg = resp[0:4] + "002c" + resp[8:12] + "002c" + "2e0200010000" + "0020" + 32*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short Payload in PAX_STD-2
hapd.note("EAP-PAX: Too short PAX_STD-2 (CID)")
msg = resp[0:4] + "002e" + resp[8:12] + "002e" + "2e0200010000" + "0020" + 32*"00" + "ffff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too long CID in PAX_STD-2
hapd.note("EAP-PAX: Too long CID")
msg = resp[0:4] + "062e" + resp[8:12] + "062e" + "2e0200010000" + "0020" + 32*"00" + "0600" + 1536*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short Payload in PAX_STD-2
hapd.note("EAP-PAX: Too short PAX_STD-2 (MAC_CK)")
msg = resp[0:4] + "003c" + resp[8:12] + "003c" + "2e0200010000" + "0020" + 32*"00" + 16*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Unknown CID for PAX
hapd.note("EAP-PAX: EAP-PAX not enabled for CID")
msg = resp[0:4] + "0041" + resp[8:12] + "0041" + "2e0200010000" + "0020" + 32*"00" + "0001" + "00" + "0010" + 16*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short ICV
hapd.note("EAP-PAX: Too short ICV (15) in PAX_STD-2")
msg = resp[0:4] + "0063" + resp[8:12] + "0063" + resp[16:206]
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_pax_assoc(dev[0], hapd)
start_pax_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PAX_STD-2
proxy_msg(hapd, dev[0]) # PAX_STD-3
resp = rx_msg(dev[0])
# Unexpected PAX_STD-2
hapd.note("EAP-PAX: Expected PAX-ACK - ignore op 1")
msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0100000000" + 16*"00"
tx_msg(dev[0], hapd, msg)
stop_pax_assoc(dev[0], hapd)
def test_eap_proto_psk(dev, apdev):
"""EAP-PSK protocol tests"""
def psk_handler(ctx, req):
logger.info("psk_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_PSK)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Non-zero T in first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0xc0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short third message")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_PSK)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Incorrect T in third message")
return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing PCHANNEL in third message")
return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 16,
EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalic MAC_S in third message")
return struct.pack(">BBHBB4L4L5LB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16 + 16 + 21,
EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first message")
return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 16,
EAP_TYPE_PSK, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(psk_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 6):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="user",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
logger.info("Test: Invalid PSK length")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="user",
password_hex="0123456789abcdef0123456789abcd",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
finally:
stop_radius_server(srv)
def test_eap_proto_psk_errors(dev, apdev):
"""EAP-PSK local error cases"""
check_eap_capa(dev[0], "PSK")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 3):
with alloc_fail(dev[0], i, "eap_psk_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 4):
with fail_test(dev[0], i, "eap_psk_key_setup;eap_psk_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "=eap_psk_process_1"),
(2, "=eap_psk_process_1"),
(1, "eap_msg_alloc;eap_psk_process_1"),
(1, "=eap_psk_process_3"),
(2, "=eap_psk_process_3"),
(1, "eap_msg_alloc;eap_psk_process_3"),
(1, "eap_psk_getKey"),
(1, "eap_psk_get_session_id"),
(1, "eap_psk_get_emsk")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
note="No allocation failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "os_get_random;eap_psk_process_1"),
(1, "omac1_aes_128;eap_psk_process_3"),
(1, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
(2, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
(3, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
(1, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
(2, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
(3, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
(1, "aes_128_eax_decrypt;eap_psk_process_3"),
(2, "aes_128_eax_decrypt;eap_psk_process_3"),
(3, "aes_128_eax_decrypt;eap_psk_process_3"),
(1, "aes_128_eax_encrypt;eap_psk_process_3"),
(2, "aes_128_eax_encrypt;eap_psk_process_3"),
(3, "aes_128_eax_encrypt;eap_psk_process_3"),
(1, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(2, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(3, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(4, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(5, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(6, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(7, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(8, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(9, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(10, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
(1, "aes_ctr_encrypt;aes_128_eax_decrypt;eap_psk_process_3"),
(1, "aes_ctr_encrypt;aes_128_eax_encrypt;eap_psk_process_3")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL",
note="No failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def run_eap_psk_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_psk_errors_server(dev, apdev):
"""EAP-PSK local error cases on server"""
check_eap_capa(dev[0], "PSK")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_psk_init"),
(1, "eap_msg_alloc;eap_psk_build_1"),
(1, "eap_msg_alloc;eap_psk_build_3"),
(1, "=eap_psk_build_3"),
(1, "=eap_psk_process_2"),
(2, "=eap_psk_process_2"),
(1, "=eap_psk_process_4"),
(1, "aes_128_eax_decrypt;eap_psk_process_4"),
(1, "eap_psk_getKey"),
(1, "eap_psk_get_emsk"),
(1, "eap_psk_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_psk_connect(dev[0])
tests = [(1, "os_get_random;eap_psk_build_1"),
(1, "omac1_aes_128;eap_psk_build_3"),
(1, "eap_psk_derive_keys;eap_psk_build_3"),
(1, "aes_128_eax_encrypt;eap_psk_build_3"),
(1, "eap_psk_key_setup;eap_psk_process_2"),
(1, "omac1_aes_128;eap_psk_process_2"),
(1, "aes_128_eax_decrypt;eap_psk_process_4")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_psk_connect(dev[0])
def start_psk_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # PSK-1
def stop_psk_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_psk_server(dev, apdev):
"""EAP-PSK protocol testing for the server"""
check_eap_capa(dev[0], "PSK")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_psk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PSK-2
proxy_msg(hapd, dev[0]) # PSK-3
proxy_msg(dev[0], hapd) # PSK-4
proxy_msg(hapd, dev[0]) # EAP-Success
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
dev[0].wait_connected()
stop_psk_assoc(dev[0], hapd)
start_psk_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-PSK header (no Flags)
hapd.note("EAP-PSK: Invalid frame")
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "2f"
tx_msg(dev[0], hapd, msg)
# Unexpected PSK-1
hapd.note("EAP-PSK: Expected PSK-2 - ignore T=0")
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "2f00"
tx_msg(dev[0], hapd, msg)
# Too short PSK-2
hapd.note("EAP-PSK: Too short frame")
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "2f40"
tx_msg(dev[0], hapd, msg)
# PSK-2 with unknown ID_P
hapd.note("EAP-PSK: EAP-PSK not enabled for ID_P")
msg = resp[0:4] + "004a" + resp[8:12] + "004a" + "2f40" + 3*16*"00" + 20*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # EAP-Failure
stop_psk_assoc(dev[0], hapd)
start_psk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PSK-2
proxy_msg(hapd, dev[0]) # PSK-3
resp = rx_msg(dev[0])
# Unexpected PSK-2
hapd.note("EAP-PSK: Expected PSK-4 - ignore T=1")
msg = resp[0:4] + "0016" + resp[8:12] + "0016" + "2f40" + 16*"00"
tx_msg(dev[0], hapd, msg)
# Too short PSK-4 (no PCHANNEL)
hapd.note("EAP-PSK: Too short PCHANNEL data in PSK-4 (len=0, expected 21)")
msg = resp[0:4] + "0016" + resp[8:12] + "0016" + "2fc0" + 16*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # PSK-3 retry
stop_psk_assoc(dev[0], hapd)
start_psk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PSK-2
proxy_msg(hapd, dev[0]) # PSK-3
resp = rx_msg(dev[0])
# PCHANNEL Nonce did not increase
hapd.note("EAP-PSK: Nonce did not increase")
msg = resp[0:4] + "002b" + resp[8:12] + "002b" + "2fc0" + 16*"00" + 21*"00"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # PSK-3 retry
stop_psk_assoc(dev[0], hapd)
start_psk_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # PSK-2
proxy_msg(hapd, dev[0]) # PSK-3
resp = rx_msg(dev[0])
# Invalid PCHANNEL encryption
hapd.note("EAP-PSK: PCHANNEL decryption failed")
msg = resp[0:4] + "002b" + resp[8:12] + "002b" + "2fc0" + 16*"00" + 21*"11"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd) # PSK-3 retry
stop_psk_assoc(dev[0], hapd)
EAP_SIM_SUBTYPE_START = 10
EAP_SIM_SUBTYPE_CHALLENGE = 11
EAP_SIM_SUBTYPE_NOTIFICATION = 12
EAP_SIM_SUBTYPE_REAUTHENTICATION = 13
EAP_SIM_SUBTYPE_CLIENT_ERROR = 14
EAP_AKA_SUBTYPE_CHALLENGE = 1
EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT = 2
EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE = 4
EAP_AKA_SUBTYPE_IDENTITY = 5
EAP_AKA_SUBTYPE_NOTIFICATION = 12
EAP_AKA_SUBTYPE_REAUTHENTICATION = 13
EAP_AKA_SUBTYPE_CLIENT_ERROR = 14
EAP_SIM_AT_RAND = 1
EAP_SIM_AT_AUTN = 2
EAP_SIM_AT_RES = 3
EAP_SIM_AT_AUTS = 4
EAP_SIM_AT_PADDING = 6
EAP_SIM_AT_NONCE_MT = 7
EAP_SIM_AT_PERMANENT_ID_REQ = 10
EAP_SIM_AT_MAC = 11
EAP_SIM_AT_NOTIFICATION = 12
EAP_SIM_AT_ANY_ID_REQ = 13
EAP_SIM_AT_IDENTITY = 14
EAP_SIM_AT_VERSION_LIST = 15
EAP_SIM_AT_SELECTED_VERSION = 16
EAP_SIM_AT_FULLAUTH_ID_REQ = 17
EAP_SIM_AT_COUNTER = 19
EAP_SIM_AT_COUNTER_TOO_SMALL = 20
EAP_SIM_AT_NONCE_S = 21
EAP_SIM_AT_CLIENT_ERROR_CODE = 22
EAP_SIM_AT_KDF_INPUT = 23
EAP_SIM_AT_KDF = 24
EAP_SIM_AT_IV = 129
EAP_SIM_AT_ENCR_DATA = 130
EAP_SIM_AT_NEXT_PSEUDONYM = 132
EAP_SIM_AT_NEXT_REAUTH_ID = 133
EAP_SIM_AT_CHECKCODE = 134
EAP_SIM_AT_RESULT_IND = 135
EAP_SIM_AT_BIDDING = 136
def test_eap_proto_aka(dev, apdev):
"""EAP-AKA protocol tests"""
def aka_handler(ctx, req):
logger.info("aka_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_AKA)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown subtype")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, 255, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Client Error")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short attribute header")
return struct.pack(">BBHBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated attribute")
return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short attribute data")
return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Skippable/non-skippable unrecognzized attribute")
return struct.pack(">BBHBBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 10,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
255, 1, 0, 127, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request without ID type")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID (duplicate)")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request PERMANENT_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request PERMANENT_ID (duplicate)")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AKA Challenge with BIDDING")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_BIDDING, 1, 0x8000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification indicating success, but no MAC")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 32768)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification indicating success, but invalid MAC value")
return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 20,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 32768,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification indicating success with zero-key MAC")
return struct.pack(">BBHBBHBBHBBH16B", EAP_CODE_REQUEST,
ctx['id'] - 2,
4 + 1 + 3 + 4 + 20,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 32768,
EAP_SIM_AT_MAC, 5, 0,
0xbe, 0x2e, 0xbb, 0xa9, 0xfa, 0x2e, 0x82, 0x36,
0x37, 0x8c, 0x32, 0x41, 0xb7, 0xc7, 0x58, 0xa3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 16384)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 16385)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification with unrecognized non-failure")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth (duplicate)")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Re-authentication (unexpected) with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AKA Challenge with Checkcode claiming identity round was used")
return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AKA Challenge with Checkcode claiming no identity round was used")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_CHECKCODE, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AKA Challenge with mismatching Checkcode value")
return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Re-authentication (unexpected) with Checkcode claimin identity round was used")
return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
0,
EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_RAND length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_RAND, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_AUTN length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_AUTN, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_PADDING")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_PADDING, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_NONCE_MT length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_NONCE_MT, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_MAC length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_MAC, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_NOTIFICATION length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_NOTIFICATION, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AT_IDENTITY overflow")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_IDENTITY, 1, 0xffff)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_VERSION_LIST")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_VERSION_LIST, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_SELECTED_VERSION length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_SELECTED_VERSION, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_COUNTER")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_COUNTER, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_COUNTER_TOO_SMALL")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_COUNTER_TOO_SMALL, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_NONCE_S")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_NONCE_S, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_CLIENT_ERROR_CODE length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_CLIENT_ERROR_CODE, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_IV length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_IV, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_ENCR_DATA length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_ENCR_DATA, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_NEXT_PSEUDONYM")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_NEXT_PSEUDONYM, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unencrypted AT_NEXT_REAUTH_ID")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_NEXT_REAUTH_ID, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_RES length")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_RES, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_RES length")
return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 24,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_RES, 6, 0xffff, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_AUTS length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_AUTS, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_CHECKCODE length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_CHECKCODE, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_RESULT_IND length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_RESULT_IND, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_KDF_INPUT")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_KDF_INPUT, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_KDF")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_KDF, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_BIDDING length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_BIDDING, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(aka_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 49):
eap = "AKA AKA'" if i == 11 else "AKA"
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap=eap, identity="0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [0, 15]:
time.sleep(0.1)
else:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_aka_prime(dev, apdev):
"""EAP-AKA' protocol tests"""
def aka_prime_handler(ctx, req):
logger.info("aka_prime_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
dev[0].note("Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_AKA_PRIME)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with no attributes")
dev[0].note("Challenge with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with empty AT_KDF_INPUT")
dev[0].note("Challenge with empty AT_KDF_INPUT")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with AT_KDF_INPUT")
dev[0].note("Test: Challenge with AT_KDF_INPUT")
return struct.pack(">BBHBBHBBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'))
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with duplicated KDF")
dev[0].note("Challenge with duplicated KDF")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 2,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with incorrect KDF selected")
dev[0].note("Challenge with incorrect KDF selected")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with selected KDF not duplicated")
dev[0].note("Challenge with selected KDF not duplicated")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with selected KDF duplicated (missing MAC, RAND, AUTN)")
dev[0].note("Challenge with selected KDF duplicated (missing MAC, RAND, AUTN)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple unsupported KDF proposals")
dev[0].note("Challenge with multiple unsupported KDF proposals")
return struct.pack(">BBHBBHBBHBBBBBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 2 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with invalid MAC, RAND, AUTN values)")
dev[0].note("Challenge with invalid MAC, RAND, AUTN values)")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBHBBH4LBBH4LBBH4L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 * 4 + 20 + 20 + 20,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0,
EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
EAP_SIM_AT_AUTN, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge - AMF separation bit not set)")
dev[0].note("Challenge - AMF separation bit not set)")
return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
EAP_SIM_AT_AUTN, 5, 0, 9, 10,
0x2fda8ef7, 0xbba518cc)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge - Invalid MAC")
dev[0].note("Challenge - Invalid MAC")
return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
0xd1f90322, 0x40514cb4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge - Valid MAC")
dev[0].note("Challenge - Valid MAC")
return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_MAC, 5, 0,
0xf4a3c1d3, 0x7c901401, 0x34bd8b01, 0x6f7fa32f,
EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
0xd1f90322, 0x40514cb4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_KDF_INPUT length")
dev[0].note("Invalid AT_KDF_INPUT length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_KDF_INPUT, 2, 0xffff, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid AT_KDF length")
dev[0].note("Invalid AT_KDF length")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
EAP_SIM_AT_KDF, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with large number of KDF proposals")
dev[0].note("Challenge with large number of KDF proposals")
return struct.pack(">BBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 12 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF, 1, 255,
EAP_SIM_AT_KDF, 1, 254,
EAP_SIM_AT_KDF, 1, 253,
EAP_SIM_AT_KDF, 1, 252,
EAP_SIM_AT_KDF, 1, 251,
EAP_SIM_AT_KDF, 1, 250,
EAP_SIM_AT_KDF, 1, 249,
EAP_SIM_AT_KDF, 1, 248,
EAP_SIM_AT_KDF, 1, 247,
EAP_SIM_AT_KDF, 1, 246,
EAP_SIM_AT_KDF, 1, 245,
EAP_SIM_AT_KDF, 1, 244)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 2 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 2,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with an extra KDF appended")
dev[0].note("Challenge with an extra KDF appended")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 2,
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with multiple KDF proposals")
dev[0].note("Challenge with multiple KDF proposals (preparation)")
return struct.pack(">BBHBBHBBHBBBBBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 2 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 2,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge with a modified KDF")
dev[0].note("Challenge with a modified KDF")
return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 3 * 4,
EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
ord('c'), ord('d'),
EAP_SIM_AT_KDF, 1, 1,
EAP_SIM_AT_KDF, 1, 0,
EAP_SIM_AT_KDF, 1, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(aka_prime_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 18):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA'", identity="6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [0]:
time.sleep(0.1)
else:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_sim(dev, apdev):
"""EAP-SIM protocol tests"""
def sim_handler(ctx, req):
logger.info("sim_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_SIM)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_AUTN")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_AUTN, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short AT_VERSION_LIST")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: AT_VERSION_LIST overflow")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 1, 0xffff)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_AUTS")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_AUTS, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected AT_CHECKCODE")
return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_CHECKCODE, 2, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No AT_VERSION_LIST in Start")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No support version in AT_VERSION_LIST")
return struct.pack(">BBHBBHBBH4B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 3, 2, 3, 4, 5)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request without ID type")
return struct.pack(">BBHBBHBBH2H", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID (duplicate)")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request ANY_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_ANY_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request FULLAUTH_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request PERMANENT_ID")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Identity request PERMANENT_ID (duplicate)")
return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 8 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No AT_MAC and AT_RAND in Challenge")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No AT_RAND in Challenge")
return struct.pack(">BBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 20,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Insufficient number of challenges in Challenge")
return struct.pack(">BBHBBHBBH4LBBH4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 20 + 20,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too many challenges in Challenge")
return struct.pack(">BBHBBHBBH4L4L4L4LBBH4L", EAP_CODE_REQUEST,
ctx['id'],
4 + 1 + 3 + 4 + 4 * 16 + 20,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_RAND, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Same RAND multiple times in Challenge")
return struct.pack(">BBHBBHBBH4L4L4LBBH4L", EAP_CODE_REQUEST,
ctx['id'],
4 + 1 + 3 + 4 + 3 * 16 + 20,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
EAP_SIM_AT_RAND, 13, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification indicating success, but no MAC")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 32768)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification indicating success, but invalid MAC value")
return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 20,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 32768,
EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 16384)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 16385)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification with unrecognized non-failure")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Notification before auth (duplicate)")
return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Re-authentication (unexpected) with no attributes")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_REAUTHENTICATION,
0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Client Error")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown subtype")
return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3,
EAP_TYPE_SIM, 255, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(sim_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 25):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [0]:
time.sleep(0.1)
else:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_sim_errors(dev, apdev):
"""EAP-SIM protocol tests (error paths)"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_sim_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with fail_test(dev[0], 1, "os_get_random;eap_sim_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with fail_test(dev[0], 1, "aes_128_cbc_encrypt;eap_sim_response_reauth"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with fail_test(dev[0], 1, "os_get_random;eap_sim_msg_add_encr_start"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with fail_test(dev[0], 1, "os_get_random;eap_sim_init_for_reauth"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
with alloc_fail(dev[0], 1, "eap_sim_parse_encr;eap_sim_process_reauthentication"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [(1, "eap_sim_verify_mac;eap_sim_process_challenge"),
(1, "eap_sim_parse_encr;eap_sim_process_challenge"),
(1, "eap_sim_msg_init;eap_sim_response_start"),
(1, "wpabuf_alloc;eap_sim_msg_init;eap_sim_response_start"),
(1, "=eap_sim_learn_ids"),
(2, "=eap_sim_learn_ids"),
(2, "eap_sim_learn_ids"),
(3, "eap_sim_learn_ids"),
(1, "eap_sim_process_start"),
(1, "eap_sim_getKey"),
(1, "eap_sim_get_emsk"),
(1, "eap_sim_get_session_id")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000@domain",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
erp="1", wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [(1, "aes_128_cbc_decrypt;eap_sim_parse_encr")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="SIM", identity="1232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
params = int_eap_server_params()
params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
params['eap_sim_aka_result_ind'] = "1"
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
with alloc_fail(dev[0], 1,
"eap_sim_msg_init;eap_sim_response_notification"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="SIM", identity="1232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = ["eap_sim_msg_add_encr_start;eap_sim_response_notification",
"aes_128_cbc_encrypt;eap_sim_response_notification"]
for func in tests:
with fail_test(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="SIM", identity="1232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not started on reauthentication")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = ["eap_sim_parse_encr;eap_sim_process_notification_reauth"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="SIM", identity="1232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not started on reauthentication")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_eap_proto_aka_errors(dev, apdev):
"""EAP-AKA protocol tests (error paths)"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_aka_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA", identity="0232010000000000",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "=eap_aka_learn_ids"),
(2, "=eap_aka_learn_ids"),
(1, "eap_sim_parse_encr;eap_aka_process_challenge"),
(1, "wpabuf_alloc;eap_aka_add_id_msg"),
(1, "eap_aka_getKey"),
(1, "eap_aka_get_emsk"),
(1, "eap_aka_get_session_id")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA", identity="0232010000000000@domain",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
erp="1", wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
params = int_eap_server_params()
params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
params['eap_sim_aka_result_ind'] = "1"
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
with alloc_fail(dev[0], 1,
"eap_sim_msg_init;eap_aka_response_notification"):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA", identity="0232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [(1, "aes_128_encrypt_block;milenage_f1;milenage_check", None),
(2, "aes_128_encrypt_block;milenage_f1;milenage_check", None),
(1, "milenage_f2345;milenage_check", None),
(7, "aes_128_encrypt_block;milenage_f2345;milenage_check",
"ff0000000123"),
(1, "aes_128_encrypt_block;milenage_f1;milenage_check",
"fff000000123")]
for count, func, seq in tests:
if not seq:
seq = "000000000123"
with fail_test(dev[0], count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="AKA", identity="0232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:" + seq,
wait_connect=False)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
tests = ["eap_sim_msg_add_encr_start;eap_aka_response_notification",
"aes_128_cbc_encrypt;eap_aka_response_notification"]
for func in tests:
with fail_test(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="AKA", identity="0232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not started on reauthentication")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = ["eap_sim_parse_encr;eap_aka_process_notification_reauth"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="AKA", identity="0232010000000000",
phase1="result_ind=1",
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("EAP method not started on reauthentication")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_eap_proto_aka_prime_errors(dev, apdev):
"""EAP-AKA' protocol tests (error paths)"""
check_hlr_auc_gw_support()
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_aka_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA'", identity="6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA'", identity="6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
with fail_test(dev[0], 1, "aes_128_cbc_encrypt;eap_aka_response_reauth"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA'", identity="6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
with alloc_fail(dev[0], 1, "eap_sim_parse_encr;eap_aka_process_reauthentication"):
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP re-authentication did not start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
tests = [(1, "eap_sim_verify_mac_sha256"),
(1, "=eap_aka_process_challenge")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="AKA'", identity="6555444333222111",
password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
erp="1", wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].dump_monitor()
def test_eap_proto_ikev2(dev, apdev):
"""EAP-IKEv2 protocol tests"""
check_eap_capa(dev[0], "IKEV2")
global eap_proto_ikev2_test_done
eap_proto_ikev2_test_done = False
def ikev2_handler(ctx, req):
logger.info("ikev2_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_IKEV2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated Message Length field")
return struct.pack(">BBHBB3B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 3,
EAP_TYPE_IKEV2, 0x80, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short Message Length value")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_IKEV2, 0x80, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated message")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_IKEV2, 0x80, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated message(2)")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_IKEV2, 0x80, 0xffffffff)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated message(3)")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_IKEV2, 0xc0, 0xffffffff)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated message(4)")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_IKEV2, 0xc0, 10000000)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too long fragments (first fragment)")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_IKEV2, 0xc0, 2, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too long fragments (second fragment)")
return struct.pack(">BBHBB2B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_IKEV2, 0x00, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No Message Length field in first fragment")
return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 1,
EAP_TYPE_IKEV2, 0x40, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: ICV before keys")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_IKEV2, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported IKEv2 header version")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Incorrect IKEv2 header Length")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 0, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected IKEv2 Exchange Type in SA_INIT state")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 0, 0, 0, 28)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected IKEv2 Message ID in SA_INIT state")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 34, 0, 1, 28)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected IKEv2 Flags value")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 34, 0, 0, 28)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected IKEv2 Flags value(2)")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 34, 0x20, 0, 28)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No SAi1 in SA_INIT")
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 28,
EAP_TYPE_IKEV2, 0x00,
0, 0, 0, 0,
0, 0x20, 34, 0x08, 0, 28)
def build_ike(id, next=0, exch_type=34, flags=0x00, ike=b''):
return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, id,
4 + 1 + 1 + 28 + len(ike),
EAP_TYPE_IKEV2, flags,
0, 0, 0, 0,
next, 0x20, exch_type, 0x08, 0,
28 + len(ike)) + ike
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected extra data after payloads")
return build_ike(ctx['id'], ike=struct.pack(">B", 1))
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated payload header")
return build_ike(ctx['id'], next=128, ike=struct.pack(">B", 1))
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too small payload header length")
ike = struct.pack(">BBH", 0, 0, 3)
return build_ike(ctx['id'], next=128, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too large payload header length")
ike = struct.pack(">BBH", 0, 0, 5)
return build_ike(ctx['id'], next=128, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported payload (non-critical and critical)")
ike = struct.pack(">BBHBBH", 129, 0, 4, 0, 0x01, 4)
return build_ike(ctx['id'], next=128, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Certificate and empty SAi1")
ike = struct.pack(">BBHBBH", 33, 0, 4, 0, 0, 4)
return build_ike(ctx['id'], next=37, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short proposal")
ike = struct.pack(">BBHBBHBBB", 0, 0, 4 + 7,
0, 0, 7, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too small proposal length in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 7, 0, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too large proposal length in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 9, 0, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected proposal type in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
1, 0, 8, 0, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Protocol ID in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 8, 0, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected proposal number in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 8, 0, 1, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Not enough room for SPI in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 8, 1, 1, 1, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected SPI in SAi1")
ike = struct.pack(">BBHBBHBBBBB", 0, 0, 4 + 9,
0, 0, 9, 1, 1, 1, 0, 1)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No transforms in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 8, 1, 1, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short transform in SAi1")
ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
0, 0, 8, 1, 1, 0, 1)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too small transform length in SAi1")
ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
0, 0, 8 + 8, 1, 1, 0, 1,
0, 0, 7, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too large transform length in SAi1")
ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
0, 0, 8 + 8, 1, 1, 0, 1,
0, 0, 9, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Transform type in SAi1")
ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
0, 0, 8 + 8, 1, 1, 0, 1,
1, 0, 8, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No transform attributes in SAi1")
ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
0, 0, 8 + 8, 1, 1, 0, 1,
0, 0, 8, 0, 0, 0)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No transform attr for AES and unexpected data after transforms in SAi1")
tlen1 = 8 + 3
tlen2 = 8 + 4
tlen3 = 8 + 4
tlen = tlen1 + tlen2 + tlen3
ike = struct.pack(">BBHBBHBBBBBBHBBH3BBBHBBHHHBBHBBHHHB",
0, 0, 4 + 8 + tlen + 1,
0, 0, 8 + tlen + 1, 1, 1, 0, 3,
3, 0, tlen1, 1, 0, 12, 1, 2, 3,
3, 0, tlen2, 1, 0, 12, 0, 128,
0, 0, tlen3, 1, 0, 12, 0x8000 | 14, 127,
1)
return build_ike(ctx['id'], next=33, ike=ike)
def build_sa(next=0):
tlen = 5 * 8
return struct.pack(">BBHBBHBBBBBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
next, 0, 4 + 8 + tlen,
0, 0, 8 + tlen, 1, 1, 0, 5,
3, 0, 8, 1, 0, 3,
3, 0, 8, 2, 0, 1,
3, 0, 8, 3, 0, 1,
3, 0, 8, 4, 0, 5,
0, 0, 8, 241, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal, but no KEi in SAi1")
ike = build_sa()
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Empty KEi in SAi1")
ike = build_sa(next=34) + struct.pack(">BBH", 0, 0, 4)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Mismatch in DH Group in SAi1")
ike = build_sa(next=34)
ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 12345, 0)
ike += 96*b'\x00'
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid DH public value length in SAi1")
ike = build_sa(next=34)
ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 5, 0)
ike += 96*b'\x00'
return build_ike(ctx['id'], next=33, ike=ike)
def build_ke(next=0):
ke = struct.pack(">BBHHH", next, 0, 4 + 4 + 192, 5, 0)
ke += 191*b'\x00'+b'\x02'
return ke
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal and KEi, but no Ni in SAi1")
ike = build_sa(next=34)
ike += build_ke()
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short Ni in SAi1")
ike = build_sa(next=34)
ike += build_ke(next=40)
ike += struct.pack(">BBH", 0, 0, 4)
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too long Ni in SAi1")
ike = build_sa(next=34)
ike += build_ke(next=40)
ike += struct.pack(">BBH", 0, 0, 4 + 257) + 257*b'\x00'
return build_ike(ctx['id'], next=33, ike=ike)
def build_ni(next=0):
return struct.pack(">BBH", next, 0, 4 + 256) + 256*b'\x00'
def build_sai1(id):
ike = build_sa(next=34)
ike += build_ke(next=40)
ike += build_ni()
return build_ike(ctx['id'], next=33, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
return build_sai1(ctx['id'])
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
return build_sai1(ctx['id'])
idx += 1
if ctx['num'] == idx:
logger.info("Test: No integrity checksum")
ike = b''
return build_ike(ctx['id'], next=37, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
return build_sai1(ctx['id'])
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated integrity checksum")
return struct.pack(">BBHBB",
EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_IKEV2, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
return build_sai1(ctx['id'])
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid integrity checksum")
ike = b''
return build_ike(ctx['id'], next=37, flags=0x20, ike=ike)
idx += 1
if ctx['num'] == idx:
logger.info("No more test responses available - test case completed")
global eap_proto_ikev2_test_done
eap_proto_ikev2_test_done = True
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_IKEV2)
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(ikev2_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_ikev2_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="user",
password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP method start")
if i in [41, 46]:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
else:
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[1].dump_monitor()
dev[2].dump_monitor()
finally:
stop_radius_server(srv)
def NtPasswordHash(password):
pw = password.encode('utf_16_le')
return hashlib.new('md4', pw).digest()
def HashNtPasswordHash(password_hash):
return hashlib.new('md4', password_hash).digest()
def ChallengeHash(peer_challenge, auth_challenge, username):
data = peer_challenge + auth_challenge + username
return hashlib.sha1(data).digest()[0:8]
def GenerateAuthenticatorResponse(password, nt_response, peer_challenge,
auth_challenge, username):
magic1 = binascii.unhexlify("4D616769632073657276657220746F20636C69656E74207369676E696E6720636F6E7374616E74")
magic2 = binascii.unhexlify("50616420746F206D616B6520697420646F206D6F7265207468616E206F6E6520697465726174696F6E")
password_hash = NtPasswordHash(password)
password_hash_hash = HashNtPasswordHash(password_hash)
data = password_hash_hash + nt_response + magic1
digest = hashlib.sha1(data).digest()
challenge = ChallengeHash(peer_challenge, auth_challenge, username.encode())
data = digest + challenge + magic2
resp = hashlib.sha1(data).digest()
return resp
def test_eap_proto_ikev2_errors(dev, apdev):
"""EAP-IKEv2 local error cases"""
check_eap_capa(dev[0], "IKEV2")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_ikev2_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="ike password",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "ikev2_encr_encrypt"),
(1, "ikev2_encr_decrypt"),
(1, "ikev2_derive_auth_data"),
(2, "ikev2_derive_auth_data"),
(1, "=ikev2_decrypt_payload"),
(1, "ikev2_encr_decrypt;ikev2_decrypt_payload"),
(1, "ikev2_encr_encrypt;ikev2_build_encrypted"),
(1, "ikev2_derive_sk_keys"),
(2, "ikev2_derive_sk_keys"),
(3, "ikev2_derive_sk_keys"),
(4, "ikev2_derive_sk_keys"),
(5, "ikev2_derive_sk_keys"),
(6, "ikev2_derive_sk_keys"),
(7, "ikev2_derive_sk_keys"),
(8, "ikev2_derive_sk_keys"),
(1, "eap_ikev2_derive_keymat;eap_ikev2_peer_keymat"),
(1, "eap_msg_alloc;eap_ikev2_build_msg"),
(1, "eap_ikev2_getKey"),
(1, "eap_ikev2_get_emsk"),
(1, "eap_ikev2_get_session_id"),
(1, "=ikev2_derive_keys"),
(2, "=ikev2_derive_keys"),
(1, "wpabuf_alloc;ikev2_process_kei"),
(1, "=ikev2_process_idi"),
(1, "ikev2_derive_auth_data;ikev2_build_auth"),
(1, "wpabuf_alloc;ikev2_build_sa_init"),
(2, "wpabuf_alloc;ikev2_build_sa_init"),
(3, "wpabuf_alloc;ikev2_build_sa_init"),
(4, "wpabuf_alloc;ikev2_build_sa_init"),
(5, "wpabuf_alloc;ikev2_build_sa_init"),
(6, "wpabuf_alloc;ikev2_build_sa_init"),
(1, "wpabuf_alloc;ikev2_build_sa_auth"),
(2, "wpabuf_alloc;ikev2_build_sa_auth"),
(1, "ikev2_build_auth;ikev2_build_sa_auth")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user@domain",
password="ike password", erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "wpabuf_alloc;ikev2_build_notify"),
(2, "wpabuf_alloc;ikev2_build_notify"),
(1, "ikev2_build_encrypted;ikev2_build_notify")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="wrong password", erp="1",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "ikev2_integ_hash"),
(1, "ikev2_integ_hash;ikev2_decrypt_payload"),
(1, "os_get_random;ikev2_build_encrypted"),
(1, "ikev2_prf_plus;ikev2_derive_sk_keys"),
(1, "eap_ikev2_derive_keymat;eap_ikev2_peer_keymat"),
(1, "os_get_random;ikev2_build_sa_init"),
(2, "os_get_random;ikev2_build_sa_init"),
(1, "ikev2_integ_hash;eap_ikev2_validate_icv"),
(1, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_keys"),
(1, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data"),
(2, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data"),
(3, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="ike password", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
params = {"ssid": "eap-test2", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
"fragment_size": "50"}
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
tests = [(1, "eap_ikev2_build_frag_ack"),
(1, "wpabuf_alloc;eap_ikev2_process_fragment")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test2", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="ike password", erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen for %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def run_eap_ikev2_connect(dev):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="ike password",
fragment_size="30", wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_ikev2_errors_server(dev, apdev):
"""EAP-IKEV2 local error cases on server"""
check_eap_capa(dev[0], "IKEV2")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_ikev2_init"),
(2, "=eap_ikev2_init"),
(3, "=eap_ikev2_init"),
(1, "eap_msg_alloc;eap_ikev2_build_msg"),
(1, "ikev2_initiator_build;eap_ikev2_buildReq"),
(1, "eap_ikev2_process_fragment"),
(1, "wpabuf_alloc_copy;ikev2_process_ker"),
(1, "ikev2_process_idr"),
(1, "ikev2_derive_auth_data;ikev2_process_auth_secret"),
(1, "ikev2_decrypt_payload;ikev2_process_sa_auth"),
(1, "ikev2_process_sa_auth_decrypted;ikev2_process_sa_auth"),
(1, "dh_init;ikev2_build_kei"),
(1, "ikev2_build_auth"),
(1, "wpabuf_alloc;ikev2_build_sa_init"),
(1, "ikev2_build_sa_auth"),
(1, "=ikev2_build_sa_auth"),
(2, "=ikev2_derive_auth_data"),
(1, "wpabuf_alloc;ikev2_build_sa_auth"),
(2, "wpabuf_alloc;=ikev2_build_sa_auth"),
(1, "ikev2_decrypt_payload;ikev2_process_sa_init_encr"),
(1, "dh_derive_shared;ikev2_derive_keys"),
(1, "=ikev2_derive_keys"),
(2, "=ikev2_derive_keys"),
(1, "eap_ikev2_getKey"),
(1, "eap_ikev2_get_emsk"),
(1, "eap_ikev2_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_ikev2_connect(dev[0])
tests = [(1, "eap_ikev2_validate_icv;eap_ikev2_process_icv"),
(1, "eap_ikev2_server_keymat"),
(1, "ikev2_build_auth"),
(1, "os_get_random;ikev2_build_sa_init"),
(2, "os_get_random;ikev2_build_sa_init"),
(1, "ikev2_derive_keys"),
(2, "ikev2_derive_keys"),
(3, "ikev2_derive_keys"),
(4, "ikev2_derive_keys"),
(5, "ikev2_derive_keys"),
(6, "ikev2_derive_keys"),
(7, "ikev2_derive_keys"),
(8, "ikev2_derive_keys"),
(1, "ikev2_decrypt_payload;ikev2_process_sa_auth"),
(1, "eap_ikev2_process_icv;eap_ikev2_process")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_ikev2_connect(dev[0])
def start_ikev2_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
eap="IKEV2", identity="ikev2 user",
password="ike password", wait_connect=False)
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # IKEV2 1
def stop_ikev2_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_ikev2_server(dev, apdev):
"""EAP-IKEV2 protocol testing for the server"""
check_eap_capa(dev[0], "IKEV2")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
# Successful exchange to verify proxying mechanism
start_ikev2_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # IKEV2 2
proxy_msg(hapd, dev[0]) # IKEV2 3
proxy_msg(dev[0], hapd) # IKEV2 4
proxy_msg(hapd, dev[0]) # EAP-Success
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
dev[0].wait_connected()
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-IKEV2 header
hapd.note("IKEV2: Too short frame to include HDR")
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "31"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-IKEV2 header - missing Message Length field
hapd.note("EAP-IKEV2: Message underflow")
msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3180"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-IKEV2 header - too small Message Length
hapd.note("EAP-IKEV2: Invalid Message Length (0; 1 remaining in this msg)")
msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "318000000000ff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short EAP-IKEV2 header - too large Message Length
hapd.note("EAP-IKEV2: Ignore too long message")
msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "31c0bbccddeeff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# No Message Length in first fragment
hapd.note("EAP-IKEV2: No Message Length field in a fragmented packet")
msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "3140ff"
tx_msg(dev[0], hapd, msg)
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# First fragment (valid)
hapd.note("EAP-IKEV2: Received 1 bytes in first fragment, waiting for 255 bytes more")
msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "31c000000100ff"
tx_msg(dev[0], hapd, msg)
req = rx_msg(hapd)
id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
hapd.note("EAP-IKEV2: Received 1 bytes in first fragment, waiting for 254 bytes more")
payload = struct.pack('BBB', 49, 0x40, 0)
msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
req = rx_msg(hapd)
id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
hapd.note("EAP-IKEV2: Fragment overflow")
payload = struct.pack('BB', 49, 0x40) + 255*b'\x00'
msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
start_ikev2_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # IKEV2 2
req = proxy_msg(hapd, dev[0]) # IKEV2 3
id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
# Missing ICV
hapd.note("EAP-IKEV2: The message should have included integrity checksum")
payload = struct.pack('BB', 49, 0) + b'\x00'
msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
tests = [("Unsupported HDR version 0x0 (expected 0x20)",
struct.pack('BB', 49, 0) + 16*b'\x00' +
struct.pack('>BBBBLL', 0, 0, 0, 0, 0, 0)),
("IKEV2: Invalid length (HDR: 0 != RX: 28)",
struct.pack('BB', 49, 0) + 16*b'\x00' +
struct.pack('>BBBBLL', 0, 0x20, 0, 0, 0, 0)),
("IKEV2: Unexpected Exchange Type 0 in SA_INIT state",
struct.pack('BB', 49, 0) + 16*b'\x00' +
struct.pack('>BBBBLL', 0, 0x20, 0, 0, 0, 28)),
("IKEV2: Unexpected Flags value 0x0",
struct.pack('BB', 49, 0) + 16*b'\x00' +
struct.pack('>BBBBLL', 0, 0x20, 34, 0, 0, 28)),
("IKEV2: SAr1 not received",
struct.pack('BB', 49, 0) + 16*b'\x00' +
struct.pack('>BBBBLL', 0, 0x20, 34, 0x20, 0, 28))]
for txt, payload in tests:
start_ikev2_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
id, = struct.unpack('B', binascii.unhexlify(resp)[5:6])
hapd.note(txt)
msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
rx_msg(hapd)
stop_ikev2_assoc(dev[0], hapd)
def test_eap_proto_mschapv2(dev, apdev):
"""EAP-MSCHAPv2 protocol tests"""
check_eap_capa(dev[0], "MSCHAPV2")
def mschapv2_handler(ctx, req):
logger.info("mschapv2_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_MSCHAPV2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unknown MSCHAPv2 op_code")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
0, 0, 5, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid ms_len and unknown MSCHAPv2 op_code")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
255, 0, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success before challenge")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
3, 0, 5, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure before challenge - required challenge field not present")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
4, 0, 5, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure before challenge - invalid failure challenge len")
payload = b'C=12'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure before challenge - invalid failure challenge len")
payload = b'C=12 V=3'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure before challenge - invalid failure challenge")
payload = b'C=00112233445566778899aabbccddeefQ '
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure before challenge - password expired")
payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success after password change")
payload = b"S=1122334455667788990011223344556677889900"
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
3, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid challenge length")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short challenge packet")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1, 16)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1 + 16 + 6,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure - password expired")
payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success after password change")
if len(req) != 591:
logger.info("Unexpected Change-Password packet length: %s" % len(req))
return None
data = req[9:]
enc_pw = data[0:516]
data = data[516:]
enc_hash = data[0:16]
data = data[16:]
peer_challenge = data[0:16]
data = data[16:]
# Reserved
data = data[8:]
nt_response = data[0:24]
data = data[24:]
flags = data
logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
logger.info("flags: " + binascii.hexlify(flags).decode())
auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
peer_challenge,
auth_challenge, "user")
payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
logger.info("Success message payload: " + payload.decode())
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
3, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure - password expired")
payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Success after password change")
if len(req) != 591:
logger.info("Unexpected Change-Password packet length: %s" % len(req))
return None
data = req[9:]
enc_pw = data[0:516]
data = data[516:]
enc_hash = data[0:16]
data = data[16:]
peer_challenge = data[0:16]
data = data[16:]
# Reserved
data = data[8:]
nt_response = data[0:24]
data = data[24:]
flags = data
logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
logger.info("flags: " + binascii.hexlify(flags).decode())
auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
peer_challenge,
auth_challenge, "user")
payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
logger.info("Success message payload: " + payload.decode())
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
3, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1 + 16 + 6,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure - authentication failure")
payload = b'E=691 R=1 C=00112233445566778899aabbccddeeff V=3 M=Authentication failed'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1 + 16 + 6,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure - authentication failure")
payload = b'E=691 R=1 C=00112233445566778899aabbccddeeff V=3 M=Authentication failed (2)'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Challenge - invalid ms_len and workaround disabled")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1 + 16 + 6,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1 + 16 + 6 + 1, 16) + 16*b'A' + b'foobar'
return None
srv = start_radius_server(mschapv2_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(0, 16):
logger.info("RUN: %d" % i)
if i == 12:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
elif i == 14:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
phase2="mschapv2_retry=0",
password="password", wait_connect=False)
elif i == 15:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
eap_workaround="0",
password="password", wait_connect=False)
else:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password="password", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [8, 11, 12]:
ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"],
timeout=10)
if ev is None:
raise Exception("Timeout on new password request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
if i in [11, 12]:
ev = dev[0].wait_event(["CTRL-EVENT-PASSWORD-CHANGED"],
timeout=10)
if ev is None:
raise Exception("Timeout on password change")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP success")
else:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
if i in [13]:
ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"],
timeout=10)
if ev is None:
raise Exception("Timeout on identity request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-IDENTITY-" + id + ":user")
ev = dev[0].wait_event(["CTRL-REQ-PASSWORD"],
timeout=10)
if ev is None:
raise Exception("Timeout on password request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-PASSWORD-" + id + ":password")
# TODO: Does this work correctly?
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
if i in [4, 5, 6, 7, 14]:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
timeout=10)
if ev is None:
raise Exception("Timeout on EAP failure")
else:
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
finally:
stop_radius_server(srv)
def test_eap_proto_mschapv2_errors(dev, apdev):
"""EAP-MSCHAPv2 protocol tests (error paths)"""
check_eap_capa(dev[0], "MSCHAPV2")
def mschapv2_fail_password_expired(ctx):
logger.info("Test: Failure before challenge - password expired")
payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
4, 0, 4 + len(payload)) + payload
def mschapv2_success_after_password_change(ctx, req=None):
logger.info("Test: Success after password change")
if req is None or len(req) != 591:
payload = b"S=1122334455667788990011223344556677889900"
else:
data = req[9:]
enc_pw = data[0:516]
data = data[516:]
enc_hash = data[0:16]
data = data[16:]
peer_challenge = data[0:16]
data = data[16:]
# Reserved
data = data[8:]
nt_response = data[0:24]
data = data[24:]
flags = data
logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
logger.info("flags: " + binascii.hexlify(flags).decode())
auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
peer_challenge,
auth_challenge, "user")
payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + len(payload),
EAP_TYPE_MSCHAPV2,
3, 0, 4 + len(payload)) + payload
def mschapv2_handler(ctx, req):
logger.info("mschapv2_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
return mschapv2_fail_password_expired(ctx)
idx += 1
if ctx['num'] == idx:
return mschapv2_success_after_password_change(ctx, req)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return None
srv = start_radius_server(mschapv2_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = ["os_get_random;eap_mschapv2_change_password",
"generate_nt_response;eap_mschapv2_change_password",
"get_master_key;eap_mschapv2_change_password",
"nt_password_hash;eap_mschapv2_change_password",
"old_nt_password_hash_encrypted_with_new_nt_password_hash"]
for func in tests:
with fail_test(dev[0], 1, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password="password", wait_connect=False)
ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
if ev is None:
raise Exception("Timeout on new password request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
tests = ["encrypt_pw_block_with_password_hash;eap_mschapv2_change_password",
"nt_password_hash;eap_mschapv2_change_password",
"nt_password_hash;eap_mschapv2_success"]
for func in tests:
with fail_test(dev[0], 1, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
if ev is None:
raise Exception("Timeout on new password request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
tests = ["eap_msg_alloc;eap_mschapv2_change_password"]
for func in tests:
with alloc_fail(dev[0], 1, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password="password", wait_connect=False)
ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
if ev is None:
raise Exception("Timeout on new password request")
id = ev.split(':')[0].split('-')[-1]
dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
time.sleep(0.1)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
finally:
stop_radius_server(srv)
def test_eap_proto_pwd(dev, apdev):
"""EAP-pwd protocol tests"""
check_eap_capa(dev[0], "PWD")
global eap_proto_pwd_test_done, eap_proto_pwd_test_wait
eap_proto_pwd_test_done = False
eap_proto_pwd_test_wait = False
def pwd_handler(ctx, req):
logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
global eap_proto_pwd_test_wait
eap_proto_pwd_test_wait = False
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing payload")
# EAP-pwd: Got a frame but pos is not NULL and len is 0
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'], 4 + 1,
EAP_TYPE_PWD)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing Total-Length field")
# EAP-pwd: Frame too short to contain Total-Length field
payload = struct.pack("B", 0x80)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too large Total-Length")
# EAP-pwd: Incoming fragments whose total length = 65535
payload = struct.pack(">BH", 0x80, 65535)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: First fragment")
# EAP-pwd: Incoming fragments whose total length = 10
# EAP-pwd: ACKing a 0 byte fragment
payload = struct.pack(">BH", 0xc0, 10)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Total-Length value in the second fragment")
# EAP-pwd: Incoming fragments whose total length = 0
# EAP-pwd: Unexpected new fragment start when previous fragment is still in use
payload = struct.pack(">BH", 0x80, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: First and only fragment")
# EAP-pwd: Incoming fragments whose total length = 0
# EAP-pwd: processing frame: exch 0, len 0
# EAP-pwd: Ignoring message with unknown opcode 128
payload = struct.pack(">BH", 0x80, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: First and only fragment with extra data")
# EAP-pwd: Incoming fragments whose total length = 0
# EAP-pwd: processing frame: exch 0, len 1
# EAP-pwd: Ignoring message with unknown opcode 128
payload = struct.pack(">BHB", 0x80, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: First fragment")
# EAP-pwd: Incoming fragments whose total length = 2
# EAP-pwd: ACKing a 1 byte fragment
payload = struct.pack(">BHB", 0xc0, 2, 1)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Extra data in the second fragment")
# EAP-pwd: Buffer overflow attack detected (3 vs. 1)!
payload = struct.pack(">BBB", 0x0, 2, 3)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short id exchange")
# EAP-pwd: processing frame: exch 1, len 0
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">B", 0x01)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported rand func in id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=0 random=0 prf=0 prep=0
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">BHBBLB", 0x01, 0, 0, 0, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported prf in id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=0 prep=0
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 0, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported password pre-processing technique in id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=255
# EAP-PWD: Unsupported password pre-processing technique (Prep=255)
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 255)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected id exchange")
# EAP-pwd: processing frame: exch 1, len 9
# EAP-PWD: PWD-Commit-Req -> FAILURE
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected commit exchange")
# EAP-pwd: processing frame: exch 2, len 0
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=None)")
# EAP-pwd commit request, password prep is NONE
# EAP-pwd: Unexpected Commit payload length 0 (expected 96)
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Commit payload with all zeros values --> Shared key at infinity")
# EAP-pwd: Invalid coordinate in element
payload = struct.pack(">B", 0x02) + 96*b'\0'
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Commit payload with valid values")
# EAP-pwd commit request, password prep is NONE
element = binascii.unhexlify("8dcab2862c5396839a6bac0c689ff03d962863108e7c275bbf1d6eedf634ee832a214db99f0d0a1a6317733eecdd97f0fc4cda19f57e1bb9bb9c8dcf8c60ba6f")
scalar = binascii.unhexlify("450f31e058cf2ac2636a5d6e2b3c70b1fcc301957f0716e77f13aa69f9a2e5bd")
payload = struct.pack(">B", 0x02) + element + scalar
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Confirm payload length 0")
# EAP-pwd: Unexpected Confirm payload length 0 (expected 32)
payload = struct.pack(">B", 0x03)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Commit payload with valid values")
# EAP-pwd commit request, password prep is NONE
element = binascii.unhexlify("8dcab2862c5396839a6bac0c689ff03d962863108e7c275bbf1d6eedf634ee832a214db99f0d0a1a6317733eecdd97f0fc4cda19f57e1bb9bb9c8dcf8c60ba6f")
scalar = binascii.unhexlify("450f31e058cf2ac2636a5d6e2b3c70b1fcc301957f0716e77f13aa69f9a2e5bd")
payload = struct.pack(">B", 0x02) + element + scalar
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Confirm payload with incorrect value")
# EAP-PWD (peer): confirm did not verify
payload = struct.pack(">B", 0x03) + 32*b'\0'
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected confirm exchange")
# EAP-pwd: processing frame: exch 3, len 0
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">B", 0x03)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unsupported password pre-processing technique SASLprep in id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=2
# EAP-PWD: Unsupported password pre-processing technique (Prep=2)
# EAP-PWD: PWD-ID-Req -> FAILURE
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 2)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=1
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 1)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=MS)")
# EAP-pwd commit request, password prep is MS
# EAP-pwd: Unexpected Commit payload length 0 (expected 96)
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
# EAP-pwd commit request, password prep is salted sha1
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
# EAP-pwd commit request, password prep is salted sha1
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">BB", 0x02, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
# EAP-pwd commit request, password prep is salted sha1
# EAP-pwd: Unexpected Commit payload length 1 (expected 98)
payload = struct.pack(">BB", 0x02, 1)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
# EAP-pwd commit request, password prep is salted sha256
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
# EAP-pwd commit request, password prep is salted sha256
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">BB", 0x02, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
# EAP-pwd commit request, password prep is salted sha256
# EAP-pwd: Unexpected Commit payload length 1 (expected 98)
payload = struct.pack(">BB", 0x02, 1)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
# EAP-pwd commit request, password prep is salted sha512
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">B", 0x02)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
# EAP-pwd commit request, password prep is salted sha512
# EAP-pwd: Invalid Salt-len
payload = struct.pack(">BB", 0x02, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
eap_proto_pwd_test_wait = True
logger.info("Test: Valid id exchange")
# EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
# EAP-pwd commit request, password prep is salted sha512
# EAP-pwd: Unexpected Commit payload length 1 (expected 98)
payload = struct.pack(">BB", 0x02, 1)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
logger.info("No more test responses available - test case completed")
global eap_proto_pwd_test_done
eap_proto_pwd_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(pwd_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_pwd_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ok = False
for j in range(5):
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
"CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
if "CTRL-EVENT-EAP-PROPOSED-METHOD" in ev:
ok = True
break
if "CTRL-EVENT-EAP-STATUS" in ev and "status='completion' parameter='failure'" in ev:
ok = True
break
if not ok:
raise Exception("Expected EAP event not seen")
if eap_proto_pwd_test_wait:
for k in range(20):
time.sleep(0.1)
if not eap_proto_pwd_test_wait:
break
if eap_proto_pwd_test_wait:
raise Exception("eap_proto_pwd_test_wait not cleared")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_pwd_invalid_scalar(dev, apdev):
"""EAP-pwd protocol tests - invalid server scalar"""
check_eap_capa(dev[0], "PWD")
run_eap_proto_pwd_invalid_scalar(dev, apdev, 32*b'\0')
run_eap_proto_pwd_invalid_scalar(dev, apdev, 31*b'\0' + b'\x01')
# Group Order
val = binascii.unhexlify("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")
run_eap_proto_pwd_invalid_scalar(dev, apdev, val)
# Group Order - 1
val = binascii.unhexlify("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550")
run_eap_proto_pwd_invalid_scalar(dev, apdev, val, valid_scalar=True)
def run_eap_proto_pwd_invalid_scalar(dev, apdev, scalar, valid_scalar=False):
global eap_proto_pwd_invalid_scalar_fail
eap_proto_pwd_invalid_scalar_fail = False
def pwd_handler(ctx, req):
logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid id exchange")
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Commit payload with invalid scalar")
payload = struct.pack(">B", 0x02) + binascii.unhexlify("67feb2b46d59e6dd3af3a429ec9c04a949337564615d3a2c19bdf6826eb6f5efa303aed86af3a072ed819d518d620adb2659f0e84c4f8b739629db8c93088cfc") + scalar
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Confirm message next - should not get here")
global eap_proto_pwd_invalid_scalar_fail
eap_proto_pwd_invalid_scalar_fail = True
payload = struct.pack(">B", 0x03) + 32*b'\0'
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
logger.info("No more test responses available - test case completed")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(pwd_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
if valid_scalar and not eap_proto_pwd_invalid_scalar_fail:
raise Exception("Peer did not accept valid EAP-pwd-Commit scalar")
if not valid_scalar and eap_proto_pwd_invalid_scalar_fail:
raise Exception("Peer did not stop after invalid EAP-pwd-Commit scalar")
def test_eap_proto_pwd_invalid_element(dev, apdev):
"""EAP-pwd protocol tests - invalid server element"""
check_eap_capa(dev[0], "PWD")
# Invalid x,y coordinates
run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\x00')
run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x00' + 32*b'\x01')
run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x01' + 32*b'\x00')
run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\xff' + 32*b'\x01')
run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x01' + 32*b'\xff')
run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\xff')
# Not on curve
run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\x01')
def run_eap_proto_pwd_invalid_element(dev, apdev, element):
global eap_proto_pwd_invalid_element_fail
eap_proto_pwd_invalid_element_fail = False
def pwd_handler(ctx, req):
logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid id exchange")
payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Commit payload with invalid element")
payload = struct.pack(">B", 0x02) + element + 31*b'\0' + b'\x02'
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Confirm message next - should not get here")
global eap_proto_pwd_invalid_element_fail
eap_proto_pwd_invalid_element_fail = True
payload = struct.pack(">B", 0x03) + 32*b'\0'
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + len(payload), EAP_TYPE_PWD) + payload
logger.info("No more test responses available - test case completed")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(pwd_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("EAP failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
if eap_proto_pwd_invalid_element_fail:
raise Exception("Peer did not stop after invalid EAP-pwd-Commit element")
def rx_msg(src):
ev = src.wait_event(["EAPOL-TX"], timeout=5)
if ev is None:
raise Exception("No EAPOL-TX")
return ev.split(' ')[2]
def tx_msg(src, dst, msg):
dst.request("EAPOL_RX " + src.own_addr() + " " + msg)
def proxy_msg(src, dst):
msg = rx_msg(src)
tx_msg(src, dst, msg)
return msg
def start_pwd_exchange(dev, ap):
check_eap_capa(dev, "PWD")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(ap, params)
hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="PWD", identity="pwd user", password="secret password",
wait_connect=False, scan_freq="2412")
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # EAP-pwd-ID/Request
proxy_msg(dev, hapd) # EAP-pwd-ID/Response
return hapd
def test_eap_proto_pwd_unexpected_fragment(dev, apdev):
"""EAP-pwd protocol tests - unexpected more-fragment frame"""
hapd = start_pwd_exchange(dev[0], apdev[0])
# EAP-pwd-Commit/Request
req = rx_msg(hapd)
if req[18:20] != "02":
raise Exception("Unexpected EAP-pwd-Commit/Request flag")
msg = req[0:18] + "42" + req[20:]
tx_msg(hapd, dev[0], msg)
def test_eap_proto_pwd_reflection_attack(dev, apdev):
"""EAP-pwd protocol tests - reflection attack on the server"""
hapd = start_pwd_exchange(dev[0], apdev[0])
# EAP-pwd-Commit/Request
req = proxy_msg(hapd, dev[0])
if len(req) != 212:
raise Exception("Unexpected EAP-pwd-Commit/Response length")
# EAP-pwd-Commit/Response
resp = rx_msg(dev[0])
# Reflect same Element/Scalar back to the server
msg = resp[0:20] + req[20:]
tx_msg(dev[0], hapd, msg)
# EAP-pwd-Commit/Response or EAP-Failure
req = rx_msg(hapd)
if req[8:10] != "04":
# reflect EAP-pwd-Confirm/Request
msg = req[0:8] + "02" + req[10:]
tx_msg(dev[0], hapd, msg)
req = rx_msg(hapd)
if req[8:10] == "03":
raise Exception("EAP-Success after reflected Element/Scalar")
raise Exception("No EAP-Failure to reject invalid EAP-pwd-Commit/Response")
def test_eap_proto_pwd_invalid_scalar_peer(dev, apdev):
"""EAP-pwd protocol tests - invalid peer scalar"""
run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, 32*"00")
run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, 31*"00" + "01")
# Group Order
run_eap_proto_pwd_invalid_scalar_peer(dev, apdev,
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")
# Group Order - 1
run_eap_proto_pwd_invalid_scalar_peer(dev, apdev,
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550",
valid_scalar=True)
def run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, scalar,
valid_scalar=False):
hapd = start_pwd_exchange(dev[0], apdev[0])
proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
# EAP-pwd-Commit/Response
resp = rx_msg(dev[0])
# Replace scalar with an invalid value
msg = resp[0:20] + resp[20:148] + scalar
tx_msg(dev[0], hapd, msg)
# EAP-pwd-Commit/Response or EAP-Failure
req = rx_msg(hapd)
if valid_scalar and req[8:10] == "04":
raise Exception("Unexpected EAP-Failure with valid scalar")
if not valid_scalar and req[8:10] != "04":
raise Exception("No EAP-Failure to reject invalid scalar")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
hapd.disable()
def test_eap_proto_pwd_invalid_element_peer(dev, apdev):
"""EAP-pwd protocol tests - invalid peer element"""
# Invalid x,y coordinates
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'00')
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'00' + 32*'01')
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'01' + 32*'00')
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'ff' + 32*'01')
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'01' + 32*'ff')
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'ff')
# Not on curve
run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'01')
def run_eap_proto_pwd_invalid_element_peer(dev, apdev, element):
hapd = start_pwd_exchange(dev[0], apdev[0])
proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
# EAP-pwd-Commit/Response
resp = rx_msg(dev[0])
# Replace element with an invalid value
msg = resp[0:20] + element + resp[148:]
tx_msg(dev[0], hapd, msg)
# EAP-pwd-Commit/Response or EAP-Failure
req = rx_msg(hapd)
if req[8:10] != "04":
raise Exception("No EAP-Failure to reject invalid element")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
hapd.disable()
def test_eap_proto_pwd_errors(dev, apdev):
"""EAP-pwd local error cases"""
check_eap_capa(dev[0], "PWD")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 4):
with alloc_fail(dev[0], i, "eap_pwd_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "eap_pwd_get_session_id"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
fragment_size="0",
password="secret password")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
funcs = ["eap_pwd_getkey", "eap_pwd_get_emsk",
"=wpabuf_alloc;eap_pwd_perform_commit_exchange",
"=wpabuf_alloc;eap_pwd_perform_confirm_exchange"]
for func in funcs:
with alloc_fail(dev[0], 1, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user@domain",
password="secret password", erp="1",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_pwd_perform_id_exchange"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1, "wpabuf_alloc;eap_pwd_perform_id_exchange"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 9):
with alloc_fail(dev[0], i, "eap_pwd_perform_commit_exchange"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 12):
with alloc_fail(dev[0], i, "eap_pwd_perform_confirm_exchange"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
ok = False
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
ok = True
break
time.sleep(0.1)
if not ok:
raise Exception("No allocation failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_msg_alloc;=eap_pwd_process"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password", fragment_size="50",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
# No password configured
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=52"],
timeout=15)
if ev is None:
raise Exception("EAP-pwd not started")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
funcs = [(1, "hash_nt_password_hash;eap_pwd_perform_commit_exchange"),
(1, "=crypto_bignum_init;eap_pwd_perform_commit_exchange"),
(1, "=crypto_ec_point_init;eap_pwd_perform_commit_exchange"),
(2, "=crypto_ec_point_init;eap_pwd_perform_commit_exchange"),
(1, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
(2, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
(3, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
(1, "=crypto_ec_point_add;eap_pwd_perform_commit_exchange"),
(1, "=crypto_ec_point_invert;eap_pwd_perform_commit_exchange"),
(1, "=crypto_ec_point_to_bin;eap_pwd_perform_commit_exchange"),
(1, "crypto_hash_finish;eap_pwd_kdf"),
(1, "crypto_ec_point_from_bin;eap_pwd_get_element"),
(3, "crypto_bignum_init;compute_password_element"),
(4, "crypto_bignum_init;compute_password_element"),
(1, "crypto_bignum_init_set;compute_password_element"),
(2, "crypto_bignum_init_set;compute_password_element"),
(3, "crypto_bignum_init_set;compute_password_element"),
(1, "crypto_bignum_to_bin;compute_password_element"),
(1, "crypto_ec_point_compute_y_sqr;compute_password_element"),
(1, "crypto_ec_point_solve_y_coord;compute_password_element"),
(1, "crypto_bignum_rand;compute_password_element"),
(1, "crypto_bignum_sub;compute_password_element")]
for count, func in funcs:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd-hash",
password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP-Failure reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
params = {"ssid": "eap-test2", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
"rsn_pairwise": "CCMP", "ieee8021x": "1",
"eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
"pwd_group": "19", "fragment_size": "40"}
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "wpabuf_alloc;=eap_pwd_process"):
dev[0].connect("eap-test2", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd user",
password="secret password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
for i in range(1, 5):
with fail_test(dev[0], i,
"=crypto_ec_point_to_bin;eap_pwd_perform_confirm_exchange"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PWD", identity="pwd-hash",
password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP-Failure reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
def run_eap_pwd_connect(dev, hash=True, fragment=2000):
if hash:
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
fragment_size=str(fragment),
eap="PWD", identity="pwd-hash",
password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
scan_freq="2412", wait_connect=False)
else:
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
fragment_size=str(fragment),
eap="PWD", identity="pwd-hash-sha1",
password="secret password",
scan_freq="2412", wait_connect=False)
ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
"CTRL-EVENT-DISCONNECTED"],
timeout=1)
dev.request("REMOVE_NETWORK all")
if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
dev.wait_disconnected()
dev.dump_monitor()
def test_eap_proto_pwd_errors_server(dev, apdev):
"""EAP-pwd local error cases on server"""
check_eap_capa(dev[0], "PWD")
params = int_eap_server_params()
params['erp_domain'] = 'example.com'
params['eap_server_erp'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "eap_pwd_init"),
(2, "eap_pwd_init"),
(3, "eap_pwd_init"),
(1, "eap_pwd_build_id_req"),
(1, "eap_pwd_build_commit_req"),
(1, "eap_pwd_build_confirm_req"),
(1, "eap_pwd_h_init;eap_pwd_build_confirm_req"),
(1, "wpabuf_alloc;eap_pwd_build_confirm_req"),
(1, "eap_msg_alloc;eap_pwd_build_req"),
(1, "eap_pwd_process_id_resp"),
(1, "get_eap_pwd_group;eap_pwd_process_id_resp"),
(1, "eap_pwd_process_confirm_resp"),
(1, "eap_pwd_h_init;eap_pwd_process_confirm_resp"),
(1, "compute_keys;eap_pwd_process_confirm_resp"),
(1, "eap_pwd_getkey"),
(1, "eap_pwd_get_emsk"),
(1, "eap_pwd_get_session_id")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_pwd_connect(dev[0], hash=True)
tests = [(1, "eap_msg_alloc;eap_pwd_build_req"),
(2, "eap_msg_alloc;eap_pwd_build_req"),
(1, "wpabuf_alloc;eap_pwd_process")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_pwd_connect(dev[0], hash=True, fragment=13)
tests = [(4, "eap_pwd_init")]
for count, func in tests:
with alloc_fail(hapd, count, func):
run_eap_pwd_connect(dev[0], hash=False)
tests = [(1, "eap_pwd_build_id_req"),
(1, "eap_pwd_build_commit_req"),
(1, "crypto_ec_point_mul;eap_pwd_build_commit_req"),
(1, "crypto_ec_point_invert;eap_pwd_build_commit_req"),
(1, "crypto_ec_point_to_bin;eap_pwd_build_commit_req"),
(1, "crypto_ec_point_to_bin;eap_pwd_build_confirm_req"),
(2, "=crypto_ec_point_to_bin;eap_pwd_build_confirm_req"),
(1, "hash_nt_password_hash;eap_pwd_process_id_resp"),
(1, "compute_password_element;eap_pwd_process_id_resp"),
(1, "crypto_bignum_init;eap_pwd_process_commit_resp"),
(1, "crypto_ec_point_mul;eap_pwd_process_commit_resp"),
(2, "crypto_ec_point_mul;eap_pwd_process_commit_resp"),
(1, "crypto_ec_point_add;eap_pwd_process_commit_resp"),
(1, "crypto_ec_point_to_bin;eap_pwd_process_confirm_resp"),
(2, "=crypto_ec_point_to_bin;eap_pwd_process_confirm_resp")]
for count, func in tests:
with fail_test(hapd, count, func):
run_eap_pwd_connect(dev[0], hash=True)
def start_pwd_assoc(dev, hapd):
dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
eap="PWD", identity="pwd user", password="secret password",
wait_connect=False, scan_freq="2412")
proxy_msg(hapd, dev) # EAP-Identity/Request
proxy_msg(dev, hapd) # EAP-Identity/Response
proxy_msg(hapd, dev) # EAP-pwd-Identity/Request
def stop_pwd_assoc(dev, hapd):
dev.request("REMOVE_NETWORK all")
dev.wait_disconnected()
dev.dump_monitor()
hapd.dump_monitor()
def test_eap_proto_pwd_server(dev, apdev):
"""EAP-pwd protocol testing for the server"""
check_eap_capa(dev[0], "PWD")
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Replace exch field with unexpected value
# --> EAP-pwd: Unexpected opcode=4 in state=0
msg = resp[0:18] + "04" + resp[20:]
tx_msg(dev[0], hapd, msg)
# Too short EAP-pwd header (no flags/exch field)
# --> EAP-pwd: Invalid frame
msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "34"
tx_msg(dev[0], hapd, msg)
# Too short EAP-pwd header (L=1 but only one octet of total length field)
# --> EAP-pwd: Frame too short to contain Total-Length field
msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "34" + "81ff"
tx_msg(dev[0], hapd, msg)
# server continues exchange, so start from scratch for the next step
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too large total length
msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "34" + "c1ffff"
tx_msg(dev[0], hapd, msg)
# server continues exchange, so start from scratch for the next step
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# First fragment
msg = resp[0:4] + "0009" + resp[8:12] + "0009" + "34" + "c100ff" + "aa"
tx_msg(dev[0], hapd, msg)
# Ack
req = rx_msg(hapd)
# Unexpected first fragment
# --> EAP-pwd: Unexpected new fragment start when previous fragment is still in use
msg = resp[0:4] + "0009" + resp[8:10] + req[10:12] + "0009" + "34" + "c100ee" + "bb"
tx_msg(dev[0], hapd, msg)
# server continues exchange, so start from scratch for the next step
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too much data in first fragment
# --> EAP-pwd: Buffer overflow attack detected! (0+2 > 1)
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "c10001" + "aabb"
tx_msg(dev[0], hapd, msg)
# EAP-Failure
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Change parameters
# --> EAP-pwd: peer changed parameters
msg = resp[0:20] + "ff" + resp[22:]
tx_msg(dev[0], hapd, msg)
# EAP-Failure
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Too short ID response
# --> EAP-pwd: Invalid ID response
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "01ffeeddcc"
tx_msg(dev[0], hapd, msg)
# server continues exchange, so start from scratch for the next step
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
# EAP-pwd-Identity/Response
resp = rx_msg(dev[0])
tx_msg(dev[0], hapd, resp)
# EAP-pwd-Commit/Request
req = rx_msg(hapd)
# Unexpected EAP-pwd-Identity/Response
# --> EAP-pwd: Unexpected opcode=1 in state=1
msg = resp[0:10] + req[10:12] + resp[12:]
tx_msg(dev[0], hapd, msg)
# server continues exchange, so start from scratch for the next step
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # EAP-pwd-Identity/Response
proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
# EAP-pwd-Commit/Response
resp = rx_msg(dev[0])
# Too short Commit response
# --> EAP-pwd: Unexpected Commit payload length 4 (expected 96)
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "02ffeeddcc"
tx_msg(dev[0], hapd, msg)
# EAP-Failure
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
proxy_msg(dev[0], hapd) # EAP-pwd-Identity/Response
proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
proxy_msg(dev[0], hapd) # EAP-pwd-Commit/Response
proxy_msg(hapd, dev[0]) # EAP-pwd-Confirm/Request
# EAP-pwd-Confirm/Response
resp = rx_msg(dev[0])
# Too short Confirm response
# --> EAP-pwd: Unexpected Confirm payload length 4 (expected 32)
msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "03ffeeddcc"
tx_msg(dev[0], hapd, msg)
# EAP-Failure
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
start_pwd_assoc(dev[0], hapd)
resp = rx_msg(dev[0])
# Set M=1
# --> EAP-pwd: No buffer for reassembly
msg = resp[0:18] + "41" + resp[20:]
tx_msg(dev[0], hapd, msg)
# EAP-Failure
rx_msg(hapd)
stop_pwd_assoc(dev[0], hapd)
def test_eap_proto_erp(dev, apdev):
"""ERP protocol tests"""
check_erp_capa(dev[0])
global eap_proto_erp_test_done
eap_proto_erp_test_done = False
def erp_handler(ctx, req):
logger.info("erp_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing type")
return struct.pack(">BBH", EAP_CODE_INITIATE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected type")
return struct.pack(">BBHB", EAP_CODE_INITIATE, ctx['id'], 4 + 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing Reserved field")
return struct.pack(">BBHB", EAP_CODE_INITIATE, ctx['id'], 4 + 1,
EAP_ERP_TYPE_REAUTH_START)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Zero-length TVs/TLVs")
payload = b""
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short TLV")
payload = struct.pack("B", 191)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated TLV")
payload = struct.pack("BB", 191, 1)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Ignored unknown TLV and unknown TV/TLV terminating parsing")
payload = struct.pack("BBB", 191, 0, 192)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: More than one keyName-NAI")
payload = struct.pack("BBBB", EAP_ERP_TLV_KEYNAME_NAI, 0,
EAP_ERP_TLV_KEYNAME_NAI, 0)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too short TLV keyName-NAI")
payload = struct.pack("B", EAP_ERP_TLV_KEYNAME_NAI)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Truncated TLV keyName-NAI")
payload = struct.pack("BB", EAP_ERP_TLV_KEYNAME_NAI, 1)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid rRK lifetime TV followed by too short rMSK lifetime TV")
payload = struct.pack(">BLBH", EAP_ERP_TV_RRK_LIFETIME, 0,
EAP_ERP_TV_RMSK_LIFETIME, 0)
return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_ERP_TYPE_REAUTH_START, 0) + payload
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing type (Finish)")
return struct.pack(">BBH", EAP_CODE_FINISH, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected type (Finish)")
return struct.pack(">BBHB", EAP_CODE_FINISH, ctx['id'], 4 + 1,
255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing fields (Finish)")
return struct.pack(">BBHB", EAP_CODE_FINISH, ctx['id'], 4 + 1,
EAP_ERP_TYPE_REAUTH)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected SEQ (Finish)")
return struct.pack(">BBHBBHB", EAP_CODE_FINISH, ctx['id'],
4 + 1 + 4,
EAP_ERP_TYPE_REAUTH, 0, 0xffff, 0)
logger.info("No more test responses available - test case completed")
global eap_proto_erp_test_done
eap_proto_erp_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(erp_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_erp_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PAX", identity="pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_fast_errors(dev, apdev):
"""EAP-FAST local error cases"""
check_eap_capa(dev[0], "FAST")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_fast_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "wpabuf_alloc;eap_fast_tlv_eap_payload"),
(1, "eap_fast_derive_key;eap_fast_derive_key_auth"),
(1, "eap_msg_alloc;eap_peer_tls_phase2_nak"),
(1, "wpabuf_alloc;eap_fast_tlv_result"),
(1, "wpabuf_alloc;eap_fast_tlv_pac_ack"),
(1, "=eap_peer_tls_derive_session_id;eap_fast_process_crypto_binding"),
(1, "eap_peer_tls_decrypt;eap_fast_decrypt"),
(1, "eap_fast_getKey"),
(1, "eap_fast_get_session_id"),
(1, "eap_fast_get_emsk")]
for count, func in tests:
dev[0].request("SET blob fast_pac_auth_errors ")
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user@example.com", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth_errors",
erp="1",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_fast_derive_key;eap_fast_derive_key_provisioning"),
(1, "eap_mschapv2_getKey;eap_fast_get_phase2_key"),
(1, "=eap_fast_use_pac_opaque"),
(1, "eap_fast_copy_buf"),
(1, "=eap_fast_add_pac"),
(1, "=eap_fast_init_pac_data"),
(1, "=eap_fast_write_pac"),
(2, "=eap_fast_write_pac")]
for count, func in tests:
dev[0].request("SET blob fast_pac_errors ")
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_errors",
erp="1",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_fast_get_cmk;eap_fast_process_crypto_binding"),
(1, "eap_fast_derive_eap_msk;eap_fast_process_crypto_binding"),
(1, "eap_fast_derive_eap_emsk;eap_fast_process_crypto_binding")]
for count, func in tests:
dev[0].request("SET blob fast_pac_auth_errors ")
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_auth_errors",
erp="1",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].request("SET blob fast_pac_errors ")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_errors",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
# EAP-FAST: Only EAP-MSCHAPv2 is allowed during unauthenticated
# provisioning; reject phase2 type 6
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
logger.info("Wrong password in Phase 2")
dev[0].request("SET blob fast_pac_errors ")
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="wrong password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_errors",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = ["FOOBAR\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nFOOBAR\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nSTART\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Type=12345\nEND\n"
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=12\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=1\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=1q\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Opaque=1\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nA-ID=1\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nI-ID=1\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nA-ID-Info=1\nEND\n"]
for pac in tests:
blob = binascii.hexlify(pac.encode()).decode()
dev[0].request("SET blob fast_pac_errors " + blob)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_errors",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = ["wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nEND\n",
"wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nEND\nSTART\nEND\nSTART\nEND\n"]
for pac in tests:
blob = binascii.hexlify(pac.encode()).decode()
dev[0].request("SET blob fast_pac_errors " + blob)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
phase1="fast_provisioning=2",
pac_file="blob://fast_pac_errors")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].request("SET blob fast_pac_errors ")
def test_eap_proto_peap_errors_server(dev, apdev):
"""EAP-PEAP local error cases on server"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [(1, "get_asymetric_start_key;eap_mschapv2_getKey"),
(1, "generate_authenticator_response_pwhash;eap_mschapv2_process_response"),
(1, "hash_nt_password_hash;eap_mschapv2_process_response"),
(1, "get_master_key;eap_mschapv2_process_response")]
for count, func in tests:
with fail_test(hapd, count, func):
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
scan_freq="2412",
eap="PEAP", anonymous_identity="peap",
identity="user", password="password",
phase1="peapver=0 crypto_binding=2",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("EAP-Failure not reported")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_proto_peap_errors(dev, apdev):
"""EAP-PEAP local error cases"""
check_eap_capa(dev[0], "PEAP")
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_peap_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PEAP", anonymous_identity="peap",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_mschapv2_getKey;eap_peap_get_isk;eap_peap_derive_cmk"),
(1, "eap_msg_alloc;eap_tlv_build_result"),
(1, "eap_mschapv2_init;eap_peap_phase2_request"),
(1, "eap_peer_tls_decrypt;eap_peap_decrypt"),
(1, "wpabuf_alloc;=eap_peap_decrypt"),
(1, "eap_peer_tls_encrypt;eap_peap_decrypt"),
(1, "eap_peer_tls_process_helper;eap_peap_process"),
(1, "eap_peer_tls_derive_key;eap_peap_process"),
(1, "eap_peer_tls_derive_session_id;eap_peap_process"),
(1, "eap_peap_getKey"),
(1, "eap_peap_get_session_id")]
for count, func in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PEAP", anonymous_identity="peap",
identity="user", password="password",
phase1="peapver=0 crypto_binding=2",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "peap_prfplus;eap_peap_derive_cmk"),
(1, "eap_tlv_add_cryptobinding;eap_tlv_build_result"),
(1, "peap_prfplus;eap_peap_getKey"),
(1, "get_asymetric_start_key;eap_mschapv2_getKey")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PEAP", anonymous_identity="peap",
identity="user", password="password",
phase1="peapver=0 crypto_binding=2",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
with alloc_fail(dev[0], 1,
"eap_peer_tls_phase2_nak;eap_peap_phase2_request"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="PEAP", anonymous_identity="peap",
identity="cert user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_proto_ttls_errors(dev, apdev):
"""EAP-TTLS local error cases"""
check_eap_capa(dev[0], "TTLS")
check_eap_capa(dev[0], "MSCHAPV2")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
for i in range(1, 5):
with alloc_fail(dev[0], i, "eap_ttls_init"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TTLS", anonymous_identity="ttls",
identity="user", password="password",
ca_cert="auth_serv/ca.pem",
phase2="autheap=MSCHAPV2",
wait_connect=False)
ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "eap_peer_tls_derive_key;eap_ttls_v0_derive_key",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_peer_tls_derive_session_id;eap_ttls_v0_derive_key",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "wpabuf_alloc;eap_ttls_phase2_request_mschapv2",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_mschapv2",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_peer_tls_encrypt;eap_ttls_encrypt_response;eap_ttls_implicit_identity_request",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_peer_tls_decrypt;eap_ttls_decrypt",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_ttls_getKey",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_ttls_get_session_id",
"DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
(1, "eap_ttls_get_emsk",
"mschapv2 user@domain", "auth=MSCHAPV2"),
(1, "wpabuf_alloc;eap_ttls_phase2_request_mschap",
"mschap user", "auth=MSCHAP"),
(1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_mschap",
"mschap user", "auth=MSCHAP"),
(1, "wpabuf_alloc;eap_ttls_phase2_request_chap",
"chap user", "auth=CHAP"),
(1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_chap",
"chap user", "auth=CHAP"),
(1, "wpabuf_alloc;eap_ttls_phase2_request_pap",
"pap user", "auth=PAP"),
(1, "wpabuf_alloc;eap_ttls_avp_encapsulate",
"user", "autheap=MSCHAPV2"),
(1, "eap_mschapv2_init;eap_ttls_phase2_request_eap_method",
"user", "autheap=MSCHAPV2"),
(1, "eap_sm_buildIdentity;eap_ttls_phase2_request_eap",
"user", "autheap=MSCHAPV2"),
(1, "eap_ttls_avp_encapsulate;eap_ttls_phase2_request_eap",
"user", "autheap=MSCHAPV2"),
(1, "eap_ttls_parse_attr_eap",
"user", "autheap=MSCHAPV2"),
(1, "eap_peer_tls_encrypt;eap_ttls_encrypt_response;eap_ttls_process_decrypted",
"user", "autheap=MSCHAPV2"),
(1, "eap_ttls_fake_identity_request",
"user", "autheap=MSCHAPV2"),
(1, "eap_msg_alloc;eap_tls_process_output",
"user", "autheap=MSCHAPV2"),
(1, "eap_msg_alloc;eap_peer_tls_build_ack",
"user", "autheap=MSCHAPV2"),
(1, "eap_peer_tls_phase2_nak;eap_ttls_phase2_request_eap_method",
"cert user", "autheap=MSCHAPV2")]
tls = dev[0].request("GET tls_library")
if tls.startswith("internal"):
tests += [(1, "tlsv1_client_decrypt;eap_peer_tls_decrypt",
"user", "autheap=MSCHAPV2")]
else:
tests += [(1, "tls_connection_decrypt;eap_peer_tls_decrypt",
"user", "autheap=MSCHAPV2")]
for count, func, identity, phase2 in tests:
with alloc_fail(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TTLS", anonymous_identity="ttls",
identity=identity, password="password",
ca_cert="auth_serv/ca.pem", phase2=phase2,
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
note="Allocation failure not triggered for: %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "os_get_random;eap_ttls_phase2_request_mschapv2"),
(1, "mschapv2_derive_response;eap_ttls_phase2_request_mschapv2")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TTLS", anonymous_identity="ttls",
identity="DOMAIN\mschapv2 user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL",
note="Test failure not triggered for: %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
tests = [(1, "nt_challenge_response;eap_ttls_phase2_request_mschap")]
for count, func in tests:
with fail_test(dev[0], count, func):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TTLS", anonymous_identity="ttls",
identity="mschap user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
erp="1", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=15)
if ev is None:
raise Exception("Timeout on EAP start")
wait_fail_trigger(dev[0], "GET_FAIL",
note="Test failure not triggered for: %d:%s" % (count, func))
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_proto_expanded(dev, apdev):
"""EAP protocol tests with expanded header"""
global eap_proto_expanded_test_done
eap_proto_expanded_test_done = False
def expanded_handler(ctx, req):
logger.info("expanded_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: MD5 challenge in expanded header")
return struct.pack(">BBHB3BLBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 3,
EAP_TYPE_EXPANDED, 0, 0, 0, EAP_TYPE_MD5,
1, 0xaa, ord('n'))
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid expanded EAP length")
return struct.pack(">BBHB3BH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 2,
EAP_TYPE_EXPANDED, 0, 0, 0, EAP_TYPE_MD5)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid expanded frame type")
return struct.pack(">BBHB3BL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_EXPANDED, 0, 0, 1, EAP_TYPE_MD5)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: MSCHAPv2 Challenge")
return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 4 + 1 + 16 + 6,
EAP_TYPE_MSCHAPV2,
1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid expanded frame type")
return struct.pack(">BBHB3BL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4,
EAP_TYPE_EXPANDED, 0, 0, 1, EAP_TYPE_MSCHAPV2)
logger.info("No more test responses available - test case completed")
global eap_proto_expanded_test_done
eap_proto_expanded_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(expanded_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_expanded_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
if i == 4:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MSCHAPV2", identity="user",
password="password",
wait_connect=False)
else:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
if i in [1]:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
elif i in [2, 3]:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
timeout=5)
if ev is None:
raise Exception("Timeout on EAP proposed method")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
else:
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_tls(dev, apdev):
"""EAP-TLS protocol tests"""
check_eap_capa(dev[0], "TLS")
global eap_proto_tls_test_done, eap_proto_tls_test_wait
eap_proto_tls_test_done = False
eap_proto_tls_test_wait = False
def tls_handler(ctx, req):
logger.info("tls_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
global eap_proto_tls_test_wait
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too much payload in TLS/Start: TLS Message Length (0 bytes) smaller than this fragment (1 bytes)")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TLS, 0xa0, 0, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS/Start")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TLS, 0xe0, 2, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too long fragment of TLS/Start: Invalid reassembly state: tls_in_left=2 tls_in_len=0 in_len=0")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_TLS, 0x00, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TLS/Start")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TLS, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS message")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TLS, 0xc0, 2, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid TLS message: no Flags octet included + workaround")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_TLS)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Too long fragment of TLS message: more data than TLS message length indicated")
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_TLS, 0x00, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS/Start and truncated Message Length field")
return struct.pack(">BBHBB3B", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 3,
EAP_TYPE_TLS, 0xe0, 1, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TLS/Start")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TLS, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS message")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TLS, 0xc0, 2, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid TLS message: no Flags octet included + workaround disabled")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_TLS)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TLS/Start")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TLS, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS message (long; first)")
payload = 1450*b'A'
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + len(payload),
EAP_TYPE_TLS, 0xc0, 65536) + payload
# "Too long TLS fragment (size over 64 kB)" on the last one
for i in range(44):
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmented TLS message (long; cont %d)" % i)
eap_proto_tls_test_wait = True
payload = 1470*b'A'
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(payload),
EAP_TYPE_TLS, 0x40) + payload
eap_proto_tls_test_wait = False
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TLS/Start")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TLS, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Non-ACK to more-fragment message")
return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 1,
EAP_TYPE_TLS, 0x00, 255)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Failure")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
logger.info("No more test responses available - test case completed")
global eap_proto_tls_test_done
eap_proto_tls_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(tls_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_tls_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
workaround = "0" if i == 6 else "1"
fragment_size = "100" if i == 8 else "1400"
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TLS", identity="tls user",
ca_cert="auth_serv/ca.pem",
client_cert="auth_serv/user.pem",
private_key="auth_serv/user.key",
eap_workaround=workaround,
fragment_size=fragment_size,
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
"CTRL-EVENT-EAP-STATUS"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
time.sleep(0.1)
start = os.times()[4]
while eap_proto_tls_test_wait:
now = os.times()[4]
if now - start > 10:
break
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_proto_tnc(dev, apdev):
"""EAP-TNC protocol tests"""
check_eap_capa(dev[0], "TNC")
global eap_proto_tnc_test_done
eap_proto_tnc_test_done = False
def tnc_handler(ctx, req):
logger.info("tnc_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNC start with unsupported version")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x20)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNC without Flags field")
return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1,
EAP_TYPE_TNC)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Message underflow due to missing Message Length")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0xa1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid Message Length")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TNC, 0xa1, 0, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid Message Length")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_TNC, 0xe1, 75001)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Start with Message Length")
return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4,
EAP_TYPE_TNC, 0xa1, 1)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Server used start flag again")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragmentation and unexpected payload in ack")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x01)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 1,
EAP_TYPE_TNC, 0x01, 0)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Server fragmenting and fragment overflow")
return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 1,
EAP_TYPE_TNC, 0xe1, 2, 1)
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 2,
EAP_TYPE_TNC, 0x01, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Server fragmenting and no message length in a fragment")
return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 1,
EAP_TYPE_TNC, 0x61, 2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNC start followed by invalid TNCCS-Batch")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"FOO"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNC start followed by invalid TNCCS-Batch (2)")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"</TNCCS-Batch><TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNCCS-Batch missing BatchId attribute")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch foo=3></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected IF-TNCCS BatchId")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=123456789></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing IMC-IMV-Message and TNCC-TNCS-Message end tags")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><IMC-IMV-Message><TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing IMC-IMV-Message and TNCC-TNCS-Message Type")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><IMC-IMV-Message></IMC-IMV-Message><TNCC-TNCS-Message></TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing TNCC-TNCS-Message XML end tag")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML></TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing TNCC-TNCS-Message Base64 start tag")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type></TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing TNCC-TNCS-Message Base64 end tag")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><Base64>abc</TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNCC-TNCS-Message Base64 message")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><Base64>aGVsbG8=</Base64></TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid TNCC-TNCS-Message XML message")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML>hello</XML></TNCC-TNCS-Message></TNCCS-Batch>"
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing TNCCS-Recommendation type")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation foo=1></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNCCS-Recommendation type=none")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation type="none"></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: TNCCS-Recommendation type=isolate")
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1,
EAP_TYPE_TNC, 0x21)
idx += 1
if ctx['num'] == idx:
logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation type="isolate"></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(resp),
EAP_TYPE_TNC, 0x01) + resp
idx += 1
if ctx['num'] == idx:
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
logger.info("No more test responses available - test case completed")
global eap_proto_tnc_test_done
eap_proto_tnc_test_done = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(tnc_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_tnc_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
frag = 1400
if i == 8:
frag = 150
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="TNC", identity="tnc", fragment_size=str(frag),
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
"CTRL-EVENT-EAP-STATUS"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_canned_success_after_identity(dev, apdev):
"""EAP protocol tests for canned EAP-Success after identity"""
check_eap_capa(dev[0], "MD5")
def eap_canned_success_handler(ctx, req):
logger.info("eap_canned_success_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
idx += 1
if ctx['num'] == idx:
logger.info("Test: EAP-Success")
return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
return None
srv = start_radius_server(eap_canned_success_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
phase1="allow_canned_success=1",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
def test_eap_proto_wsc(dev, apdev):
"""EAP-WSC protocol tests"""
global eap_proto_wsc_test_done, eap_proto_wsc_wait_failure
eap_proto_wsc_test_done = False
def wsc_handler(ctx, req):
logger.info("wsc_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] += 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
global eap_proto_wsc_wait_failure
eap_proto_wsc_wait_failure = False
idx += 1
if ctx['num'] == idx:
logger.info("Test: Missing Flags field")
return struct.pack(">BBHB3BLB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 1,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Message underflow (missing Message Length field)")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x02)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid Message Length (> 50000)")
return struct.pack(">BBHB3BLBBH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 4,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x02, 65535)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Invalid Message Length (< current payload)")
return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 5,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x02, 0, 0xff)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Op-Code 5 in WAIT_START state")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
5, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid WSC Start to start the sequence")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: No Message Length field in a fragmented packet")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
4, 0x01)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid WSC Start to start the sequence")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first fragmented packet")
return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 5,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
4, 0x03, 10, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Op-Code 5 in fragment (expected 4)")
return struct.pack(">BBHB3BLBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 3,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
5, 0x01, 2)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid WSC Start to start the sequence")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid first fragmented packet")
return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 5,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
4, 0x03, 2, 1)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Fragment overflow")
return struct.pack(">BBHB3BLBBBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 4,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
4, 0x01, 2, 3)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid WSC Start to start the sequence")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Unexpected Op-Code 5 in WAIT_FRAG_ACK state")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
5, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("Test: Valid WSC Start")
return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 3 + 4 + 2,
EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
1, 0x00)
idx += 1
if ctx['num'] == idx:
logger.info("No more test responses available - test case completed")
global eap_proto_wsc_test_done
eap_proto_wsc_test_done = True
eap_proto_wsc_wait_failure = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(wsc_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
i = 0
while not eap_proto_wsc_test_done:
i += 1
logger.info("Running connection iteration %d" % i)
fragment_size = 1398 if i != 9 else 50
dev[0].connect("eap-test", key_mgmt="WPA-EAP", eap="WSC",
fragment_size=str(fragment_size),
identity="WFA-SimpleConfig-Enrollee-1-0",
phase1="pin=12345670",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP method start")
if eap_proto_wsc_wait_failure:
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
else:
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected(timeout=1)
dev[0].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_canned_success_before_method(dev, apdev):
"""EAP protocol tests for canned EAP-Success before any method"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
bssid = apdev[0]['bssid']
hapd.request("SET ext_eapol_frame_io 1")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
phase1="allow_canned_success=1",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " 0200000403020004")
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP success")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_canned_failure_before_method(dev, apdev):
"""EAP protocol tests for canned EAP-Failure before any method"""
params = int_eap_server_params()
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
bssid = apdev[0]['bssid']
hapd.request("SET ext_eapol_frame_io 1")
dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
phase1="allow_canned_success=1",
eap="MD5", identity="user", password="password",
wait_connect=False)
ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " 0200000404020004")
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
if ev is None:
raise Exception("Timeout on EAP failure")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_nak_oom(dev, apdev):
"""EAP-Nak OOM"""
check_eap_capa(dev[0], "MD5")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sm_buildNak"):
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="MD5", identity="sake user", password="password",
wait_connect=False)
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_eap_nak_expanded(dev, apdev):
"""EAP-Nak with expanded method"""
check_eap_capa(dev[0], "MD5")
check_eap_capa(dev[0], "VENDOR-TEST")
params = hostapd.wpa2_eap_params(ssid="eap-test")
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="VENDOR-TEST WSC",
identity="sake user", password="password",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
if ev is None or "NAK" not in ev:
raise Exception("No NAK event seen")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
if ev is None:
raise Exception("No EAP-Failure seen")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
EAP_TLV_RESULT_TLV = 3
EAP_TLV_NAK_TLV = 4
EAP_TLV_ERROR_CODE_TLV = 5
EAP_TLV_CONNECTION_BINDING_TLV = 6
EAP_TLV_VENDOR_SPECIFIC_TLV = 7
EAP_TLV_URI_TLV = 8
EAP_TLV_EAP_PAYLOAD_TLV = 9
EAP_TLV_INTERMEDIATE_RESULT_TLV = 10
EAP_TLV_PAC_TLV = 11
EAP_TLV_CRYPTO_BINDING_TLV = 12
EAP_TLV_CALLING_STATION_ID_TLV = 13
EAP_TLV_CALLED_STATION_ID_TLV = 14
EAP_TLV_NAS_PORT_TYPE_TLV = 15
EAP_TLV_SERVER_IDENTIFIER_TLV = 16
EAP_TLV_IDENTITY_TYPE_TLV = 17
EAP_TLV_SERVER_TRUSTED_ROOT_TLV = 18
EAP_TLV_REQUEST_ACTION_TLV = 19
EAP_TLV_PKCS7_TLV = 20
EAP_TLV_RESULT_SUCCESS = 1
EAP_TLV_RESULT_FAILURE = 2
EAP_TLV_TYPE_MANDATORY = 0x8000
EAP_TLV_TYPE_MASK = 0x3fff
PAC_TYPE_PAC_KEY = 1
PAC_TYPE_PAC_OPAQUE = 2
PAC_TYPE_CRED_LIFETIME = 3
PAC_TYPE_A_ID = 4
PAC_TYPE_I_ID = 5
PAC_TYPE_A_ID_INFO = 7
PAC_TYPE_PAC_ACKNOWLEDGEMENT = 8
PAC_TYPE_PAC_INFO = 9
PAC_TYPE_PAC_TYPE = 10
def eap_fast_start(ctx):
logger.info("Send EAP-FAST/Start")
return struct.pack(">BBHBBHH", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + 4 + 16,
EAP_TYPE_FAST, 0x21, 4, 16) + 16*b'A'
def test_eap_fast_proto(dev, apdev):
"""EAP-FAST Phase protocol testing"""
check_eap_capa(dev[0], "FAST")
global eap_fast_proto_ctx
eap_fast_proto_ctx = None
def eap_handler(ctx, req):
logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
global eap_fast_proto_ctx
eap_fast_proto_ctx = ctx
ctx['test_done'] = False
idx += 1
if ctx['num'] == idx:
return eap_fast_start(ctx)
idx += 1
if ctx['num'] == idx:
logger.info("EAP-FAST: TLS processing failed")
data = b'ABCDEFGHIK'
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(data),
EAP_TYPE_FAST, 0x01) + data
idx += 1
if ctx['num'] == idx:
ctx['test_done'] = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
logger.info("Past last test case")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(eap_handler)
try:
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_proto",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Could not start EAP-FAST")
ok = False
for i in range(100):
if eap_fast_proto_ctx:
if eap_fast_proto_ctx['test_done']:
ok = True
break
time.sleep(0.05)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
finally:
stop_radius_server(srv)
def run_eap_fast_phase2(dev, test_payload, test_failure=True):
global eap_fast_proto_ctx
eap_fast_proto_ctx = None
def ssl_info_callback(conn, where, ret):
logger.debug("SSL: info where=%d ret=%d" % (where, ret))
def log_conn_state(conn):
try:
state = conn.state_string()
except AttributeError:
state = conn.get_state_string()
if state:
logger.info("State: " + str(state))
def process_clienthello(ctx, payload):
logger.info("Process ClientHello")
ctx['sslctx'] = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
ctx['sslctx'].set_info_callback(ssl_info_callback)
ctx['sslctx'].load_tmp_dh("auth_serv/dh.conf")
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
ctx['sslctx'].set_cipher_list("ADH-AES128-SHA:@SECLEVEL=0")
else:
ctx['sslctx'].set_cipher_list("ADH-AES128-SHA")
ctx['conn'] = OpenSSL.SSL.Connection(ctx['sslctx'], None)
ctx['conn'].set_accept_state()
log_conn_state(ctx['conn'])
ctx['conn'].bio_write(payload)
try:
ctx['conn'].do_handshake()
except OpenSSL.SSL.WantReadError:
pass
log_conn_state(ctx['conn'])
data = ctx['conn'].bio_read(4096)
log_conn_state(ctx['conn'])
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(data),
EAP_TYPE_FAST, 0x01) + data
def process_clientkeyexchange(ctx, payload, appl_data):
logger.info("Process ClientKeyExchange")
log_conn_state(ctx['conn'])
ctx['conn'].bio_write(payload)
try:
ctx['conn'].do_handshake()
except OpenSSL.SSL.WantReadError:
pass
ctx['conn'].send(appl_data)
log_conn_state(ctx['conn'])
data = ctx['conn'].bio_read(4096)
log_conn_state(ctx['conn'])
return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
4 + 1 + 1 + len(data),
EAP_TYPE_FAST, 0x01) + data
def eap_handler(ctx, req):
logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
if 'num' not in ctx:
ctx['num'] = 0
ctx['num'] = ctx['num'] + 1
if 'id' not in ctx:
ctx['id'] = 1
ctx['id'] = (ctx['id'] + 1) % 256
idx = 0
global eap_fast_proto_ctx
eap_fast_proto_ctx = ctx
ctx['test_done'] = False
logger.debug("ctx['num']=%d" % ctx['num'])
idx += 1
if ctx['num'] == idx:
return eap_fast_start(ctx)
idx += 1
if ctx['num'] == idx:
return process_clienthello(ctx, req[6:])
idx += 1
if ctx['num'] == idx:
if not test_failure:
ctx['test_done'] = True
return process_clientkeyexchange(ctx, req[6:], test_payload)
idx += 1
if ctx['num'] == idx:
ctx['test_done'] = True
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
logger.info("Past last test case")
return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
srv = start_radius_server(eap_handler)
try:
dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
eap="FAST", anonymous_identity="FAST",
identity="user", password="password",
ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
phase1="fast_provisioning=1",
pac_file="blob://fast_pac_proto",
wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
if ev is None:
raise Exception("Could not start EAP-FAST")
dev[0].dump_monitor()
ok = False
for i in range(100):
if eap_fast_proto_ctx:
if eap_fast_proto_ctx['test_done']:
ok = True
break
time.sleep(0.05)
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if not ok:
raise Exception("EAP-FAST TLS exchange did not complete")
for i in range(3):
dev[i].dump_monitor()
finally:
stop_radius_server(srv)
def test_eap_fast_proto_phase2(dev, apdev):
"""EAP-FAST Phase 2 protocol testing"""
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
check_eap_capa(dev[0], "FAST")
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
tests = [("Too short Phase 2 TLV frame (len=3)",
"ABC",
False),
("EAP-FAST: TLV overflow",
struct.pack(">HHB", 0, 2, 0xff),
False),
("EAP-FAST: Unknown TLV (optional and mandatory)",
struct.pack(">HHB", 0, 1, 0xff) +
struct.pack(">HHB", EAP_TLV_TYPE_MANDATORY, 1, 0xff),
True),
("EAP-FAST: More than one EAP-Payload TLV in the message",
struct.pack(">HHBHHB",
EAP_TLV_EAP_PAYLOAD_TLV, 1, 0xff,
EAP_TLV_EAP_PAYLOAD_TLV, 1, 0xff),
True),
("EAP-FAST: Unknown Result 255 and More than one Result TLV in the message",
struct.pack(">HHHHHH",
EAP_TLV_RESULT_TLV, 2, 0xff,
EAP_TLV_RESULT_TLV, 2, 0xff),
True),
("EAP-FAST: Too short Result TLV",
struct.pack(">HHB", EAP_TLV_RESULT_TLV, 1, 0xff),
True),
("EAP-FAST: Unknown Intermediate Result 255 and More than one Intermediate-Result TLV in the message",
struct.pack(">HHHHHH",
EAP_TLV_INTERMEDIATE_RESULT_TLV, 2, 0xff,
EAP_TLV_INTERMEDIATE_RESULT_TLV, 2, 0xff),
True),
("EAP-FAST: Too short Intermediate-Result TLV",
struct.pack(">HHB", EAP_TLV_INTERMEDIATE_RESULT_TLV, 1, 0xff),
True),
("EAP-FAST: More than one Crypto-Binding TLV in the message",
struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A' +
struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A',
True),
("EAP-FAST: Too short Crypto-Binding TLV",
struct.pack(">HHB", EAP_TLV_CRYPTO_BINDING_TLV, 1, 0xff),
True),
("EAP-FAST: More than one Request-Action TLV in the message",
struct.pack(">HHBBHHBB",
EAP_TLV_REQUEST_ACTION_TLV, 2, 0xff, 0xff,
EAP_TLV_REQUEST_ACTION_TLV, 2, 0xff, 0xff),
True),
("EAP-FAST: Too short Request-Action TLV",
struct.pack(">HHB", EAP_TLV_REQUEST_ACTION_TLV, 1, 0xff),
True),
("EAP-FAST: More than one PAC TLV in the message",
struct.pack(">HHBHHB",
EAP_TLV_PAC_TLV, 1, 0xff,
EAP_TLV_PAC_TLV, 1, 0xff),
True),
("EAP-FAST: Too short EAP Payload TLV (Len=3)",
struct.pack(">HH3B",
EAP_TLV_EAP_PAYLOAD_TLV, 3, 0, 0, 0),
False),
("EAP-FAST: Too short Phase 2 request (Len=0)",
struct.pack(">HHBBH",
EAP_TLV_EAP_PAYLOAD_TLV, 4,
EAP_CODE_REQUEST, 0, 0),
False),
("EAP-FAST: EAP packet overflow in EAP Payload TLV",
struct.pack(">HHBBH",
EAP_TLV_EAP_PAYLOAD_TLV, 4,
EAP_CODE_REQUEST, 0, 4 + 1),
False),
("EAP-FAST: Unexpected code=0 in Phase 2 EAP header",
struct.pack(">HHBBH",
EAP_TLV_EAP_PAYLOAD_TLV, 4,
0, 0, 0),
False),
("EAP-FAST: PAC TLV without Result TLV acknowledging success",
struct.pack(">HHB", EAP_TLV_PAC_TLV, 1, 0xff),
True),
("EAP-FAST: PAC TLV does not include all the required fields",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHB", EAP_TLV_PAC_TLV, 1, 0xff),
True),
("EAP-FAST: Invalid PAC-Key length 0, Ignored unknown PAC type 0, and PAC TLV overrun (type=0 len=2 left=1)",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHHB", EAP_TLV_PAC_TLV, 4 + 4 + 5,
PAC_TYPE_PAC_KEY, 0, 0, 0, 0, 2, 0),
True),
("EAP-FAST: PAC-Info does not include all the required fields",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHH", EAP_TLV_PAC_TLV, 4 + 4 + 4 + 32,
PAC_TYPE_PAC_OPAQUE, 0,
PAC_TYPE_PAC_INFO, 0,
PAC_TYPE_PAC_KEY, 32) + 32*b'A',
True),
("EAP-FAST: Invalid CRED_LIFETIME length, Ignored unknown PAC-Info type 0, and Invalid PAC-Type length 1",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHHHHHHBHH", EAP_TLV_PAC_TLV, 4 + 4 + 13 + 4 + 32,
PAC_TYPE_PAC_OPAQUE, 0,
PAC_TYPE_PAC_INFO, 13, PAC_TYPE_CRED_LIFETIME, 0,
0, 0, PAC_TYPE_PAC_TYPE, 1, 0,
PAC_TYPE_PAC_KEY, 32) + 32*b'A',
True),
("EAP-FAST: Unsupported PAC-Type 0",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHHHHH", EAP_TLV_PAC_TLV, 4 + 4 + 6 + 4 + 32,
PAC_TYPE_PAC_OPAQUE, 0,
PAC_TYPE_PAC_INFO, 6, PAC_TYPE_PAC_TYPE, 2, 0,
PAC_TYPE_PAC_KEY, 32) + 32*b'A',
True),
("EAP-FAST: PAC-Info overrun (type=0 len=2 left=1)",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHHBHH", EAP_TLV_PAC_TLV, 4 + 4 + 5 + 4 + 32,
PAC_TYPE_PAC_OPAQUE, 0,
PAC_TYPE_PAC_INFO, 5, 0, 2, 1,
PAC_TYPE_PAC_KEY, 32) + 32*b'A',
True),
("EAP-FAST: Valid PAC",
struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
EAP_TLV_RESULT_SUCCESS) +
struct.pack(">HHHHHHHHBHHBHH", EAP_TLV_PAC_TLV,
4 + 4 + 10 + 4 + 32,
PAC_TYPE_PAC_OPAQUE, 0,
PAC_TYPE_PAC_INFO, 10, PAC_TYPE_A_ID, 1, 0x41,
PAC_TYPE_A_ID_INFO, 1, 0x42,
PAC_TYPE_PAC_KEY, 32) + 32*b'A',
True),
("EAP-FAST: Invalid version/subtype in Crypto-Binding TLV",
struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A',
True)]
for title, payload, failure in tests:
logger.info("Phase 2 test: " + title)
run_eap_fast_phase2(dev, payload, failure)
def test_eap_fast_tlv_nak_oom(dev, apdev):
"""EAP-FAST Phase 2 TLV NAK OOM"""
if not openssl_imported:
raise HwsimSkip("OpenSSL python method not available")
check_eap_capa(dev[0], "FAST")
hapd = start_ap(apdev[0])
dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
with alloc_fail(dev[0], 1, "eap_fast_tlv_nak"):
run_eap_fast_phase2(dev, struct.pack(">HHB", EAP_TLV_TYPE_MANDATORY,
1, 0xff), False)
diff --git a/tests/hwsim/test_fils.py b/tests/hwsim/test_fils.py
index 39b48e9808ce..9998299d81a8 100644
--- a/tests/hwsim/test_fils.py
+++ b/tests/hwsim/test_fils.py
@@ -1,2360 +1,2411 @@
# Test cases for FILS
# Copyright (c) 2015-2017, Qualcomm Atheros, Inc.
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import binascii
import hashlib
import logging
logger = logging.getLogger()
import os
import socket
import struct
import time
import hostapd
from tshark import run_tshark
from wpasupplicant import WpaSupplicant
import hwsim_utils
from utils import *
from test_erp import start_erp_as
from test_ap_hs20 import ip_checksum
def test_fils_sk_full_auth(dev, apdev, params):
"""FILS SK full authentication"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['wpa_group_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
bss = dev[0].get_bss(bssid)
logger.debug("BSS: " + str(bss))
if "[FILS]" not in bss['flags']:
raise Exception("[FILS] flag not indicated")
if "[WPA2-FILS-SHA256-CCMP]" not in bss['flags']:
raise Exception("[WPA2-FILS-SHA256-CCMP] flag not indicated")
res = dev[0].request("SCAN_RESULTS")
logger.debug("SCAN_RESULTS: " + res)
if "[FILS]" not in res:
raise Exception("[FILS] flag not indicated")
if "[WPA2-FILS-SHA256-CCMP]" not in res:
raise Exception("[WPA2-FILS-SHA256-CCMP] flag not indicated")
dev[0].request("ERP_FLUSH")
dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
conf = hapd.get_config()
if conf['key_mgmt'] != 'FILS-SHA256':
raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
def test_fils_sk_sha384_full_auth(dev, apdev, params):
"""FILS SK full authentication (SHA384)"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA384"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['wpa_group_rekey'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
bss = dev[0].get_bss(bssid)
logger.debug("BSS: " + str(bss))
if "[FILS]" not in bss['flags']:
raise Exception("[FILS] flag not indicated")
if "[WPA2-FILS-SHA384-CCMP]" not in bss['flags']:
raise Exception("[WPA2-FILS-SHA384-CCMP] flag not indicated")
res = dev[0].request("SCAN_RESULTS")
logger.debug("SCAN_RESULTS: " + res)
if "[FILS]" not in res:
raise Exception("[FILS] flag not indicated")
if "[WPA2-FILS-SHA384-CCMP]" not in res:
raise Exception("[WPA2-FILS-SHA384-CCMP] flag not indicated")
dev[0].request("ERP_FLUSH")
dev[0].connect("fils", key_mgmt="FILS-SHA384",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
conf = hapd.get_config()
if conf['key_mgmt'] != 'FILS-SHA384':
raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
def test_fils_sk_pmksa_caching(dev, apdev, params):
"""FILS SK and PMKSA caching"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
# Verify EAPOL reauthentication after FILS authentication
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_pmksa_caching_ocv(dev, apdev, params):
"""FILS SK and PMKSA caching with OCV"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['ieee80211w'] = '1'
params['ocv'] = '1'
try:
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412", ieee80211w="1", ocv="1")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
# Verify EAPOL reauthentication after FILS authentication
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_pmksa_caching_and_cache_id(dev, apdev):
"""FILS SK and PMKSA caching with Cache Identifier"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['fils_cache_id'] = "abcd"
params["radius_server_clients"] = "auth_serv/radius_clients.conf"
params["radius_server_auth_port"] = '18128'
params["eap_server"] = "1"
params["eap_user_file"] = "auth_serv/eap_user.conf"
params["ca_cert"] = "auth_serv/ca.pem"
params["server_cert"] = "auth_serv/server.pem"
params["private_key"] = "auth_serv/server.key"
params["eap_sim_db"] = "unix:/tmp/hlr_auc_gw.sock"
params["dh_file"] = "auth_serv/dh.conf"
params["pac_opaque_encr_key"] = "000102030405060708090a0b0c0d0e0f"
params["eap_fast_a_id"] = "101112131415161718191a1b1c1d1e1f"
params["eap_fast_a_id_info"] = "test server"
params["eap_server_erp"] = "1"
params["erp_domain"] = "example.com"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
res = dev[0].request("PMKSA")
if "FILS Cache Identifier" not in res:
raise Exception("PMKSA list does not include FILS Cache Identifier")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
if "cache_id" not in pmksa:
raise Exception("No FILS Cache Identifier listed")
if pmksa["cache_id"] != "abcd":
raise Exception("The configured FILS Cache Identifier not seen in PMKSA")
bssid2 = apdev[1]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['fils_cache_id'] = "abcd"
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].dump_monitor()
if "OK" not in dev[0].request("ROAM " + bssid2):
raise Exception("ROAM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if bssid2 not in ev:
raise Exception("Failed to connect to the second AP")
hwsim_utils.test_connectivity(dev[0], hapd2)
pmksa2 = dev[0].get_pmksa(bssid2)
if pmksa2:
raise Exception("Unexpected extra PMKSA cache added")
pmksa2 = dev[0].get_pmksa(bssid)
if not pmksa2:
raise Exception("Original PMKSA cache entry removed")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
def test_fils_sk_pmksa_caching_ctrl_ext(dev, apdev, params):
"""FILS SK and PMKSA caching with Cache Identifier and external management"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
hapd_as = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA384"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['fils_cache_id'] = "ffee"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA384",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
res1 = dev[0].request("PMKSA_GET %d" % id)
logger.info("PMKSA_GET: " + res1)
if "UNKNOWN COMMAND" in res1:
raise HwsimSkip("PMKSA_GET not supported in the build")
if bssid not in res1:
raise Exception("PMKSA cache entry missing")
if "ffee" not in res1:
raise Exception("FILS Cache Identifier not seen in PMKSA cache entry")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd_as.disable()
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("PMKSA_FLUSH")
dev[0].request("ERP_FLUSH")
for entry in res1.splitlines():
if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
raise Exception("Failed to add PMKSA entry")
bssid2 = apdev[1]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA384"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['fils_cache_id'] = "ffee"
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].set_network(id, "bssid", bssid2)
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_connected()
if bssid2 not in ev:
raise Exception("Unexpected BSS selected")
def test_fils_sk_erp(dev, apdev, params):
"""FILS SK using ERP"""
run_fils_sk_erp(dev, apdev, "FILS-SHA256", params)
def test_fils_sk_erp_sha384(dev, apdev, params):
"""FILS SK using ERP and SHA384"""
run_fils_sk_erp(dev, apdev, "FILS-SHA384", params)
def run_fils_sk_erp(dev, apdev, key_mgmt, params):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = key_mgmt
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt=key_mgmt,
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_erp_followed_by_pmksa_caching(dev, apdev, params):
"""FILS SK ERP following by PMKSA caching"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Force the second connection to use ERP by deleting the PMKSA entry.
dev[0].request("PMKSA_FLUSH")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# The third connection is expected to use PMKSA caching for FILS
# authentication.
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
def test_fils_sk_erp_another_ssid(dev, apdev, params):
"""FILS SK using ERP and roam to another SSID"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
dev[0].flush_scan_cache()
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
params = hostapd.wpa2_eap_params(ssid="fils2")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].dump_monitor()
id = dev[0].connect("fils2", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_multiple_realms(dev, apdev, params):
"""FILS SK and multiple realms"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
fils_realms = ['r1.example.org', 'r2.EXAMPLE.org', 'r3.example.org',
'r4.example.org', 'r5.example.org', 'r6.example.org',
'r7.example.org', 'r8.example.org',
'example.com',
'r9.example.org', 'r10.example.org', 'r11.example.org',
'r12.example.org', 'r13.example.org', 'r14.example.org',
'r15.example.org', 'r16.example.org']
params['fils_realm'] = fils_realms
params['fils_cache_id'] = "1234"
params['hessid'] = bssid
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
if "OK" not in dev[0].request("ANQP_GET " + bssid + " 275"):
raise Exception("ANQP_GET command failed")
ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("GAS query timed out")
bss = dev[0].get_bss(bssid)
if 'fils_info' not in bss:
raise Exception("FILS Indication element information missing")
if bss['fils_info'] != '02b8':
raise Exception("Unexpected FILS Information: " + bss['fils_info'])
if 'fils_cache_id' not in bss:
raise Exception("FILS Cache Identifier missing")
if bss['fils_cache_id'] != '1234':
raise Exception("Unexpected FILS Cache Identifier: " + bss['fils_cache_id'])
if 'fils_realms' not in bss:
raise Exception("FILS Realm Identifiers missing")
expected = ''
count = 0
for realm in fils_realms:
hash = hashlib.sha256(realm.lower().encode()).digest()
expected += binascii.hexlify(hash[0:2]).decode()
count += 1
if count == 7:
break
if bss['fils_realms'] != expected:
raise Exception("Unexpected FILS Realm Identifiers: " + bss['fils_realms'])
if 'anqp_fils_realm_info' not in bss:
raise Exception("FILS Realm Information ANQP-element not seen")
info = bss['anqp_fils_realm_info']
expected = ''
for realm in fils_realms:
hash = hashlib.sha256(realm.lower().encode()).digest()
expected += binascii.hexlify(hash[0:2]).decode()
if info != expected:
raise Exception("Unexpected FILS Realm Info ANQP-element: " + info)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
# DHCP message op codes
BOOTREQUEST = 1
BOOTREPLY = 2
OPT_PAD = 0
OPT_DHCP_MESSAGE_TYPE = 53
OPT_RAPID_COMMIT = 80
OPT_END = 255
DHCPDISCOVER = 1
DHCPOFFER = 2
DHCPREQUEST = 3
DHCPDECLINE = 4
DHCPACK = 5
DHCPNAK = 6
DHCPRELEASE = 7
DHCPINFORM = 8
def build_dhcp(req, dhcp_msg, chaddr, giaddr="0.0.0.0",
ip_src="0.0.0.0", ip_dst="255.255.255.255",
rapid_commit=True, override_op=None, magic_override=None,
opt_end=True, extra_op=None):
proto = b'\x08\x00' # IPv4
_ip_src = socket.inet_pton(socket.AF_INET, ip_src)
_ip_dst = socket.inet_pton(socket.AF_INET, ip_dst)
_ciaddr = b'\x00\x00\x00\x00'
_yiaddr = b'\x00\x00\x00\x00'
_siaddr = b'\x00\x00\x00\x00'
_giaddr = socket.inet_pton(socket.AF_INET, giaddr)
_chaddr = binascii.unhexlify(chaddr.replace(':', '')) + 10 * b'\x00'
htype = 1 # Hardware address type; 1 = Ethernet
hlen = 6 # Hardware address length
hops = 0
xid = 123456
secs = 0
flags = 0
if req:
op = BOOTREQUEST
src_port = 68
dst_port = 67
else:
op = BOOTREPLY
src_port = 67
dst_port = 68
if override_op is not None:
op = override_op
payload = struct.pack('>BBBBLHH', op, htype, hlen, hops, xid, secs, flags)
sname = 64*b'\x00'
file = 128*b'\x00'
payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + sname + file
# magic - DHCP
if magic_override is not None:
payload += magic_override
else:
payload += b'\x63\x82\x53\x63'
# Option: DHCP Message Type
if dhcp_msg is not None:
payload += struct.pack('BBB', OPT_DHCP_MESSAGE_TYPE, 1, dhcp_msg)
if rapid_commit:
# Option: Rapid Commit
payload += struct.pack('BB', OPT_RAPID_COMMIT, 0)
if extra_op:
payload += extra_op
# End Option
if opt_end:
payload += struct.pack('B', OPT_END)
udp = struct.pack('>HHHH', src_port, dst_port,
8 + len(payload), 0) + payload
tot_len = 20 + len(udp)
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
ipv4 = start + csum + _ip_src + _ip_dst
return proto + ipv4 + udp
def fils_hlp_config(fils_hlp_wait_time=10000):
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['own_ip_addr'] = '127.0.0.3'
params['dhcp_server'] = '127.0.0.2'
params['fils_hlp_wait_time'] = str(fils_hlp_wait_time)
return params
def test_fils_sk_hlp(dev, apdev, params):
"""FILS SK HLP (rapid commit server)"""
run_fils_sk_hlp(dev, apdev, True, params)
def test_fils_sk_hlp_no_rapid_commit(dev, apdev, params):
"""FILS SK HLP (no rapid commit server)"""
run_fils_sk_hlp(dev, apdev, False, params)
def run_fils_sk_hlp(dev, apdev, rapid_commit_server, params):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(5)
sock.bind(("127.0.0.2", 67))
bssid = apdev[0]['bssid']
params = fils_hlp_config()
params['fils_hlp_wait_time'] = '10000'
if not rapid_commit_server:
params['dhcp_rapid_commit_proxy'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
raise Exception("Failed to flush pending FILS HLP requests")
tests = ["",
"q",
"ff:ff:ff:ff:ff:ff",
"ff:ff:ff:ff:ff:ff q"]
for t in tests:
if "FAIL" not in dev[0].request("FILS_HLP_REQ_ADD " + t):
raise Exception("Invalid FILS_HLP_REQ_ADD accepted: " + t)
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr())
tests = ["ff:ff:ff:ff:ff:ff aabb",
"ff:ff:ff:ff:ff:ff " + 255*'cc',
hapd.own_addr() + " ddee010203040506070809",
"ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()]
for t in tests:
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + t):
raise Exception("FILS_HLP_REQ_ADD failed: " + t)
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
if rapid_commit_server:
# TODO: Proper rapid commit response
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpdisc[2+20+8:], addr)
else:
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpdisc[2+20+8:], addr)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpdisc[2+20+8:], addr)
ev = dev[0].wait_event(["FILS-HLP-RX"], timeout=10)
if ev is None:
raise Exception("FILS HLP response not reported")
vals = ev.split(' ')
frame = binascii.unhexlify(vals[3].split('=')[1])
proto, = struct.unpack('>H', frame[0:2])
if proto != 0x0800:
raise Exception("Unexpected ethertype in HLP response: %d" % proto)
frame = frame[2:]
ip = frame[0:20]
if ip_checksum(ip) != b'\x00\x00':
raise Exception("IP header checksum mismatch in HLP response")
frame = frame[20:]
udp = frame[0:8]
frame = frame[8:]
sport, dport, ulen, ucheck = struct.unpack('>HHHH', udp)
if sport != 67 or dport != 68:
raise Exception("Unexpected UDP port in HLP response")
dhcp = frame[0:28]
frame = frame[28:]
op, htype, hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr = struct.unpack('>4BL2H4L', dhcp)
chaddr = frame[0:16]
frame = frame[16:]
sname = frame[0:64]
frame = frame[64:]
file = frame[0:128]
frame = frame[128:]
options = frame
if options[0:4] != b'\x63\x82\x53\x63':
raise Exception("No DHCP magic seen in HLP response")
options = options[4:]
# TODO: fully parse and validate DHCPACK options
if struct.pack('BBB', OPT_DHCP_MESSAGE_TYPE, 1, DHCPACK) not in options:
raise Exception("DHCPACK not in HLP response")
dev[0].wait_connected()
dev[0].request("FILS_HLP_REQ_FLUSH")
def test_fils_sk_hlp_timeout(dev, apdev, params):
"""FILS SK HLP (rapid commit server timeout)"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(5)
sock.bind(("127.0.0.2", 67))
bssid = apdev[0]['bssid']
params = fils_hlp_config(fils_hlp_wait_time=30)
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
raise Exception("Failed to flush pending FILS HLP requests")
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr())
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
# Wait for HLP wait timeout to hit
# FILS: HLP response timeout - continue with association response
dev[0].wait_connected()
dev[0].request("FILS_HLP_REQ_FLUSH")
def test_fils_sk_hlp_oom(dev, apdev, params):
"""FILS SK HLP and hostapd OOM"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(5)
sock.bind(("127.0.0.2", 67))
bssid = apdev[0]['bssid']
params = fils_hlp_config(fils_hlp_wait_time=500)
params['dhcp_rapid_commit_proxy'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
raise Exception("Failed to flush pending FILS HLP requests")
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr())
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "fils_process_hlp"):
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "fils_process_hlp_dhcp"):
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "wpabuf_alloc;fils_process_hlp_dhcp"):
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "wpabuf_alloc;fils_dhcp_handler"):
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpdisc[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "wpabuf_resize;fils_dhcp_handler"):
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpdisc[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
with alloc_fail(hapd, 1, "wpabuf_resize;fils_dhcp_request"):
sock.sendto(dhcpoffer[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("FILS_HLP_REQ_FLUSH")
def test_fils_sk_hlp_req_parsing(dev, apdev, params):
"""FILS SK HLP request parsing"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = fils_hlp_config(fils_hlp_wait_time=30)
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
raise Exception("Failed to flush pending FILS HLP requests")
tot_len = 20 + 1
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
_ip_src = b'\x00\x00\x00\x00'
_ip_dst = b'\x00\x00\x00\x00'
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
ipv4_overflow = start + csum + _ip_src + _ip_dst
tot_len = 20
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 123)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
ipv4_unknown_proto = start + csum + _ip_src + _ip_dst
tot_len = 20
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
ipv4_missing_udp_hdr = start + csum + _ip_src + _ip_dst
src_port = 68
dst_port = 67
udp = struct.pack('>HHHH', src_port, dst_port, 8 + 1, 0)
tot_len = 20 + len(udp)
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
udp_overflow = start + csum + _ip_src + _ip_dst + udp
udp = struct.pack('>HHHH', src_port, dst_port, 7, 0)
tot_len = 20 + len(udp)
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
udp_underflow = start + csum + _ip_src + _ip_dst + udp
src_port = 123
dst_port = 456
udp = struct.pack('>HHHH', src_port, dst_port, 8, 0)
tot_len = 20 + len(udp)
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
udp_unknown_port = start + csum + _ip_src + _ip_dst + udp
src_port = 68
dst_port = 67
udp = struct.pack('>HHHH', src_port, dst_port, 8, 0)
tot_len = 20 + len(udp)
start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
csum = ip_checksum(ipv4)
dhcp_missing_data = start + csum + _ip_src + _ip_dst + udp
dhcp_not_req = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr(), override_op=BOOTREPLY)
dhcp_no_magic = build_dhcp(req=True, dhcp_msg=None,
chaddr=dev[0].own_addr(), magic_override=b'',
rapid_commit=False, opt_end=False)
dhcp_unknown_magic = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr(),
magic_override=b'\x00\x00\x00\x00')
dhcp_opts = build_dhcp(req=True, dhcp_msg=DHCPNAK,
chaddr=dev[0].own_addr(),
extra_op=b'\x00\x11', opt_end=False)
dhcp_opts2 = build_dhcp(req=True, dhcp_msg=DHCPNAK,
chaddr=dev[0].own_addr(),
extra_op=b'\x11\x01', opt_end=False)
dhcp_valid = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr())
tests = ["ff",
"0800",
"0800" + 20*"00",
"0800" + binascii.hexlify(ipv4_overflow).decode(),
"0800" + binascii.hexlify(ipv4_unknown_proto).decode(),
"0800" + binascii.hexlify(ipv4_missing_udp_hdr).decode(),
"0800" + binascii.hexlify(udp_overflow).decode(),
"0800" + binascii.hexlify(udp_underflow).decode(),
"0800" + binascii.hexlify(udp_unknown_port).decode(),
"0800" + binascii.hexlify(dhcp_missing_data).decode(),
binascii.hexlify(dhcp_not_req).decode(),
binascii.hexlify(dhcp_no_magic).decode(),
binascii.hexlify(dhcp_unknown_magic).decode()]
for t in tests:
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + t):
raise Exception("FILS_HLP_REQ_ADD failed: " + t)
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("FILS_HLP_REQ_FLUSH")
tests = [binascii.hexlify(dhcp_opts).decode(),
binascii.hexlify(dhcp_opts2).decode()]
for t in tests:
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + t):
raise Exception("FILS_HLP_REQ_ADD failed: " + t)
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("FILS_HLP_REQ_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcp_valid).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
hapd.set("own_ip_addr", "0.0.0.0")
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.set("dhcp_server", "0.0.0.0")
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS: Failed to bind DHCP socket: Address already in use
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(5)
sock.bind(("127.0.0.2", 67))
hapd.set("own_ip_addr", "127.0.0.2")
hapd.set("dhcp_server", "127.0.0.2")
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS: DHCP sendto failed: Invalid argument
hapd.set("own_ip_addr", "127.0.0.3")
hapd.set("dhcp_server", "127.0.0.2")
hapd.set("dhcp_relay_port", "0")
hapd.set("dhcp_server_port", "0")
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("FILS_HLP_REQ_FLUSH")
def test_fils_sk_hlp_dhcp_parsing(dev, apdev, params):
"""FILS SK HLP and DHCP response parsing"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(5)
sock.bind(("127.0.0.2", 67))
bssid = apdev[0]['bssid']
params = fils_hlp_config(fils_hlp_wait_time=30)
params['dhcp_rapid_commit_proxy'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
raise Exception("Failed to flush pending FILS HLP requests")
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr())
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
with alloc_fail(hapd, 1, "fils_process_hlp"):
dev[0].select_network(id, freq=2412)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
#sock.sendto(dhcpdisc[2+20+8:], addr)
chaddr = binascii.unhexlify(dev[0].own_addr().replace(':', '')) + 10*b'\x00'
tests = [b"\x00",
b"\x02" + 500 * b"\x00",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 500*b"\x00",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x00\x11",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x11\x01",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + chaddr + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x35\x00\xff",
b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + chaddr + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x35\x01\x00\xff",
1501 * b"\x00"]
for t in tests:
sock.sendto(t, addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS: DHCP sendto failed: Invalid argument for second DHCP TX in proxy
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
hapd.set("dhcp_server_port", "0")
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
sock.sendto(dhcpoffer[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.set("dhcp_server_port", "67")
# Options in DHCPOFFER
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
extra_op=b"\x00\x11", opt_end=False)
sock.sendto(dhcpoffer[2+20+8:], addr)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Options in DHCPOFFER (2)
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
extra_op=b"\x11\x01", opt_end=False)
sock.sendto(dhcpoffer[2+20+8:], addr)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Server ID in DHCPOFFER
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
extra_op=b"\x36\x01\x30")
sock.sendto(dhcpoffer[2+20+8:], addr)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS: Could not update DHCPDISCOVER
dev[0].request("FILS_HLP_REQ_FLUSH")
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr(),
extra_op=b"\x00\x11", opt_end=False)
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
extra_op=b"\x36\x01\x30")
sock.sendto(dhcpoffer[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS: Could not update DHCPDISCOVER (2)
dev[0].request("FILS_HLP_REQ_FLUSH")
dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
chaddr=dev[0].own_addr(),
extra_op=b"\x11\x01", opt_end=False)
if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
raise Exception("FILS_HLP_REQ_ADD failed")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
(msg, addr) = sock.recvfrom(1000)
logger.debug("Received DHCP message from %s" % str(addr))
dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
extra_op=b"\x36\x01\x30")
sock.sendto(dhcpoffer[2+20+8:], addr)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("FILS_HLP_REQ_FLUSH")
def test_fils_sk_erp_and_reauth(dev, apdev, params):
"""FILS SK using ERP and AP going away"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['broadcast_deauth'] = '0'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
hapd.disable()
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.enable()
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Reconnection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
def test_fils_sk_erp_sim(dev, apdev, params):
"""FILS SK using ERP with SIM"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
realm = 'wlan.mnc001.mcc232.3gppnetwork.org'
start_erp_as(erp_domain=realm,
msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['fils_realm'] = realm
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="SIM", identity="1232010000000000@" + realm,
password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
erp="1", scan_freq="2412")
hapd.disable()
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd.enable()
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Reconnection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
def test_fils_sk_pfs_19(dev, apdev, params):
"""FILS SK with PFS (DH group 19)"""
run_fils_sk_pfs(dev, apdev, "19", params)
def test_fils_sk_pfs_20(dev, apdev, params):
"""FILS SK with PFS (DH group 20)"""
run_fils_sk_pfs(dev, apdev, "20", params)
def test_fils_sk_pfs_21(dev, apdev, params):
"""FILS SK with PFS (DH group 21)"""
run_fils_sk_pfs(dev, apdev, "21", params)
def test_fils_sk_pfs_25(dev, apdev, params):
"""FILS SK with PFS (DH group 25)"""
run_fils_sk_pfs(dev, apdev, "25", params)
def test_fils_sk_pfs_26(dev, apdev, params):
"""FILS SK with PFS (DH group 26)"""
run_fils_sk_pfs(dev, apdev, "26", params)
def test_fils_sk_pfs_27(dev, apdev, params):
"""FILS SK with PFS (DH group 27)"""
run_fils_sk_pfs(dev, apdev, "27", params)
def test_fils_sk_pfs_28(dev, apdev, params):
"""FILS SK with PFS (DH group 28)"""
run_fils_sk_pfs(dev, apdev, "28", params)
def test_fils_sk_pfs_29(dev, apdev, params):
"""FILS SK with PFS (DH group 29)"""
run_fils_sk_pfs(dev, apdev, "29", params)
def test_fils_sk_pfs_30(dev, apdev, params):
"""FILS SK with PFS (DH group 30)"""
run_fils_sk_pfs(dev, apdev, "30", params)
def run_fils_sk_pfs(dev, apdev, group, params):
check_fils_sk_pfs_capa(dev[0])
check_erp_capa(dev[0])
tls = dev[0].request("GET tls_library")
if int(group) in [25]:
if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls)):
raise HwsimSkip("EC group not supported")
if int(group) in [27, 28, 29, 30]:
if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls)):
raise HwsimSkip("Brainpool EC group not supported")
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['fils_dh_group'] = group
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", fils_dh_group=group, scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_pfs_group_mismatch(dev, apdev, params):
"""FILS SK PFS DH group mismatch"""
check_fils_sk_pfs_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['fils_dh_group'] = "20"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", fils_dh_group="19", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("Authentication rejection not seen")
if "auth_type=5 auth_transaction=2 status_code=77" not in ev:
raise Exception("Unexpected auth reject value: " + ev)
def test_fils_sk_pfs_pmksa_caching(dev, apdev, params):
"""FILS SK with PFS and PMKSA caching"""
check_fils_sk_pfs_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['fils_dh_group'] = "19"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", fils_dh_group="19", scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS authentication with PMKSA caching and PFS
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
# Verify EAPOL reauthentication after FILS authentication
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS authentication with ERP and PFS
dev[0].request("PMKSA_FLUSH")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-EAP-SUCCESS",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using ERP and PFS timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "CTRL-EVENT-EAP-SUCCESS" not in ev:
raise Exception("ERP success not reported")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"SME: Trying to authenticate",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using ERP and PFS timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "SME: Trying to authenticate" in ev:
raise Exception("Unexpected extra authentication round with ERP and PFS")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa3 = dev[0].get_pmksa(bssid)
if pmksa3 is None:
raise Exception("No PMKSA cache entry found")
if pmksa2['pmkid'] == pmksa3['pmkid']:
raise Exception("PMKID did not change")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# FILS authentication with PMKSA caching and PFS
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
hwsim_utils.test_connectivity(dev[0], hapd)
pmksa4 = dev[0].get_pmksa(bssid)
if pmksa4 is None:
raise Exception("No PMKSA cache entry found")
if pmksa3['pmkid'] != pmksa4['pmkid']:
raise Exception("Unexpected PMKID change (2)")
def test_fils_sk_auth_mismatch(dev, apdev, params):
"""FILS SK authentication type mismatch (PFS not supported)"""
check_fils_sk_pfs_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", fils_dh_group="19", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.dump_monitor()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" not in ev:
raise Exception("No EAP exchange seen")
dev[0].wait_connected()
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
def setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=0, wpa_group_rekey=0,
pmksa_caching=True, ext_key_id=False):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
if wpa_ptk_rekey:
params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
if wpa_group_rekey:
params['wpa_group_rekey'] = str(wpa_group_rekey)
if not pmksa_caching:
params['disable_pmksa_caching'] = '1'
if ext_key_id:
params['extended_key_id'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using ERP or PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
dev[0].dump_monitor()
hwsim_utils.test_connectivity(dev[0], hapd)
return hapd
def test_fils_auth_gtk_rekey(dev, apdev, params):
"""GTK rekeying after FILS authentication"""
hapd = setup_fils_rekey(dev, apdev, params, wpa_group_rekey=1)
ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
if ev is None:
raise Exception("GTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
if ev is not None:
raise Exception("Rekeying failed - disconnected")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_auth_ptk_rekey_ap(dev, apdev, params):
"""PTK rekeying after FILS authentication triggered by AP"""
hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2)
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
if ev is not None:
raise Exception("Rekeying failed - disconnected")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_auth_ptk_rekey_ap_erp(dev, apdev, params):
"""PTK rekeying after FILS authentication triggered by AP (ERP)"""
hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2,
pmksa_caching=False)
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
if ev is None:
raise Exception("PTK rekey timed out")
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
if ev is not None:
raise Exception("Rekeying failed - disconnected")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_and_ft(dev, apdev, params):
"""FILS SK using ERP and FT initial mobility domain association"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
er = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
dev[0].flush_scan_cache()
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
params = hostapd.wpa2_eap_params(ssid="fils-ft")
params['wpa_key_mgmt'] = "FILS-SHA256 FT-FILS-SHA256 FT-EAP"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params["mobility_domain"] = "a1b2"
params["r0_key_lifetime"] = "10000"
params["pmk_r1_push"] = "1"
params["reassociation_deadline"] = "1000"
params['nas_identifier'] = "nas1.w1.fi"
params['r1_key_holder'] = "000102030405"
params['r0kh'] = ["02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
params['ieee80211w'] = "1"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].dump_monitor()
id = dev[0].connect("fils-ft", key_mgmt="FILS-SHA256 FT-FILS-SHA256 FT-EAP",
ieee80211w="1",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-AUTH-REJECT",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "CTRL-EVENT-AUTH-REJECT" in ev:
raise Exception("Authentication failed")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
er.disable()
# FIX: FT-FILS-SHA256 does not currently work for FT protocol due to not
# fully defined FT Reassociation Request/Response frame MIC use in FTE.
# FT-EAP can be used to work around that in this test case to confirm the
# FT key hierarchy was properly formed in the previous step.
#params['wpa_key_mgmt'] = "FILS-SHA256 FT-FILS-SHA256"
params['wpa_key_mgmt'] = "FT-EAP"
params['nas_identifier'] = "nas2.w1.fi"
params['r1_key_holder'] = "000102030406"
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
# FIX: Cannot use FT-over-DS without the FTE MIC issue addressed
#dev[0].roam_over_ds(apdev[1]['bssid'])
dev[0].roam(apdev[1]['bssid'])
def test_fils_and_ft_over_air(dev, apdev, params):
"""FILS SK using ERP and FT-over-air (SHA256)"""
run_fils_and_ft_over_air(dev, apdev, params, "FT-FILS-SHA256")
def test_fils_and_ft_over_air_sha384(dev, apdev, params):
"""FILS SK using ERP and FT-over-air (SHA384)"""
run_fils_and_ft_over_air(dev, apdev, params, "FT-FILS-SHA384")
def run_fils_and_ft_over_air(dev, apdev, params, key_mgmt):
hapd, hapd2 = run_fils_and_ft_setup(dev, apdev, params, key_mgmt)
conf = hapd.request("GET_CONFIG")
if "key_mgmt=" + key_mgmt not in conf.splitlines():
logger.info("GET_CONFIG:\n" + conf)
raise Exception("GET_CONFIG did not report correct key_mgmt")
logger.info("FT protocol using FT key hierarchy established during FILS authentication")
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
hapd.request("NOTE FT protocol to AP2 using FT keys established during FILS FILS authentication")
dev[0].roam(apdev[1]['bssid'])
hwsim_utils.test_connectivity(dev[0], hapd2)
logger.info("FT protocol using the previously established FT key hierarchy from FILS authentication")
hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication")
dev[0].roam(apdev[0]['bssid'])
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("NOTE FT protocol back to AP2 using FT keys established during FILS FILS authentication")
dev[0].roam(apdev[1]['bssid'])
hwsim_utils.test_connectivity(dev[0], hapd2)
hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication (2)")
dev[0].roam(apdev[0]['bssid'])
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_and_ft_over_ds(dev, apdev, params):
"""FILS SK using ERP and FT-over-DS (SHA256)"""
run_fils_and_ft_over_ds(dev, apdev, params, "FT-FILS-SHA256")
def test_fils_and_ft_over_ds_sha384(dev, apdev, params):
"""FILS SK using ERP and FT-over-DS (SHA384)"""
run_fils_and_ft_over_ds(dev, apdev, params, "FT-FILS-SHA384")
def run_fils_and_ft_over_ds(dev, apdev, params, key_mgmt):
hapd, hapd2 = run_fils_and_ft_setup(dev, apdev, params, key_mgmt)
logger.info("FT protocol using FT key hierarchy established during FILS authentication")
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
hapd.request("NOTE FT protocol to AP2 using FT keys established during FILS FILS authentication")
dev[0].roam_over_ds(apdev[1]['bssid'])
logger.info("FT protocol using the previously established FT key hierarchy from FILS authentication")
hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication")
dev[0].roam_over_ds(apdev[0]['bssid'])
hapd.request("NOTE FT protocol back to AP2 using FT keys established during FILS FILS authentication")
dev[0].roam_over_ds(apdev[1]['bssid'])
hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication (2)")
dev[0].roam_over_ds(apdev[0]['bssid'])
def run_fils_and_ft_setup(dev, apdev, params, key_mgmt):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
er = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
logger.info("Set up ERP key hierarchy without FILS/FT authentication")
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = key_mgmt
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['ieee80211w'] = "2"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
hapd.request("NOTE Initial association to establish ERP keys")
id = dev[0].connect("fils", key_mgmt=key_mgmt, ieee80211w="2",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.disable()
dev[0].flush_scan_cache()
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
logger.info("Initial mobility domain association using FILS authentication")
params = hostapd.wpa2_eap_params(ssid="fils-ft")
params['wpa_key_mgmt'] = key_mgmt
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params["mobility_domain"] = "a1b2"
params["r0_key_lifetime"] = "10000"
params["pmk_r1_push"] = "1"
params["reassociation_deadline"] = "1000"
params['nas_identifier'] = "nas1.w1.fi"
params['r1_key_holder'] = "000102030405"
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
params['ieee80211w'] = "2"
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].dump_monitor()
hapd.request("NOTE Initial FT mobility domain association using FILS authentication")
dev[0].set_network_quoted(id, "ssid", "fils-ft")
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-AUTH-REJECT",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "CTRL-EVENT-AUTH-REJECT" in ev:
raise Exception("Authentication failed")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
er.disable()
params['wpa_key_mgmt'] = key_mgmt
params['nas_identifier'] = "nas2.w1.fi"
params['r1_key_holder'] = "000102030406"
params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
"02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
return hapd, hapd2
def test_fils_assoc_replay(dev, apdev, params):
"""FILS AP and replayed Association Request frame"""
capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as()
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
assocreq = None
count = 0
while count < 100:
req = hapd.mgmt_rx()
count += 1
hapd.dump_monitor()
hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
if req['subtype'] == 0:
assocreq = req
ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
break
hapd.set("ext_mgmt_frame_handling", "0")
if assocreq is None:
raise Exception("No Association Request frame seen")
dev[0].wait_connected()
dev[0].dump_monitor()
hapd.dump_monitor()
hwsim_utils.test_connectivity(dev[0], hapd)
logger.info("Replay the last Association Request frame")
hapd.dump_monitor()
hapd.set("ext_mgmt_frame_handling", "1")
hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status seen")
cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
if "OK" not in hapd.request(cmd):
raise Exception("MGMT_TX_STATUS_PROCESS failed")
hapd.set("ext_mgmt_frame_handling", "0")
try:
hwsim_utils.test_connectivity(dev[0], hapd)
ok = True
except:
ok = False
ap = hapd.own_addr()
sta = dev[0].own_addr()
filt = "wlan.fc.type == 2 && " + \
"wlan.da == " + sta + " && " + \
"wlan.sa == " + ap + " && wlan.ccmp.extiv"
fields = ["wlan.ccmp.extiv"]
res = run_tshark(capfile, filt, fields)
vals = res.splitlines()
logger.info("CCMP PN: " + str(vals))
if len(vals) < 2:
raise Exception("Could not find all CCMP protected frames from capture")
if len(set(vals)) < len(vals):
raise Exception("Duplicate CCMP PN used")
if not ok:
raise Exception("The second hwsim connectivity test failed")
def test_fils_sk_erp_server_flush(dev, apdev, params):
"""FILS SK ERP and ERP flush on server, but not on peer"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
hapd_as = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
hapd_as.request("ERP_FLUSH")
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
if ev is None:
raise Exception("No authentication rejection seen after ERP flush on server")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-AUTH-REJECT",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection attempt using FILS/ERP timed out")
if "CTRL-EVENT-AUTH-REJECT" in ev:
raise Exception("Failed to recover from ERP flush on server")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
if "CTRL-EVENT-EAP-STARTED" not in ev:
raise Exception("New EAP exchange not seen")
dev[0].wait_connected(error="Connection timeout after ERP flush")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-AUTH-REJECT",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection attempt using FILS with new ERP keys timed out")
if "CTRL-EVENT-AUTH-REJECT" in ev:
raise Exception("Authentication failed with new ERP keys")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed with new ERP keys")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
def test_fils_sk_erp_radius_ext(dev, apdev, params):
"""FILS SK using ERP and external RADIUS server"""
as_hapd = hostapd.Hostapd("as")
try:
as_hapd.disable()
as_hapd.set("eap_server_erp", "1")
as_hapd.set("erp_domain", "erp.example.com")
as_hapd.enable()
run_fils_sk_erp_radius_ext(dev, apdev, params)
finally:
as_hapd.disable()
as_hapd.set("eap_server_erp", "0")
as_hapd.set("erp_domain", "")
as_hapd.enable()
def run_fils_sk_erp_radius_ext(dev, apdev, params):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['erp_domain'] = 'erp.example.com'
params['fils_realm'] = 'erp.example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PWD", identity="pwd@erp.example.com",
password="secret password",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_fils_sk_erp_radius_roam(dev, apdev):
"""FILS SK/ERP and roaming with different AKM"""
as_hapd = hostapd.Hostapd("as")
try:
as_hapd.disable()
as_hapd.set("eap_server_erp", "1")
as_hapd.set("erp_domain", "example.com")
as_hapd.enable()
run_fils_sk_erp_radius_roam(dev, apdev)
finally:
as_hapd.disable()
as_hapd.set("eap_server_erp", "0")
as_hapd.set("erp_domain", "")
as_hapd.enable()
def run_fils_sk_erp_radius_roam(dev, apdev):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256 FILS-SHA384",
eap="PWD", identity="erp-pwd@example.com",
password="secret password",
erp="1", scan_freq="2412")
bssid2 = apdev[1]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA384"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].dump_monitor()
if "OK" not in dev[0].request("ROAM " + bssid2):
raise Exception("ROAM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using PMKSA caching timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if bssid2 not in ev:
raise Exception("Failed to connect to the second AP")
hapd2.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd2)
def test_fils_sk_erp_roam_diff_akm(dev, apdev, params):
"""FILS SK using ERP and SHA256/SHA384 change in roam"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as()
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt="FILS-SHA256 FILS-SHA384",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
bssid2 = apdev[1]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256 FILS-SHA384"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].dump_monitor()
if "OK" not in dev[0].request("ROAM " + bssid2):
raise Exception("ROAM failed")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming using FILS timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if bssid2 not in ev:
raise Exception("Failed to connect to the second AP")
hwsim_utils.test_connectivity(dev[0], hapd2)
def test_fils_auth_ptk_rekey_ap_ext_key_id(dev, apdev, params):
"""PTK rekeying after FILS authentication triggered by AP (Ext Key ID)"""
check_ext_key_id_capa(dev[0])
try:
dev[0].set("extended_key_id", "1")
hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2,
ext_key_id=True)
check_ext_key_id_capa(hapd)
idx = int(dev[0].request("GET last_tk_key_idx"))
if idx != 0:
raise Exception("Unexpected Key ID before TK rekey: %d" % idx)
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
if ev is None:
raise Exception("PTK rekey timed out")
idx = int(dev[0].request("GET last_tk_key_idx"))
if idx != 1:
raise Exception("Unexpected Key ID after TK rekey: %d" % idx)
hwsim_utils.test_connectivity(dev[0], hapd)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
if ev is not None:
raise Exception("Rekeying failed - disconnected")
hwsim_utils.test_connectivity(dev[0], hapd)
finally:
dev[0].set("extended_key_id", "0")
def test_fils_discovery_frame(dev, apdev, params):
"""FILS Discovery frame generation"""
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['wpa_group_rekey'] = '1'
params['fils_discovery_min_interval'] = '20'
params['fils_discovery_max_interval'] = '20'
hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
if "OK" not in hapd.request("ENABLE"):
raise HwsimSkip("FILS Discovery frame transmission not supported")
ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
if ev is None:
raise Exception("AP startup timed out")
if "AP-ENABLED" not in ev:
raise Exception("AP startup failed")
dev[0].request("ERP_FLUSH")
dev[0].connect("fils", key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
+
+def test_fils_offload_to_driver(dev, apdev, params):
+ """FILS offload to driver"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+ run_fils_offload_to_driver(dev[0], apdev, params)
+
+def test_fils_offload_to_driver2(dev, apdev, params):
+ """FILS offload to driver"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ run_fils_offload_to_driver(wpas, apdev, params)
+
+def run_fils_offload_to_driver(dev, apdev, params):
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev.request("ERP_FLUSH")
+ id = dev.connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ p = "freq=2412 authorized=1 fils_erp_next_seq_num=4"
+ if "OK" not in dev.request("DRIVER_EVENT ASSOC " + p):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
+ dev.wait_connected()
+
+ dev.request("DISCONNECT")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+ dev.select_network(id, freq=2412)
+ dev.wait_connected()
+ dev.dump_monitor()
+
+ # This does not really work properly with SME-in-wpa_supplicant case
+ p = "freq=2412 authorized=1 fils_erp_next_seq_num=4"
+ if "OK" not in dev.request("DRIVER_EVENT ASSOC " + p):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
+
+ dev.wait_connected()
diff --git a/tests/hwsim/test_he.py b/tests/hwsim/test_he.py
index 68633c81c2cb..2593f35f0bf1 100644
--- a/tests/hwsim/test_he.py
+++ b/tests/hwsim/test_he.py
@@ -1,1170 +1,1188 @@
# HE tests
# Copyright (c) 2019, The Linux Foundation
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import logging
logger = logging.getLogger()
import os
import subprocess, time
import hwsim_utils
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
from test_dfs import wait_dfs_event
def test_he_open(dev, apdev):
"""HE AP with open mode configuration"""
params = {"ssid": "he",
"ieee80211ax": "1",
"he_bss_color": "42",
"he_mu_edca_ac_be_ecwmin": "7",
"he_mu_edca_ac_be_ecwmax": "15"}
hapd = hostapd.add_ap(apdev[0], params)
if hapd.get_status_field("ieee80211ax") != "1":
- raise Exception("STATUS did not indicate ieee80211ac=1")
+ raise Exception("STATUS did not indicate ieee80211ax=1")
dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
sta = hapd.get_sta(dev[0].own_addr())
if "[HE]" not in sta['flags']:
raise Exception("Missing STA flag: HE")
def test_he_disabled_on_sta(dev, apdev):
"""HE AP and HE disabled on STA"""
params = {"ssid": "he",
"ieee80211ax": "1",
"he_bss_color": "42",
"he_mu_edca_ac_be_ecwmin": "7",
"he_mu_edca_ac_be_ecwmax": "15"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("he", key_mgmt="NONE", scan_freq="2412", disable_he="1")
sta = hapd.get_sta(dev[0].own_addr())
if "[HE]" in sta['flags']:
raise Exception("Unexpected STA flag: HE")
def test_he_params(dev, apdev):
"""HE AP parameters"""
params = {"ssid": "he",
"ieee80211ax": "1",
"he_bss_color": "42",
"he_mu_edca_ac_be_ecwmin": "7",
"he_mu_edca_ac_be_ecwmax": "15",
"he_su_beamformer": "0",
"he_su_beamformee": "0",
"he_default_pe_duration": "4",
"he_twt_required": "1",
"he_rts_threshold": "64",
"he_basic_mcs_nss_set": "65535",
"he_mu_edca_qos_info_param_count": "0",
"he_mu_edca_qos_info_q_ack": "0",
"he_mu_edca_qos_info_queue_request": "1",
"he_mu_edca_qos_info_txop_request": "0",
"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",
"he_spr_sr_control": "0",
"he_spr_non_srg_obss_pd_max_offset": "0",
"he_spr_srg_obss_pd_min_offset": "0",
"he_spr_srg_obss_pd_max_offset": "0",
"he_spr_srg_bss_colors": "1 2 10 63",
"he_spr_srg_partial_bssid": "0 1 3 63",
"he_6ghz_max_ampdu_len_exp": "7",
"he_6ghz_rx_ant_pat": "1",
"he_6ghz_tx_ant_pat": "1",
"he_6ghz_max_mpdu": "2",
"he_oper_chwidth": "0",
"he_oper_centr_freq_seg0_idx": "1",
"he_oper_centr_freq_seg1_idx": "0"}
hapd = hostapd.add_ap(apdev[0], params)
if hapd.get_status_field("ieee80211ax") != "1":
- raise Exception("STATUS did not indicate ieee80211ac=1")
+ raise Exception("STATUS did not indicate ieee80211ax=1")
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+
+def test_he_spr_params(dev, apdev):
+ """HE AP spatial reuse parameters"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_spr_sr_control": "12",
+ "he_spr_non_srg_obss_pd_max_offset": "1",
+ "he_spr_srg_obss_pd_min_offset": "2",
+ "he_spr_srg_obss_pd_max_offset": "3",
+ "he_spr_srg_bss_colors": "1 2 10 63",
+ "he_spr_srg_partial_bssid": "0 1 3 63",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "1",
+ "he_oper_centr_freq_seg1_idx": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_status_field("ieee80211ax") != "1":
+ raise Exception("STATUS did not indicate ieee80211ax=1")
dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
def he_supported():
cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
reg = cmd.stdout.read().decode()
if "@ 80)" in reg or "@ 160)" in reg:
return True
return False
def test_he80(dev, apdev):
"""HE with 80 MHz channel width"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_capab": "[MAX-MPDU-11454]",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=5180" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=80 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
est = dev[0].get_bss(bssid)['est_throughput']
if est != "390001":
raise Exception("Unexpected BSS est_throughput: " + est)
status = dev[0].get_status()
if status["ieee80211ac"] != "1":
raise Exception("Unexpected STATUS ieee80211ac value (STA)")
status = hapd.get_status()
logger.info("hostapd STATUS: " + str(status))
if status["ieee80211n"] != "1":
raise Exception("Unexpected STATUS ieee80211n value")
if status["ieee80211ac"] != "1":
raise Exception("Unexpected STATUS ieee80211ac value")
if status["ieee80211ax"] != "1":
raise Exception("Unexpected STATUS ieee80211ax value")
if status["secondary_channel"] != "1":
raise Exception("Unexpected STATUS secondary_channel value")
if status["vht_oper_chwidth"] != "1":
raise Exception("Unexpected STATUS vht_oper_chwidth value")
if status["vht_oper_centr_freq_seg0_idx"] != "42":
raise Exception("Unexpected STATUS vht_oper_centr_freq_seg0_idx value")
if "vht_caps_info" not in status:
raise Exception("Missing vht_caps_info")
if status["he_oper_chwidth"] != "1":
raise Exception("Unexpected STATUS he_oper_chwidth value")
if status["he_oper_centr_freq_seg0_idx"] != "42":
raise Exception("Unexpected STATUS he_oper_centr_freq_seg0_idx value")
sta = hapd.get_sta(dev[0].own_addr())
logger.info("hostapd STA: " + str(sta))
if "[HT]" not in sta['flags']:
raise Exception("Missing STA flag: HT")
if "[VHT]" not in sta['flags']:
raise Exception("Missing STA flag: VHT")
if "[HE]" not in sta['flags']:
raise Exception("Missing STA flag: HE")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def _test_he_wifi_generation(dev, apdev, conf, scan_freq):
"""HE and wifi_generation"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"ieee80211n": "1",
"ieee80211ax": "1"}
params.update(conf)
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("he", key_mgmt="NONE", scan_freq=scan_freq)
status = dev[0].get_status()
if 'wifi_generation' not in status:
# For now, assume this is because of missing kernel support
raise HwsimSkip("Association Request IE reporting not supported")
#raise Exception("Missing wifi_generation information")
if status['wifi_generation'] != "6":
raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.connect("he", key_mgmt="NONE", scan_freq=scan_freq)
status = wpas.get_status()
if 'wifi_generation' not in status:
# For now, assume this is because of missing kernel support
raise HwsimSkip("Association Request IE reporting not supported")
#raise Exception("Missing wifi_generation information (connect)")
if status['wifi_generation'] != "6":
raise Exception("Unexpected wifi_generation value (connect): " + status['wifi_generation'])
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def test_he_wifi_generation(dev, apdev):
conf = {
"vht_oper_chwidth": "1",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42",
"vht_capab": "[MAX-MPDU-11454]",
"ieee80211ac": "1",
}
_test_he_wifi_generation(dev, apdev, conf, "5180")
def test_he_wifi_generation_24(dev, apdev):
conf = {
"hw_mode": "g",
"channel": "1",
}
_test_he_wifi_generation(dev, apdev, conf, "2412")
def he80_test(apdev, dev, channel, ht_capab):
clear_scan_cache(apdev)
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": str(channel),
"ht_capab": ht_capab,
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42"}
hapd = hostapd.add_ap(apdev, params)
bssid = apdev['bssid']
dev[0].connect("he", key_mgmt="NONE",
scan_freq=str(5000 + 5 * channel))
hwsim_utils.test_connectivity(dev[0], hapd)
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he80b(dev, apdev):
"""HE with 80 MHz channel width (HT40- channel 40)"""
he80_test(apdev[0], dev, 40, "[HT40-]")
def test_he80c(dev, apdev):
"""HE with 80 MHz channel width (HT40+ channel 44)"""
he80_test(apdev[0], dev, 44, "[HT40+]")
def test_he80d(dev, apdev):
"""HE with 80 MHz channel width (HT40- channel 48)"""
he80_test(apdev[0], dev, 48, "[HT40-]")
def test_he80_params(dev, apdev):
"""HE with 80 MHz channel width and number of optional features enabled"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_capab": "[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP0]",
"vht_oper_centr_freq_seg0_idx": "42",
"require_vht": "1",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42",
"he_su_beamformer": "1",
"he_mu_beamformer": "1",
"he_bss_color":"1",
"he_default_pe_duration":"1",
"he_twt_required":"1",
"he_rts_threshold":"1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[1].connect("he", key_mgmt="NONE", scan_freq="5180",
disable_vht="1", wait_connect=False)
dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
dev[2].connect("he", key_mgmt="NONE", scan_freq="5180",
disable_sgi="1")
ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
if ev is None:
raise Exception("Association rejection timed out")
if "status_code=104" not in ev:
raise Exception("Unexpected rejection status code")
dev[1].request("DISCONNECT")
hwsim_utils.test_connectivity(dev[0], hapd)
sta0 = hapd.get_sta(dev[0].own_addr())
sta2 = hapd.get_sta(dev[2].own_addr())
capab0 = int(sta0['vht_caps_info'], base=16)
capab2 = int(sta2['vht_caps_info'], base=16)
if capab0 & 0x60 == 0:
raise Exception("dev[0] did not support SGI")
if capab2 & 0x60 != 0:
raise Exception("dev[2] claimed support for SGI")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev, count=3)
def test_he80_invalid(dev, apdev):
"""HE with invalid 80 MHz channel configuration (seg1)"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"vht_oper_centr_freq_seg1_idx": "159",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42",
"he_oper_centr_freq_seg1_idx": "155",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
# This fails due to unexpected seg1 configuration
ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
if ev is None:
raise Exception("AP-DISABLED not reported")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he80_invalid2(dev, apdev):
"""HE with invalid 80 MHz channel configuration (seg0)"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "46",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
# This fails due to invalid seg0 configuration
ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
if ev is None:
raise Exception("AP-DISABLED not reported")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he_20(devs, apdevs):
"""HE and 20 MHz channel"""
dev = devs[0]
ap = apdevs[0]
try:
hapd = None
params = {"ssid": "test-he20",
"country_code": "DE",
"hw_mode": "a",
"channel": "36",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"ht_capab": "",
"vht_capab": "",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "0",
"supported_rates": "60 120 240 360 480 540",
"require_vht": "1",
"he_oper_chwidth": "0",
"he_oper_centr_freq_seg0_idx": "0"}
hapd = hostapd.add_ap(ap, params)
dev.connect("test-he20", scan_freq="5180", key_mgmt="NONE")
hwsim_utils.test_connectivity(dev, hapd)
finally:
dev.request("DISCONNECT")
clear_regdom(hapd, devs)
def test_he_40(devs, apdevs):
"""HE and 40 MHz channel"""
dev = devs[0]
ap = apdevs[0]
try:
hapd = None
params = {"ssid": "test-he40",
"country_code": "DE",
"hw_mode": "a",
"channel": "36",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"ht_capab": "[HT40+]",
"vht_capab": "",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "38",
"he_oper_chwidth": "0",
"he_oper_centr_freq_seg0_idx": "38",
"he_su_beamformer": "1",
"he_mu_beamformer": "1"}
hapd = hostapd.add_ap(ap, params)
dev.connect("test-he40", scan_freq="5180", key_mgmt="NONE")
hwsim_utils.test_connectivity(dev, hapd)
finally:
dev.request("DISCONNECT")
clear_regdom(hapd, devs)
@long_duration_test
def test_he160(dev, apdev):
"""HE with 160 MHz channel width (1)"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"vht_capab": "[VHT160]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "50",
"he_oper_chwidth": "2",
"he_oper_centr_freq_seg0_idx": "50",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
if "DFS-CAC-START" not in ev:
raise Exception("Unexpected DFS event")
state = hapd.get_status_field("state")
if state != "DFS":
if state == "DISABLED" and not os.path.exists("dfs"):
# Not all systems have recent enough CRDA version and
# wireless-regdb changes to support 160 MHz and DFS. For now,
# do not report failures for this test case.
raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
raise Exception("Unexpected interface state: " + state)
logger.info("Waiting for CAC to complete")
ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
if "success=1" not in ev:
raise Exception("CAC failed")
if "freq=5180" not in ev:
raise Exception("Unexpected DFS freq result")
ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
if not ev:
raise Exception("AP setup timed out")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state")
dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
dev[0].wait_regdom(country_ie=True)
hwsim_utils.test_connectivity(dev[0], hapd)
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=5180" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=160 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
@long_duration_test
def test_he160b(dev, apdev):
"""HE with 160 MHz channel width (2)"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "104",
"ht_capab": "[HT40-]",
"vht_capab": "[VHT160]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "114",
"he_oper_chwidth": "2",
"he_oper_centr_freq_seg0_idx": "114",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[1], params, wait_enabled=False)
ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
if "DFS-CAC-START" not in ev:
raise Exception("Unexpected DFS event(2)")
state = hapd.get_status_field("state")
if state != "DFS":
if state == "DISABLED" and not os.path.exists("dfs"):
# Not all systems have recent enough CRDA version and
# wireless-regdb changes to support 160 MHz and DFS. For now,
# do not report failures for this test case.
raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
raise Exception("Unexpected interface state: " + state)
logger.info("Waiting for CAC to complete")
ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
if "success=1" not in ev:
raise Exception("CAC failed(2)")
if "freq=5520" not in ev:
raise Exception("Unexpected DFS freq result(2)")
ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
if not ev:
raise Exception("AP setup timed out(2)")
state = hapd.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state(2)")
freq = hapd.get_status_field("freq")
if freq != "5520":
raise Exception("Unexpected frequency(2)")
dev[0].connect("he", key_mgmt="NONE", scan_freq="5520")
dev[0].wait_regdom(country_ie=True)
hwsim_utils.test_connectivity(dev[0], hapd)
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=5520" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=160 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def test_he160_no_dfs_100_plus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (100 plus)"""
run_ap_he160_no_dfs(dev, apdev, "100", "[HT40+]")
def test_he160_no_dfs(dev, apdev):
"""HE with 160 MHz channel width and no DFS (104 minus)"""
run_ap_he160_no_dfs(dev, apdev, "104", "[HT40-]")
def test_he160_no_dfs_108_plus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (108 plus)"""
run_ap_he160_no_dfs(dev, apdev, "108", "[HT40+]")
def test_he160_no_dfs_112_minus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (112 minus)"""
run_ap_he160_no_dfs(dev, apdev, "112", "[HT40-]")
def test_he160_no_dfs_116_plus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (116 plus)"""
run_ap_he160_no_dfs(dev, apdev, "116", "[HT40+]")
def test_he160_no_dfs_120_minus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (120 minus)"""
run_ap_he160_no_dfs(dev, apdev, "120", "[HT40-]")
def test_he160_no_dfs_124_plus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (124 plus)"""
run_ap_he160_no_dfs(dev, apdev, "124", "[HT40+]")
def test_he160_no_dfs_128_minus(dev, apdev):
"""HE with 160 MHz channel width and no DFS (128 minus)"""
run_ap_he160_no_dfs(dev, apdev, "128", "[HT40-]")
def run_ap_he160_no_dfs(dev, apdev, channel, ht_capab):
try:
hapd = None
params = {"ssid": "he",
"country_code": "ZA",
"hw_mode": "a",
"channel": channel,
"ht_capab": ht_capab,
"vht_capab": "[VHT160]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "114",
"he_oper_chwidth": "2",
"he_oper_centr_freq_seg0_idx": "114",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = hapd.wait_event(["AP-ENABLED"], timeout=2)
if not ev:
cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
reg = cmd.stdout.readlines()
for r in reg:
if b"5490" in r and b"DFS" in r:
raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
raise Exception("AP setup timed out")
freq = str(int(channel) * 5 + 5000)
dev[0].connect("he", key_mgmt="NONE", scan_freq=freq)
dev[0].wait_regdom(country_ie=True)
hwsim_utils.test_connectivity(dev[0], hapd)
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=" + freq not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=160 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he160_no_ht40(dev, apdev):
"""HE with 160 MHz channel width and HT40 disabled"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "ZA",
"hw_mode": "a",
"channel": "108",
"ht_capab": "",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "114",
"he_oper_chwidth": "2",
"he_oper_centr_freq_seg0_idx": "114",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=2)
if not ev:
cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
reg = cmd.stdout.readlines()
for r in reg:
if "5490" in r and "DFS" in r:
raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
raise Exception("AP setup timed out")
if "AP-ENABLED" in ev:
# This was supposed to fail due to sec_channel_offset == 0
raise Exception("Unexpected AP-ENABLED")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he80plus80(dev, apdev):
"""HE with 80+80 MHz channel width"""
try:
hapd = None
hapd2 = None
params = {"ssid": "he",
"country_code": "US",
"hw_mode": "a",
"channel": "52",
"ht_capab": "[HT40+]",
"vht_capab": "[VHT160-80PLUS80]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "3",
"vht_oper_centr_freq_seg0_idx": "58",
"vht_oper_centr_freq_seg1_idx": "155",
"he_oper_chwidth": "3",
"he_oper_centr_freq_seg0_idx": "58",
"he_oper_centr_freq_seg1_idx": "155",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
# This will actually fail since DFS on 80+80 is not yet supported
ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
# ignore result to avoid breaking the test once 80+80 DFS gets enabled
params = {"ssid": "he2",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"vht_capab": "[VHT160-80PLUS80]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "3",
"vht_oper_centr_freq_seg0_idx": "42",
"vht_oper_centr_freq_seg1_idx": "155",
"he_oper_chwidth": "3",
"he_oper_centr_freq_seg0_idx": "42",
"he_oper_centr_freq_seg1_idx": "155"}
hapd2 = hostapd.add_ap(apdev[1], params, wait_enabled=False)
ev = hapd2.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
if not ev:
raise Exception("AP setup timed out(2)")
if "AP-DISABLED" in ev:
# Assume this failed due to missing regulatory update for now
raise HwsimSkip("80+80 MHz channel not supported in regulatory information")
state = hapd2.get_status_field("state")
if state != "ENABLED":
raise Exception("Unexpected interface state(2)")
dev[1].connect("he2", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[1], hapd2)
sig = dev[1].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=5180" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=80+80 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
if "CENTER_FRQ1=5210" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
if "CENTER_FRQ2=5775" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
dev[1].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
def test_he80plus80_invalid(dev, apdev):
"""HE with invalid 80+80 MHz channel"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "US",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "3",
"vht_oper_centr_freq_seg0_idx": "42",
"vht_oper_centr_freq_seg1_idx": "0",
"he_oper_chwidth": "3",
"he_oper_centr_freq_seg0_idx": "42",
"he_oper_centr_freq_seg1_idx": "0",
'ieee80211d': '1',
'ieee80211h': '1'}
hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
# This fails due to missing(invalid) seg1 configuration
ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
if ev is None:
raise Exception("AP-DISABLED not reported")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he80_csa(dev, apdev):
"""HE with 80 MHz channel width and CSA"""
csa_supported(dev[0])
try:
hapd = None
params = {"ssid": "he",
"country_code": "US",
"hw_mode": "a",
"channel": "149",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "155",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "155"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("he", key_mgmt="NONE", scan_freq="5745")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5180 ht vht he blocktx center_freq1=5210 sec_channel_offset=1 bandwidth=80")
ev = hapd.wait_event(["CTRL-EVENT-STARTED-CHANNEL-SWITCH"], timeout=10)
if ev is None:
raise Exception("Channel switch start event not seen")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CS started")
ev = hapd.wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
if ev is None:
raise Exception("Channel switch completion event not seen")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CS completed")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5180" not in ev:
raise Exception("Unexpected channel in CSA finished event")
time.sleep(0.5)
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("CHAN_SWITCH 5 5745")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
if ev is None:
raise Exception("CSA finished event timed out")
if "freq=5745" not in ev:
raise Exception("Unexpected channel in CSA finished event")
time.sleep(0.5)
hwsim_utils.test_connectivity(dev[0], hapd)
# This CSA to same channel will fail in kernel, so use this only for
# extra code coverage.
hapd.request("CHAN_SWITCH 5 5745")
hapd.wait_event(["AP-CSA-FINISHED"], timeout=1)
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def test_he_on_24ghz(dev, apdev):
"""Subset of HE features on 2.4 GHz"""
hapd = None
params = {"ssid": "test-he-2g",
"hw_mode": "g",
"channel": "1",
"ieee80211n": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "1",
"he_oper_chwidth": "0",
"he_oper_centr_freq_seg0_idx": "1"}
hapd = hostapd.add_ap(apdev[0], params)
try:
dev[0].connect("test-he-2g", scan_freq="2412", key_mgmt="NONE")
hwsim_utils.test_connectivity(dev[0], hapd)
sta = hapd.get_sta(dev[0].own_addr())
dev[1].connect("test-he-2g", scan_freq="2412", key_mgmt="NONE")
sta = hapd.get_sta(dev[1].own_addr())
finally:
dev[0].request("DISCONNECT")
dev[1].request("DISCONNECT")
if hapd:
hapd.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
def test_he80_pwr_constraint(dev, apdev):
"""HE with 80 MHz channel width and local power constraint"""
hapd = None
try:
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211d": "1",
"local_pwr_constraint": "3",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
dev[0].wait_regdom(country_ie=True)
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def test_he_use_sta_nsts(dev, apdev):
"""HE with 80 MHz channel width and use_sta_nsts=1"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42",
"use_sta_nsts": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_he_tkip(dev, apdev):
"""HE and TKIP"""
skip_without_tkip(dev[0])
try:
hapd = None
params = {"ssid": "he",
"wpa": "1",
"wpa_key_mgmt": "WPA-PSK",
"wpa_pairwise": "TKIP",
"wpa_passphrase": "12345678",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("he", psk="12345678", scan_freq="5180")
hwsim_utils.test_connectivity(dev[0], hapd)
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "FREQUENCY=5180" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
if "WIDTH=20 MHz (no HT)" not in sig:
raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
status = hapd.get_status()
logger.info("hostapd STATUS: " + str(status))
if status["ieee80211n"] != "0":
raise Exception("Unexpected STATUS ieee80211n value")
if status["ieee80211ac"] != "0":
raise Exception("Unexpected STATUS ieee80211ac value")
if status["ieee80211ax"] != "0":
raise Exception("Unexpected STATUS ieee80211ax value")
if status["secondary_channel"] != "0":
raise Exception("Unexpected STATUS secondary_channel value")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def test_he_40_fallback_to_20(devs, apdevs):
"""HE and 40 MHz channel configuration falling back to 20 MHz"""
dev = devs[0]
ap = apdevs[0]
try:
hapd = None
params = {"ssid": "test-he40",
"country_code": "US",
"hw_mode": "a",
"basic_rates": "60 120 240",
"channel": "161",
"ieee80211d": "1",
"ieee80211h": "1",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"ht_capab": "[HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
"vht_capab": "[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "155",
"he_oper_chwidth": "0",
"he_oper_centr_freq_seg0_idx": "155"}
hapd = hostapd.add_ap(ap, params)
dev.connect("test-he40", scan_freq="5805", key_mgmt="NONE")
dev.wait_regdom(country_ie=True)
hwsim_utils.test_connectivity(dev, hapd)
finally:
clear_regdom(hapd, devs)
def test_he80_to_24g_he(dev, apdev):
"""HE with 80 MHz channel width reconfigured to 2.4 GHz HE"""
try:
hapd = None
params = {"ssid": "he",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"ieee80211ax": "1",
"vht_oper_chwidth": "1",
"vht_capab": "[MAX-MPDU-11454]",
"vht_oper_centr_freq_seg0_idx": "42",
"he_oper_chwidth": "1",
"he_oper_centr_freq_seg0_idx": "42"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
hapd.disable()
hapd.set("ieee80211ac", "0")
hapd.set("hw_mode", "g")
hapd.set("channel", "1")
hapd.set("ht_capab", "")
hapd.set("vht_capab", "")
hapd.set("he_oper_chwidth", "")
hapd.set("he_oper_centr_freq_seg0_idx", "")
hapd.set("vht_oper_chwidth", "")
hapd.set("vht_oper_centr_freq_seg0_idx", "")
hapd.enable()
dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not he_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
clear_regdom(hapd, dev)
def test_he_twt(dev, apdev):
"""HE and TWT"""
params = {"ssid": "he",
"ieee80211ax": "1",
"he_bss_color": "42",
"he_twt_required":"1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
if "OK" not in dev[0].request("TWT_SETUP"):
raise Exception("TWT_SETUP failed")
if "OK" not in dev[0].request("TWT_TEARDOWN"):
raise Exception("TWT_SETUP failed")
if "OK" not in dev[0].request("TWT_SETUP dialog=123 exponent=9 mantissa=10 min_twt=254 setup_cmd=1 twt=1234567890 requestor=1 trigger=0 implicit=0 flow_type=0 flow_id=2 protection=1 twt_channel=3 control=16"):
raise Exception("TWT_SETUP failed")
if "OK" not in dev[0].request("TWT_TEARDOWN flags=255"):
raise Exception("TWT_SETUP failed")
def test_he_6ghz_security(dev, apdev):
"""HE AP and 6 GHz security parameter validation"""
params = {"ssid": "he",
"ieee80211ax": "1",
"op_class": "131",
"channel": "1"}
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
# Pre-RSNA security methods are not allowed in 6 GHz
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted(1)")
# Management frame protection is required in 6 GHz"
hapd.set("wpa", "2")
hapd.set("wpa_passphrase", "12345678")
hapd.set("wpa_key_mgmt", "SAE")
hapd.set("rsn_pairwise", "CCMP")
hapd.set("ieee80211w", "1")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted(2)")
# Invalid AKM suite for 6 GHz
hapd.set("ieee80211w", "2")
hapd.set("wpa_key_mgmt", "SAE WPA-PSK")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted(3)")
# Invalid pairwise cipher suite for 6 GHz
hapd.set("wpa_key_mgmt", "SAE")
hapd.set("rsn_pairwise", "CCMP TKIP")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted(4)")
# Invalid group cipher suite for 6 GHz
hapd.set("wpa_key_mgmt", "SAE")
hapd.set("rsn_pairwise", "CCMP")
hapd.set("group_cipher", "TKIP")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("Invalid configuration accepted(5)")
diff --git a/tests/hwsim/test_ieee8021x.py b/tests/hwsim/test_ieee8021x.py
index 89c282b396e0..630d6d0dbe92 100644
--- a/tests/hwsim/test_ieee8021x.py
+++ b/tests/hwsim/test_ieee8021x.py
@@ -1,514 +1,531 @@
# IEEE 802.1X tests
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import binascii
import hmac
import logging
import os
import time
import hostapd
import hwsim_utils
from utils import *
from tshark import run_tshark
logger = logging.getLogger()
def test_ieee8021x_wep104(dev, apdev):
"""IEEE 802.1X connection using dynamic WEP104"""
check_wep_capa(dev[0])
skip_with_fips(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "13"
params["wep_key_len_unicast"] = "13"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_wep40(dev, apdev):
"""IEEE 802.1X connection using dynamic WEP40"""
check_wep_capa(dev[0])
skip_with_fips(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "5"
params["wep_key_len_unicast"] = "5"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_wep_index_workaround(dev, apdev):
"""IEEE 802.1X and EAPOL-Key index workaround"""
check_wep_capa(dev[0])
skip_with_fips(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "5"
params["eapol_key_index_workaround"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eapol_flags="1",
eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
def test_ieee8021x_open(dev, apdev):
"""IEEE 802.1X connection using open network"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
logger.info("Test EAPOL-Logoff")
dev[0].request("LOGOFF")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
if ev is None:
raise Exception("Did not get disconnected")
if "reason=23" not in ev:
raise Exception("Unexpected disconnection reason")
dev[0].request("LOGON")
dev[0].connect_network(id)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_static_wep40(dev, apdev):
"""IEEE 802.1X connection using static WEP40"""
run_static_wep(dev, apdev, '"hello"')
def test_ieee8021x_static_wep104(dev, apdev):
"""IEEE 802.1X connection using static WEP104"""
run_static_wep(dev, apdev, '"hello-there-/"')
def run_static_wep(dev, apdev, key):
check_wep_capa(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key0"] = key
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
wep_key0=key, eapol_flags="0",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_proto(dev, apdev):
"""IEEE 802.1X and EAPOL supplicant protocol testing"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[1].request("SET ext_eapol_frame_io 1")
dev[1].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412", wait_connect=False)
id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
ev = dev[1].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
start = dev[0].get_mib()
tests = ["11",
"11223344",
"020000050a93000501",
"020300050a93000501",
"0203002c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"0203002c0100050000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"02aa00050a93000501"]
for frame in tests:
res = dev[0].request("EAPOL_RX " + bssid + " " + frame)
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
dev[1].request("EAPOL_RX " + bssid + " " + frame)
stop = dev[0].get_mib()
logger.info("MIB before test frames: " + str(start))
logger.info("MIB after test frames: " + str(stop))
vals = ['dot1xSuppInvalidEapolFramesRx',
'dot1xSuppEapLengthErrorFramesRx']
for val in vals:
if int(stop[val]) <= int(start[val]):
raise Exception(val + " did not increase")
@remote_compatible
def test_ieee8021x_eapol_start(dev, apdev):
"""IEEE 802.1X and EAPOL-Start retransmissions"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
addr0 = dev[0].own_addr()
hapd.set("ext_eapol_frame_io", "1")
try:
dev[0].request("SET EAPOL::startPeriod 1")
dev[0].request("SET EAPOL::maxStart 1")
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412", wait_connect=False)
held = False
for i in range(30):
pae = dev[0].get_status_field('Supplicant PAE state')
if pae == "HELD":
mib = hapd.get_sta(addr0, info="eapol")
if mib['auth_pae_state'] != 'AUTHENTICATING':
raise Exception("Unexpected Auth PAE state: " + mib['auth_pae_state'])
held = True
break
time.sleep(0.25)
if not held:
raise Exception("PAE state HELD not reached")
dev[0].wait_disconnected()
finally:
dev[0].request("SET EAPOL::startPeriod 30")
dev[0].request("SET EAPOL::maxStart 3")
def test_ieee8021x_held(dev, apdev):
"""IEEE 802.1X and HELD state"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
hapd.set("ext_eapol_frame_io", "1")
try:
dev[0].request("SET EAPOL::startPeriod 1")
dev[0].request("SET EAPOL::maxStart 0")
dev[0].request("SET EAPOL::heldPeriod 1")
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412", wait_connect=False)
held = False
for i in range(30):
pae = dev[0].get_status_field('Supplicant PAE state')
if pae == "HELD":
held = True
break
time.sleep(0.25)
if not held:
raise Exception("PAE state HELD not reached")
hapd.set("ext_eapol_frame_io", "0")
for i in range(30):
pae = dev[0].get_status_field('Supplicant PAE state')
if pae != "HELD":
held = False
break
time.sleep(0.25)
if held:
raise Exception("PAE state HELD not left")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection timed out")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Unexpected disconnection")
finally:
dev[0].request("SET EAPOL::startPeriod 30")
dev[0].request("SET EAPOL::maxStart 3")
dev[0].request("SET EAPOL::heldPeriod 60")
+def test_ieee8021x_force_unauth(dev, apdev):
+ """IEEE 802.1X and FORCE_UNAUTH state"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("SET EAPOL::portControl ForceUnauthorized")
+ pae = dev[0].get_status_field('Supplicant PAE state')
+ dev[0].wait_disconnected()
+ dev[0].request("SET EAPOL::portControl Auto")
+
def send_eapol_key(dev, bssid, signkey, frame_start, frame_end):
zero_sign = "00000000000000000000000000000000"
frame = frame_start + zero_sign + frame_end
hmac_obj = hmac.new(binascii.unhexlify(signkey), digestmod='MD5')
hmac_obj.update(binascii.unhexlify(frame))
sign = hmac_obj.digest()
frame = frame_start + binascii.hexlify(sign).decode() + frame_end
dev.request("EAPOL_RX " + bssid + " " + frame)
def test_ieee8021x_eapol_key(dev, apdev):
"""IEEE 802.1X connection and EAPOL-Key protocol tests"""
check_wep_capa(dev[0])
skip_with_fips(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "5"
params["wep_key_len_unicast"] = "5"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="VENDOR-TEST",
identity="vendor-test", scan_freq="2412")
# Hardcoded MSK from VENDOR-TEST
encrkey = "1111111111111111111111111111111111111111111111111111111111111111"
signkey = "2222222222222222222222222222222222222222222222222222222222222222"
# EAPOL-Key replay counter does not increase
send_eapol_key(dev[0], bssid, signkey,
"02030031" + "010005" + "0000000000000000" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
"1c636a30a4")
# EAPOL-Key too large Key Length field value
send_eapol_key(dev[0], bssid, signkey,
"02030031" + "010021" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
"1c636a30a4")
# EAPOL-Key too much key data
send_eapol_key(dev[0], bssid, signkey,
"0203004d" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
33*"ff")
# EAPOL-Key too little key data
send_eapol_key(dev[0], bssid, signkey,
"02030030" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
"1c636a30")
# EAPOL-Key with no key data and too long WEP key length
send_eapol_key(dev[0], bssid, signkey,
"0203002c" + "010020" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
"")
def test_ieee8021x_reauth(dev, apdev):
"""IEEE 802.1X and EAPOL_REAUTH request"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_reauth_wep(dev, apdev, params):
"""IEEE 802.1X and EAPOL_REAUTH request with WEP"""
check_wep_capa(dev[0])
logdir = params['logdir']
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "13"
params["wep_key_len_unicast"] = "13"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
"llc.type == 0x888e", ["eapol.type", "eap.code"])
if out is None:
raise Exception("Could not find EAPOL frames in capture")
num_eapol_key = 0
num_eap_req = 0
num_eap_resp = 0
for line in out.splitlines():
vals = line.split()
if vals[0] == '3':
num_eapol_key += 1
if vals[0] == '0' and len(vals) == 2:
if vals[1] == '1':
num_eap_req += 1
elif vals[1] == '2':
num_eap_resp += 1
logger.info("num_eapol_key: %d" % num_eapol_key)
logger.info("num_eap_req: %d" % num_eap_req)
logger.info("num_eap_resp: %d" % num_eap_resp)
if num_eapol_key < 4:
raise Exception("Did not see four unencrypted EAPOL-Key frames")
if num_eap_req < 6:
raise Exception("Did not see six unencrypted EAP-Request frames")
if num_eap_resp < 6:
raise Exception("Did not see six unencrypted EAP-Response frames")
def test_ieee8021x_set_conf(dev, apdev):
"""IEEE 802.1X and EAPOL_SET command"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
addr0 = dev[0].own_addr()
tests = ["EAPOL_SET 1",
"EAPOL_SET %sfoo bar" % addr0,
"EAPOL_SET %s foo" % addr0,
"EAPOL_SET %s foo bar" % addr0,
"EAPOL_SET %s AdminControlledDirections bar" % addr0,
"EAPOL_SET %s AdminControlledPortControl bar" % addr0,
"EAPOL_SET %s reAuthEnabled bar" % addr0,
"EAPOL_SET %s KeyTransmissionEnabled bar" % addr0,
"EAPOL_SET 11:22:33:44:55:66 AdminControlledDirections Both"]
for t in tests:
if "FAIL" not in hapd.request(t):
raise Exception("Invalid EAPOL_SET command accepted: " + t)
tests = [("AdminControlledDirections", "adminControlledDirections", "In"),
("AdminControlledDirections", "adminControlledDirections",
"Both"),
("quietPeriod", "quietPeriod", "13"),
("serverTimeout", "serverTimeout", "7"),
("reAuthPeriod", "reAuthPeriod", "1234"),
("reAuthEnabled", "reAuthEnabled", "FALSE"),
("reAuthEnabled", "reAuthEnabled", "TRUE"),
("KeyTransmissionEnabled", "keyTxEnabled", "TRUE"),
("KeyTransmissionEnabled", "keyTxEnabled", "FALSE"),
("AdminControlledPortControl", "portControl", "ForceAuthorized"),
("AdminControlledPortControl", "portControl",
"ForceUnauthorized"),
("AdminControlledPortControl", "portControl", "Auto")]
for param, mibparam, val in tests:
if "OK" not in hapd.request("EAPOL_SET %s %s %s" % (addr0, param, val)):
raise Exception("Failed to set %s %s" % (param, val))
mib = hapd.get_sta(addr0, info="eapol")
if mib[mibparam] != val:
raise Exception("Unexpected %s value: %s (expected %s)" % (param, mib[mibparam], val))
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
time.sleep(0.1)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_ieee8021x_auth_awhile(dev, apdev):
"""IEEE 802.1X and EAPOL Authenticator aWhile handling"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
params['auth_server_port'] = "18129"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
addr0 = dev[0].own_addr()
params = {}
params['ssid'] = 'as'
params['beacon_int'] = '2000'
params['radius_server_clients'] = 'auth_serv/radius_clients.conf'
params['radius_server_auth_port'] = '18129'
params['eap_server'] = '1'
params['eap_user_file'] = 'auth_serv/eap_user.conf'
params['ca_cert'] = 'auth_serv/ca.pem'
params['server_cert'] = 'auth_serv/server.pem'
params['private_key'] = 'auth_serv/server.key'
hapd1 = hostapd.add_ap(apdev[1], params)
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hapd1.disable()
if "OK" not in hapd.request("EAPOL_SET %s serverTimeout 1" % addr0):
raise Exception("Failed to set serverTimeout")
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
for i in range(40):
mib = hapd.get_sta(addr0, info="eapol")
val = int(mib['aWhile'])
if val > 0:
break
time.sleep(1)
if val == 0:
raise Exception("aWhile did not increase")
hapd.dump_monitor()
for i in range(40):
mib = hapd.get_sta(addr0, info="eapol")
val = int(mib['aWhile'])
if val < 5:
break
time.sleep(1)
ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED"], timeout=10)
if ev is None:
raise Exception("Authentication restart not seen")
def test_ieee8021x_open_leap(dev, apdev):
"""IEEE 802.1X connection with LEAP included in configuration"""
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-open"
params["ieee8021x"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[1].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="LEAP", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412", wait_connect=False)
dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
eap="PSK LEAP", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
ev = dev[1].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=5)
dev[1].request("DISCONNECT")
def test_ieee8021x_and_wpa_enabled(dev, apdev):
"""IEEE 802.1X connection using dynamic WEP104 when WPA enabled"""
check_wep_capa(dev[0])
skip_with_fips(dev[0])
params = hostapd.radius_params()
params["ssid"] = "ieee8021x-wep"
params["ieee8021x"] = "1"
params["wep_key_len_broadcast"] = "13"
params["wep_key_len_unicast"] = "13"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X WPA-EAP", eap="PSK",
identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
diff --git a/tests/hwsim/test_mbo.py b/tests/hwsim/test_mbo.py
index 65d446b7ee25..36efd6a0e0ce 100644
--- a/tests/hwsim/test_mbo.py
+++ b/tests/hwsim/test_mbo.py
@@ -1,596 +1,613 @@
# MBO tests
# Copyright (c) 2016, Intel Deutschland GmbH
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import logging
logger = logging.getLogger()
import hostapd
import os
import time
import hostapd
from tshark import run_tshark
from utils import *
def set_reg(country_code, apdev0=None, apdev1=None, dev0=None):
if apdev0:
hostapd.cmd_execute(apdev0, ['iw', 'reg', 'set', country_code])
if apdev1:
hostapd.cmd_execute(apdev1, ['iw', 'reg', 'set', country_code])
if dev0:
dev0.cmd_execute(['iw', 'reg', 'set', country_code])
def run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, country, freq_list=None,
disable_ht=False, disable_vht=False):
"""MBO and supported operating classes"""
addr = dev[0].own_addr()
res2 = None
res5 = None
dev[0].flush_scan_cache()
dev[0].dump_monitor()
logger.info("Country: " + country)
dev[0].note("Setting country code " + country)
set_reg(country, apdev[0], apdev[1], dev[0])
for j in range(5):
ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
if ev is None:
raise Exception("No regdom change event")
if "alpha2=" + country in ev:
break
dev[0].dump_monitor()
dev[1].dump_monitor()
dev[2].dump_monitor()
_disable_ht = "1" if disable_ht else "0"
_disable_vht = "1" if disable_vht else "0"
if hapd:
hapd.set("country_code", country)
hapd.enable()
dev[0].scan_for_bss(hapd.own_addr(), 5180, force_scan=True)
dev[0].connect("test-wnm-mbo", key_mgmt="NONE", scan_freq="5180",
freq_list=freq_list, disable_ht=_disable_ht,
disable_vht=_disable_vht)
sta = hapd.get_sta(addr)
res5 = sta['supp_op_classes'][2:]
dev[0].wait_regdom(country_ie=True)
time.sleep(0.1)
hapd.disable()
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].request("ABORT_SCAN")
dev[0].wait_disconnected()
dev[0].dump_monitor()
hapd2.set("country_code", country)
hapd2.enable()
dev[0].scan_for_bss(hapd2.own_addr(), 2412, force_scan=True)
dev[0].connect("test-wnm-mbo-2", key_mgmt="NONE", scan_freq="2412",
freq_list=freq_list, disable_ht=_disable_ht,
disable_vht=_disable_vht)
sta = hapd2.get_sta(addr)
res2 = sta['supp_op_classes'][2:]
dev[0].wait_regdom(country_ie=True)
time.sleep(0.1)
hapd2.disable()
time.sleep(0.1)
dev[0].request("REMOVE_NETWORK all")
dev[0].request("ABORT_SCAN")
dev[0].wait_disconnected()
dev[0].dump_monitor()
return res2, res5
def run_mbo_supp_oper_class(dev, apdev, country, expected, inc5,
freq_list=None, disable_ht=False,
disable_vht=False):
if inc5:
params = {'ssid': "test-wnm-mbo",
'mbo': '1',
"country_code": "US",
'ieee80211d': '1',
"ieee80211n": "1",
"hw_mode": "a",
"channel": "36"}
hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
else:
hapd = None
params = {'ssid': "test-wnm-mbo-2",
'mbo': '1',
"country_code": "US",
'ieee80211d': '1',
"ieee80211n": "1",
"hw_mode": "g",
"channel": "1"}
hapd2 = hostapd.add_ap(apdev[1], params, no_enable=True)
try:
dev[0].request("STA_AUTOCONNECT 0")
res2, res5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, country,
freq_list=freq_list,
disable_ht=disable_ht,
disable_vht=disable_vht)
finally:
dev[0].dump_monitor()
dev[0].request("STA_AUTOCONNECT 1")
wait_regdom_changes(dev[0])
country1 = dev[0].get_driver_status_field("country")
logger.info("Country code at the end (1): " + country1)
set_reg("00", apdev[0], apdev[1], dev[0])
country2 = dev[0].get_driver_status_field("country")
logger.info("Country code at the end (2): " + country2)
for i in range(5):
ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
if ev is None or "init=USER type=WORLD" in ev:
break
wait_regdom_changes(dev[0])
country3 = dev[0].get_driver_status_field("country")
logger.info("Country code at the end (3): " + country3)
if country3 != "00":
clear_country(dev)
# For now, allow operating class 129 to be missing since not all
# installed regdb files include the 160 MHz channels.
expected2 = expected.replace('808182', '8082')
# For now, allow operating classes 121-123 to be missing since not all
# installed regdb files include the related US DFS channels.
expected2 = expected2.replace('78797a7b7c', '787c')
expected3 = expected
# For now, allow operating classes 124-127 to be missing for Finland
# since they were added only recently in regdb.
if country == "FI":
expected3 = expected3.replace("7b7c7d7e7f80", "7b80")
if res2 != expected and res2 != expected2 and res2 != expected3:
raise Exception("Unexpected supp_op_class string (country=%s, 2.4 GHz): %s (expected: %s)" % (country, res2, expected))
if inc5 and res5 != expected and res5 != expected2 and res5 != expected3:
raise Exception("Unexpected supp_op_class string (country=%s, 5 GHz): %s (expected: %s)" % (country, res5, expected))
def test_mbo_supp_oper_classes_za(dev, apdev):
"""MBO and supported operating classes (ZA)"""
run_mbo_supp_oper_class(dev, apdev, "ZA",
"515354737475767778797a7b808182", True)
def test_mbo_supp_oper_classes_fi(dev, apdev):
"""MBO and supported operating classes (FI)"""
run_mbo_supp_oper_class(dev, apdev, "FI",
"515354737475767778797a7b7c7d7e7f808182", True)
def test_mbo_supp_oper_classes_us(dev, apdev):
"""MBO and supported operating classes (US)"""
run_mbo_supp_oper_class(dev, apdev, "US",
"515354737475767778797a7b7c7d7e7f808182", True)
def test_mbo_supp_oper_classes_jp(dev, apdev):
"""MBO and supported operating classes (JP)"""
run_mbo_supp_oper_class(dev, apdev, "JP",
"51525354737475767778797a7b808182", True)
def test_mbo_supp_oper_classes_bd(dev, apdev):
"""MBO and supported operating classes (BD)"""
run_mbo_supp_oper_class(dev, apdev, "BD",
"5153547c7d7e7f80", False)
def test_mbo_supp_oper_classes_sy(dev, apdev):
"""MBO and supported operating classes (SY)"""
run_mbo_supp_oper_class(dev, apdev, "SY",
"515354", False)
def test_mbo_supp_oper_classes_us_freq_list(dev, apdev):
"""MBO and supported operating classes (US) - freq_list"""
run_mbo_supp_oper_class(dev, apdev, "US", "515354", False,
freq_list="2412 2437 2462")
def test_mbo_supp_oper_classes_us_disable_ht(dev, apdev):
"""MBO and supported operating classes (US) - disable_ht"""
run_mbo_supp_oper_class(dev, apdev, "US", "517376797c7d", False,
disable_ht=True)
def test_mbo_supp_oper_classes_us_disable_vht(dev, apdev):
"""MBO and supported operating classes (US) - disable_vht"""
run_mbo_supp_oper_class(dev, apdev, "US",
"515354737475767778797a7b7c7d7e7f", False,
disable_vht=True)
def test_mbo_assoc_disallow(dev, apdev, params):
"""MBO and association disallowed"""
hapd1 = hostapd.add_ap(apdev[0], {"ssid": "MBO", "mbo": "1"})
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "MBO", "mbo": "1"})
logger.debug("Set mbo_assoc_disallow with invalid value")
if "FAIL" not in hapd1.request("SET mbo_assoc_disallow 2"):
raise Exception("Set mbo_assoc_disallow for AP1 succeeded unexpectedly with value 2")
logger.debug("Disallow associations to AP1 and allow association to AP2")
if "OK" not in hapd1.request("SET mbo_assoc_disallow 1"):
raise Exception("Failed to set mbo_assoc_disallow for AP1")
if "OK" not in hapd2.request("SET mbo_assoc_disallow 0"):
raise Exception("Failed to set mbo_assoc_disallow for AP2")
dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
"wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00",
wait=False)
if "Destination address: " + hapd1.own_addr() in out:
raise Exception("Association request sent to disallowed AP")
timestamp = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
"wlan.fc.type_subtype == 0x00",
display=['frame.time'], wait=False)
logger.debug("Allow associations to AP1 and disallow associations to AP2")
if "OK" not in hapd1.request("SET mbo_assoc_disallow 0"):
raise Exception("Failed to set mbo_assoc_disallow for AP1")
if "OK" not in hapd2.request("SET mbo_assoc_disallow 1"):
raise Exception("Failed to set mbo_assoc_disallow for AP2")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
# Force new scan, so the assoc_disallowed indication is updated */
dev[0].request("FLUSH")
dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
filter = 'wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00 && frame.time > "' + timestamp.rstrip() + '"'
out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
filter, wait=False)
if "Destination address: " + hapd2.own_addr() in out:
raise Exception("Association request sent to disallowed AP 2")
def test_mbo_assoc_disallow_ignore(dev, apdev):
"""MBO and ignoring disallowed association"""
try:
_test_mbo_assoc_disallow_ignore(dev, apdev)
finally:
dev[0].request("SCAN_INTERVAL 5")
def _test_mbo_assoc_disallow_ignore(dev, apdev):
hapd1 = hostapd.add_ap(apdev[0], {"ssid": "MBO", "mbo": "1"})
if "OK" not in hapd1.request("SET mbo_assoc_disallow 1"):
raise Exception("Failed to set mbo_assoc_disallow for AP1")
if "OK" not in dev[0].request("SCAN_INTERVAL 1"):
raise Exception("Failed to set scan interval")
dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
if ev is None:
raise Exception("CTRL-EVENT-NETWORK-NOT-FOUND not seen")
if "OK" not in dev[0].request("SET ignore_assoc_disallow 1"):
raise Exception("Failed to set ignore_assoc_disallow")
ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
if ev is None:
raise Exception("CTRL-EVENT-ASSOC-REJECT not seen")
if "status_code=17" not in ev:
raise Exception("Unexpected association reject reason: " + ev)
if "OK" not in hapd1.request("SET mbo_assoc_disallow 0"):
raise Exception("Failed to set mbo_assoc_disallow for AP1")
dev[0].wait_connected()
@remote_compatible
def test_mbo_cell_capa_update(dev, apdev):
"""MBO cellular data capability update"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1'}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
raise Exception("Failed to set STA as cellular data capable")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
sta = hapd.get_sta(addr)
if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
raise Exception("mbo_cell_capa missing after association")
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
# Duplicate update for additional code coverage
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
time.sleep(0.2)
sta = hapd.get_sta(addr)
if 'mbo_cell_capa' not in sta:
raise Exception("mbo_cell_capa missing after update")
if sta['mbo_cell_capa'] != '3':
raise Exception("mbo_cell_capa not updated properly")
@remote_compatible
def test_mbo_cell_capa_update_pmf(dev, apdev):
"""MBO cellular data capability update with PMF required"""
ssid = "test-wnm-mbo"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
params["ieee80211w"] = "2"
params['mbo'] = '1'
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
raise Exception("Failed to set STA as cellular data capable")
dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
proto="WPA2", ieee80211w="2", scan_freq="2412")
hapd.wait_sta()
addr = dev[0].own_addr()
sta = hapd.get_sta(addr)
if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
raise Exception("mbo_cell_capa missing after association")
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
time.sleep(0.2)
sta = hapd.get_sta(addr)
if 'mbo_cell_capa' not in sta:
raise Exception("mbo_cell_capa missing after update")
if sta['mbo_cell_capa'] != '3':
raise Exception("mbo_cell_capa not updated properly")
def test_mbo_wnm_token_wrap(dev, apdev):
"""MBO WNM token wrap around"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1'}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
# Trigger transmission of 256 WNM-Notification frames to wrap around the
# 8-bit mbo_wnm_token counter.
for i in range(128):
if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
raise Exception("Failed to set STA as cellular data capable")
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
@remote_compatible
def test_mbo_non_pref_chan(dev, apdev):
"""MBO non-preferred channel list"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1'}
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:99"):
raise Exception("Invalid non_pref_chan value accepted")
if "FAIL" not in dev[0].request("SET non_pref_chan 81:15:200:3"):
raise Exception("Invalid non_pref_chan value accepted")
if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3 81:7:201:3"):
raise Exception("Invalid non_pref_chan value accepted")
if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
raise Exception("Failed to set non-preferred channel list")
if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:1 81:9:100:2"):
raise Exception("Failed to set non-preferred channel list")
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'non_pref_chan[0]' not in sta:
raise Exception("Missing non_pref_chan[0] value (assoc)")
if sta['non_pref_chan[0]'] != '81:200:1:7':
raise Exception("Unexpected non_pref_chan[0] value (assoc)")
if 'non_pref_chan[1]' not in sta:
raise Exception("Missing non_pref_chan[1] value (assoc)")
if sta['non_pref_chan[1]'] != '81:100:2:9':
raise Exception("Unexpected non_pref_chan[1] value (assoc)")
if 'non_pref_chan[2]' in sta:
raise Exception("Unexpected non_pref_chan[2] value (assoc)")
if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2"):
raise Exception("Failed to update non-preferred channel list")
time.sleep(0.1)
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'non_pref_chan[0]' not in sta:
raise Exception("Missing non_pref_chan[0] value (update 1)")
if sta['non_pref_chan[0]'] != '81:100:2:9':
raise Exception("Unexpected non_pref_chan[0] value (update 1)")
if 'non_pref_chan[1]' in sta:
raise Exception("Unexpected non_pref_chan[1] value (update 1)")
if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2 81:10:100:2 81:8:100:2 81:7:100:1 81:5:100:1"):
raise Exception("Failed to update non-preferred channel list")
time.sleep(0.1)
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'non_pref_chan[0]' not in sta:
raise Exception("Missing non_pref_chan[0] value (update 2)")
if sta['non_pref_chan[0]'] != '81:100:1:7,5':
raise Exception("Unexpected non_pref_chan[0] value (update 2)")
if 'non_pref_chan[1]' not in sta:
raise Exception("Missing non_pref_chan[1] value (update 2)")
if sta['non_pref_chan[1]'] != '81:100:2:9,10,8':
raise Exception("Unexpected non_pref_chan[1] value (update 2)")
if 'non_pref_chan[2]' in sta:
raise Exception("Unexpected non_pref_chan[2] value (update 2)")
if "OK" not in dev[0].request("SET non_pref_chan 81:5:90:2 82:14:91:2"):
raise Exception("Failed to update non-preferred channel list")
time.sleep(0.1)
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'non_pref_chan[0]' not in sta:
raise Exception("Missing non_pref_chan[0] value (update 3)")
if sta['non_pref_chan[0]'] != '81:90:2:5':
raise Exception("Unexpected non_pref_chan[0] value (update 3)")
if 'non_pref_chan[1]' not in sta:
raise Exception("Missing non_pref_chan[1] value (update 3)")
if sta['non_pref_chan[1]'] != '82:91:2:14':
raise Exception("Unexpected non_pref_chan[1] value (update 3)")
if 'non_pref_chan[2]' in sta:
raise Exception("Unexpected non_pref_chan[2] value (update 3)")
if "OK" not in dev[0].request("SET non_pref_chan "):
raise Exception("Failed to update non-preferred channel list")
time.sleep(0.1)
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'non_pref_chan[0]' in sta:
raise Exception("Unexpected non_pref_chan[0] value (update 4)")
@remote_compatible
def test_mbo_sta_supp_op_classes(dev, apdev):
"""MBO STA supported operating classes"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1'}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
sta = hapd.get_sta(addr)
logger.debug("STA: " + str(sta))
if 'supp_op_classes' not in sta:
raise Exception("No supp_op_classes")
supp = bytearray(binascii.unhexlify(sta['supp_op_classes']))
if supp[0] != 81:
raise Exception("Unexpected current operating class %d" % supp[0])
if 115 not in supp:
raise Exception("Operating class 115 missing")
def test_mbo_failures(dev, apdev):
"""MBO failure cases"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1'}
hapd = hostapd.add_ap(apdev[0], params)
with alloc_fail(dev[0], 1, "wpas_mbo_ie"):
dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
with alloc_fail(dev[0], 1, "wpas_mbo_send_wnm_notification"):
if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
raise Exception("Failed to set STA as cellular data capable")
with fail_test(dev[0], 1, "wpas_mbo_send_wnm_notification"):
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
with alloc_fail(dev[0], 1, "wpas_mbo_update_non_pref_chan"):
if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
raise Exception("non_pref_chan value accepted during OOM")
with alloc_fail(dev[0], 2, "wpas_mbo_update_non_pref_chan"):
if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
raise Exception("non_pref_chan value accepted during OOM")
def test_mbo_wnm_bss_tm_ie_parsing(dev, apdev):
"""MBO BSS transition request MBO IE parsing"""
ssid = "test-wnm-mbo"
params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
addr = dev[0].own_addr()
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
proto="WPA2", ieee80211w="0", scan_freq="2412")
dev[0].request("SET ext_mgmt_frame_handling 1")
hdr = "d0003a01" + addr.replace(':', '') + bssid.replace(':', '') + bssid.replace(':', '') + "3000"
btm_hdr = "0a070100030001"
tests = [("Truncated attribute in MBO IE", "dd06506f9a160101"),
("Unexpected cell data capa attribute length in MBO IE",
"dd09506f9a160501030500"),
("Unexpected transition reason attribute length in MBO IE",
"dd06506f9a160600"),
("Unexpected assoc retry delay attribute length in MBO IE",
"dd0c506f9a160100080200000800"),
("Unknown attribute id 255 in MBO IE",
"dd06506f9a16ff00")]
for test, mbo_ie in tests:
logger.info(test)
dev[0].request("NOTE " + test)
frame = hdr + btm_hdr + mbo_ie
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
raise Exception("MGMT_RX_PROCESS failed")
logger.info("Unexpected association retry delay")
dev[0].request("NOTE Unexpected association retry delay")
btm_hdr = "0a070108030001112233445566778899aabbcc"
mbo_ie = "dd08506f9a1608020000"
frame = hdr + btm_hdr + mbo_ie
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
raise Exception("MGMT_RX_PROCESS failed")
dev[0].request("SET ext_mgmt_frame_handling 0")
def test_mbo_without_pmf(dev, apdev):
"""MBO and WPA2 without PMF"""
ssid = "test-wnm-mbo"
params = {'ssid': ssid, 'mbo': '1', "wpa": '2',
"wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
"wpa_passphrase": "12345678"}
try:
# "MBO: PMF needs to be enabled whenever using WPA2 with MBO"
hostapd.add_ap(apdev[0], params)
raise Exception("AP setup succeeded unexpectedly")
except Exception as e:
if "Failed to enable hostapd" in str(e):
pass
else:
raise
def test_mbo_without_pmf_workaround(dev, apdev):
"""MBO and WPA2 without PMF on misbehaving AP"""
ssid = "test-wnm-mbo"
- params = {'ssid': ssid, "wpa": '2',
- "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
- "wpa_passphrase": "12345678",
- "vendor_elements": "dd07506f9a16010100"}
- hapd = hostapd.add_ap(apdev[0], params)
+ params0 = {'ssid': ssid, "wpa": '2',
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678",
+ "vendor_elements": "dd07506f9a16010100"}
+ params1 = {'ssid': ssid, "mbo": '1', "wpa": '2',
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678", "ieee80211w": "1"}
+ hapd0 = hostapd.add_ap(apdev[0], params0)
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
proto="WPA2", ieee80211w="1", scan_freq="2412")
- hapd.wait_sta()
- sta = hapd.get_sta(dev[0].own_addr())
+ hapd0.wait_sta()
+ sta = hapd0.get_sta(dev[0].own_addr())
+ ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
+ if ext_capab[2] & 0x08:
+ raise Exception("STA did not disable BSS Transition capability")
+ hapd1 = hostapd.add_ap(apdev[1], params1)
+ dev[0].scan_for_bss(hapd1.own_addr(), 2412, force_scan=True)
+ dev[0].roam(hapd1.own_addr())
+ hapd1.wait_sta()
+ sta = hapd1.get_sta(dev[0].own_addr())
+ ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
+ if not ext_capab[2] & 0x08:
+ raise Exception("STA disabled BSS Transition capability")
+ dev[0].roam(hapd0.own_addr())
+ hapd0.wait_sta()
+ sta = hapd0.get_sta(dev[0].own_addr())
ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
if ext_capab[2] & 0x08:
raise Exception("STA did not disable BSS Transition capability")
def check_mbo_anqp(dev, bssid, cell_data_conn_pref):
if "OK" not in dev.request("ANQP_GET " + bssid + " 272,mbo:2"):
raise Exception("ANQP_GET command failed")
ev = dev.wait_event(["GAS-QUERY-START"], timeout=5)
if ev is None:
raise Exception("GAS query start timed out")
ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("GAS query timed out")
if cell_data_conn_pref is not None:
ev = dev.wait_event(["RX-MBO-ANQP"], timeout=1)
if ev is None or "cell_conn_pref" not in ev:
raise Exception("Did not receive MBO Cellular Data Connection Preference")
if cell_data_conn_pref != int(ev.split('=')[1]):
raise Exception("Unexpected cell_conn_pref value: " + ev)
dev.dump_monitor()
def test_mbo_anqp(dev, apdev):
"""MBO ANQP"""
params = {'ssid': "test-wnm-mbo",
'mbo': '1',
'interworking': '1',
'mbo_cell_data_conn_pref': '1'}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
check_mbo_anqp(dev[0], bssid, 1)
hapd.set('mbo_cell_data_conn_pref', '255')
check_mbo_anqp(dev[0], bssid, 255)
hapd.set('mbo_cell_data_conn_pref', '-1')
check_mbo_anqp(dev[0], bssid, None)
diff --git a/tests/hwsim/test_mscs.py b/tests/hwsim/test_mscs.py
new file mode 100644
index 000000000000..b200550b3ac3
--- /dev/null
+++ b/tests/hwsim/test_mscs.py
@@ -0,0 +1,231 @@
+# Test cases for MSCS
+# Copyright (c) 2021, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct
+import time
+
+import hostapd
+from utils import *
+
+def register_mcsc_req(hapd):
+ type = 0x00d0
+ match = "1304"
+ if "OK" not in hapd.request("REGISTER_FRAME %04x %s" % (type, match)):
+ raise Exception("Could not register frame reception for Robust AV Streaming")
+
+def handle_mscs_req(hapd, wrong_dialog=False, status_code=0):
+ msg = hapd.mgmt_rx()
+ if msg['subtype'] != 13:
+ logger.info("RX:" + str(msg))
+ raise Exception("Received unexpected Management frame")
+ categ, act, dialog_token = struct.unpack('BBB', msg['payload'][0:3])
+ if categ != 19 or act != 4:
+ logger.info("RX:" + str(msg))
+ raise Exception("Received unexpected Action frame")
+
+ if wrong_dialog:
+ dialog_token = (dialog_token + 1) % 256
+ msg['da'] = msg['sa']
+ msg['sa'] = hapd.own_addr()
+ msg['payload'] = struct.pack('<BBBH', 19, 5, dialog_token, status_code)
+ hapd.mgmt_tx(msg)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None or "stype=13 ok=1" not in ev:
+ raise Exception("No TX status reported")
+
+def wait_mscs_result(dev, expect_status=0):
+ ev = dev.wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1)
+ if ev is None:
+ raise Exception("No MSCS result reported")
+ if "status_code=%d" % expect_status not in ev:
+ raise Exception("Unexpected MSCS result: " + ev)
+
+def test_mscs_invalid_params(dev, apdev):
+ """MSCS command invalid parameters"""
+ tests = ["",
+ "add Xp_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 Xp_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 Xtream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=12345 Xrame_classifier=045F",
+ "add up_bitmap=X0 up_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=0 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=X45F",
+ "change "]
+ for t in tests:
+ if "FAIL" not in dev[0].request("MSCS " + t):
+ raise Exception("Invalid MSCS parameters accepted: " + t)
+
+def test_mscs_without_ap_support(dev, apdev):
+ """MSCS without AP support"""
+ try:
+ run_mscs_without_ap_support(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_without_ap_support(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa_mask": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("Failed to configure MSCS")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS change accepted unexpectedly")
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS add accepted unexpectedly")
+
+def test_mscs_post_assoc(dev, apdev):
+ """MSCS configuration post-association"""
+ try:
+ run_mscs_post_assoc(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_post_assoc(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS change accepted unexpectedly")
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd, status_code=23456)
+ wait_mscs_result(dev[0], expect_status=23456)
+
+def test_mscs_pre_assoc(dev, apdev):
+ """MSCS configuration pre-association"""
+ try:
+ run_mscs_pre_assoc(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_pre_assoc(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20",
+ "assocresp_elements": "ff0c5800000000000000" + "01020000"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_mscs_result(dev[0])
+ dev[0].wait_connected()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd, wrong_dialog=True)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected MSCS result reported")
+
+def test_mscs_assoc_failure(dev, apdev):
+ """MSCS configuration failure during association exchange"""
+ try:
+ run_mscs_assoc_failure(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_assoc_failure(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20",
+ "assocresp_elements": "ff0c5800000000000000" + "01020001"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_mscs_result(dev[0], expect_status=256)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.dump_monitor()
+ # No MSCS Status subelement
+ hapd.set("assocresp_elements", "ff085800000000000000")
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-MSCS-RESULT"],
+ timeout=10)
+ if ev is None:
+ raise Exception("No connection event")
+ if "CTRL-EVENT-MSCS-RESULT" in ev:
+ raise Exception("Unexpected MSCS result")
+
+def test_mscs_local_errors(dev, apdev):
+ """MSCS configuration local errors"""
+ try:
+ run_mscs_local_errors(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_local_errors(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ for count in range(1, 3):
+ with alloc_fail(dev[0], count, "wpas_send_mscs_req"):
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS add succeeded in error case")
diff --git a/tests/hwsim/test_multi_ap.py b/tests/hwsim/test_multi_ap.py
index 183fb660d200..ca8ea3a31f90 100644
--- a/tests/hwsim/test_multi_ap.py
+++ b/tests/hwsim/test_multi_ap.py
@@ -1,353 +1,363 @@
# Test cases for Multi-AP
# Copyright (c) 2018, The Linux Foundation
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
def test_multi_ap_association(dev, apdev):
"""Multi-AP association in backhaul BSS"""
run_multi_ap_association(dev, apdev, 1)
dev[1].connect("multi-ap", psk="12345678", scan_freq="2412",
wait_connect=False)
ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-ASSOC-REJECT"],
timeout=5)
dev[1].request("DISCONNECT")
if ev is None:
raise Exception("Connection result not reported")
if "CTRL-EVENT-ASSOC-REJECT" not in ev:
raise Exception("Association rejection not reported")
if "status_code=12" not in ev:
raise Exception("Unexpected association status code: " + ev)
def test_multi_ap_association_shared_bss(dev, apdev):
"""Multi-AP association in backhaul BSS (with fronthaul BSS enabled)"""
run_multi_ap_association(dev, apdev, 3)
dev[1].connect("multi-ap", psk="12345678", scan_freq="2412")
def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True):
params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
if multi_ap:
params["multi_ap"] = str(multi_ap)
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("multi-ap", psk="12345678", scan_freq="2412",
multi_ap_backhaul_sta="1", wait_connect=wait_connect)
def test_multi_ap_backhaul_roam_with_bridge(dev, apdev):
"""Multi-AP backhaul BSS reassociation to another BSS with bridge"""
br_ifname = 'sta-br0'
ifname = 'wlan5'
try:
run_multi_ap_backhaul_roam_with_bridge(dev, apdev)
finally:
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
subprocess.call(['brctl', 'delif', br_ifname, ifname])
subprocess.call(['brctl', 'delbr', br_ifname])
subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
def run_multi_ap_backhaul_roam_with_bridge(dev, apdev):
br_ifname = 'sta-br0'
ifname = 'wlan5'
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
subprocess.call(['brctl', 'addbr', br_ifname])
subprocess.call(['brctl', 'setfd', br_ifname, '0'])
subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
wpas.interface_add(ifname, br_ifname=br_ifname)
wpas.flush_scan_cache()
params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
params["multi_ap"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
wpas.connect("multi-ap", psk="12345678", scan_freq="2412",
multi_ap_backhaul_sta="1")
hapd2 = hostapd.add_ap(apdev[1], params)
bssid2 = hapd2.own_addr()
wpas.scan_for_bss(bssid2, freq="2412", force_scan=True)
wpas.roam(bssid2)
def test_multi_ap_disabled_on_ap(dev, apdev):
"""Multi-AP association attempt when disabled on AP"""
run_multi_ap_association(dev, apdev, 0, wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-CONNECTED"],
timeout=5)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("Connection result not reported")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Unexpected connection result")
def test_multi_ap_fronthaul_on_ap(dev, apdev):
"""Multi-AP association attempt when only fronthaul BSS on AP"""
run_multi_ap_association(dev, apdev, 2, wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED",
"CTRL-EVENT-CONNECTED",
"CTRL-EVENT-ASSOC-REJECT"],
timeout=5)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("Connection result not reported")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Unexpected connection result")
def run_multi_ap_wps(dev, apdev, params, params_backhaul=None, add_apdev=False,
run_csa=False, allow_csa_fail=False):
"""Helper for running Multi-AP WPS tests
dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul
BSS. If there is a separate backhaul BSS, it must have been set up by the
caller. params are the normal SSID parameters, they will be extended with
the WPS parameters. multi_ap_bssid must be given if it is not equal to the
fronthaul BSSID."""
wpas_apdev = None
if params_backhaul:
hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
multi_ap_bssid = hapd_backhaul.own_addr()
else:
multi_ap_bssid = apdev[0]['bssid']
params.update({"wps_state": "2", "eap_server": "1"})
# WPS with multi-ap station dev[0]
hapd = hostapd.add_ap(apdev[0], params)
conf = hapd.request("GET_CONFIG").splitlines()
if "ssid=" + params['ssid'] not in conf:
raise Exception("GET_CONFIG did not show correct ssid entry")
if "multi_ap" in params and \
"multi_ap=" + params["multi_ap"] not in conf:
raise Exception("GET_CONFIG did not show correct multi_ap entry")
if "multi_ap_backhaul_ssid" in params and \
"multi_ap_backhaul_ssid=" + params["multi_ap_backhaul_ssid"].strip('"') not in conf:
raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_ssid entry")
if "wpa" in params and "multi_ap_backhaul_wpa_passphrase" in params and \
"multi_ap_backhaul_wpa_passphrase=" + params["multi_ap_backhaul_wpa_passphrase"] not in conf:
raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_passphrase entry")
if "multi_ap_backhaul_wpa_psk" in params and \
"multi_ap_backhaul_wpa_psk=" + params["multi_ap_backhaul_wpa_psk"] not in conf:
raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_psk entry")
hapd.request("WPS_PBC")
if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
dev[0].request("WPS_PBC multi_ap=1")
dev[0].wait_connected(timeout=20)
status = dev[0].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != multi_ap_bssid:
raise Exception("Not fully connected")
if status['ssid'] != params['multi_ap_backhaul_ssid'].strip('"'):
raise Exception("Unexpected SSID %s != %s" % (status['ssid'], params["multi_ap_backhaul_ssid"]))
if status['pairwise_cipher'] != 'CCMP':
raise Exception("Unexpected encryption configuration %s" % status['pairwise_cipher'])
if status['key_mgmt'] != 'WPA2-PSK':
raise Exception("Unexpected key_mgmt")
status = hapd.request("WPS_GET_STATUS")
if "PBC Status: Disabled" not in status:
raise Exception("PBC status not shown correctly")
if "Last WPS result: Success" not in status:
raise Exception("Last WPS result not shown correctly")
if "Peer Address: " + dev[0].own_addr() not in status:
raise Exception("Peer address not shown correctly")
if len(dev[0].list_networks()) != 1:
raise Exception("Unexpected number of network blocks")
# WPS with non-Multi-AP station dev[1]
hapd.request("WPS_PBC")
if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
dev[1].request("WPS_PBC")
dev[1].wait_connected(timeout=20)
status = dev[1].get_status()
if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
raise Exception("Not fully connected")
if status['ssid'] != params["ssid"]:
raise Exception("Unexpected SSID")
# Fronthaul may be something else than WPA2-PSK so don't test it.
status = hapd.request("WPS_GET_STATUS")
if "PBC Status: Disabled" not in status:
raise Exception("PBC status not shown correctly")
if "Last WPS result: Success" not in status:
raise Exception("Last WPS result not shown correctly")
if "Peer Address: " + dev[1].own_addr() not in status:
raise Exception("Peer address not shown correctly")
if len(dev[1].list_networks()) != 1:
raise Exception("Unexpected number of network blocks")
try:
# Add apdev to the same phy that dev[0]
if add_apdev:
wpas_apdev = {}
wpas_apdev['ifname'] = dev[0].ifname + "_ap"
status, buf = dev[0].cmd_execute(['iw', dev[0].ifname,
'interface', 'add',
wpas_apdev['ifname'],
'type', 'managed'])
if status != 0:
raise Exception("iw interface add failed")
wpas_hapd = hostapd.add_ap(wpas_apdev, params)
if run_csa:
if 'OK' not in hapd.request("CHAN_SWITCH 5 2462 ht"):
raise Exception("chan switch request failed")
ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5)
if not ev:
raise Exception("chan switch failed")
# now check station
ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH",
"CTRL-EVENT-DISCONNECTED"], timeout=5)
if not ev:
raise Exception("sta - no chanswitch event")
if "CTRL-EVENT-CHANNEL-SWITCH" not in ev and not allow_csa_fail:
raise Exception("Received disconnection event instead of channel switch event")
if add_apdev:
dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del'])
except:
if wpas_apdev:
dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del'])
raise
+ return hapd
+
def test_multi_ap_wps_shared(dev, apdev):
"""WPS on shared fronthaul/backhaul AP"""
ssid = "multi-ap-wps"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params.update({"multi_ap": "3",
"multi_ap_backhaul_ssid": '"%s"' % ssid,
"multi_ap_backhaul_wpa_passphrase": passphrase})
- run_multi_ap_wps(dev, apdev, params)
+ hapd = run_multi_ap_wps(dev, apdev, params)
+ # Verify WPS parameter update with Multi-AP
+ if "OK" not in hapd.request("RELOAD"):
+ raise Exception("hostapd RELOAD failed")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.request("WPS_PBC")
+ dev[0].request("WPS_PBC multi_ap=1")
+ dev[0].wait_connected(timeout=20)
def test_multi_ap_wps_shared_csa(dev, apdev):
"""WPS on shared fronthaul/backhaul AP, run CSA"""
ssid = "multi-ap-wps-csa"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params.update({"multi_ap": "3",
"multi_ap_backhaul_ssid": '"%s"' % ssid,
"multi_ap_backhaul_wpa_passphrase": passphrase})
run_multi_ap_wps(dev, apdev, params, run_csa=True)
def test_multi_ap_wps_shared_apdev_csa(dev, apdev):
"""WPS on shared fronthaul/backhaul AP add apdev on same phy and run CSA"""
ssid = "multi-ap-wps-apdev-csa"
passphrase = "12345678"
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
params.update({"multi_ap": "3",
"multi_ap_backhaul_ssid": '"%s"' % ssid,
"multi_ap_backhaul_wpa_passphrase": passphrase})
# This case is currently failing toc omplete CSA on the station interface.
# For the time being, ignore that to avoid always failing tests. Full
# validation can be enabled once the issue behind this is fixed.
run_multi_ap_wps(dev, apdev, params, add_apdev=True, run_csa=True,
allow_csa_fail=True)
def test_multi_ap_wps_shared_psk(dev, apdev):
"""WPS on shared fronthaul/backhaul AP using PSK"""
ssid = "multi-ap-wps"
psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
params = hostapd.wpa2_params(ssid=ssid)
params.update({"wpa_psk": psk,
"multi_ap": "3",
"multi_ap_backhaul_ssid": '"%s"' % ssid,
"multi_ap_backhaul_wpa_psk": psk})
run_multi_ap_wps(dev, apdev, params)
def test_multi_ap_wps_split(dev, apdev):
"""WPS on split fronthaul and backhaul AP"""
backhaul_ssid = "multi-ap-backhaul-wps"
backhaul_passphrase = "87654321"
params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps",
passphrase="12345678")
params.update({"multi_ap": "2",
"multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
"multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
passphrase=backhaul_passphrase)
params_backhaul.update({"multi_ap": "1"})
run_multi_ap_wps(dev, apdev, params, params_backhaul)
def test_multi_ap_wps_split_psk(dev, apdev):
"""WPS on split fronthaul and backhaul AP"""
backhaul_ssid = "multi-ap-backhaul-wps"
backhaul_psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps",
passphrase="12345678")
params.update({"multi_ap": "2",
"multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
"multi_ap_backhaul_wpa_psk": backhaul_psk})
params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid)
params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk})
run_multi_ap_wps(dev, apdev, params, params_backhaul)
def test_multi_ap_wps_split_mixed(dev, apdev):
"""WPS on split fronthaul and backhaul AP with mixed-mode fronthaul"""
skip_without_tkip(dev[0])
backhaul_ssid = "multi-ap-backhaul-wps"
backhaul_passphrase = "87654321"
params = hostapd.wpa_mixed_params(ssid="multi-ap-fronthaul-wps",
passphrase="12345678")
params.update({"multi_ap": "2",
"multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
"multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
passphrase=backhaul_passphrase)
params_backhaul.update({"multi_ap": "1"})
run_multi_ap_wps(dev, apdev, params, params_backhaul)
def test_multi_ap_wps_split_open(dev, apdev):
"""WPS on split fronthaul and backhaul AP with open fronthaul"""
backhaul_ssid = "multi-ap-backhaul-wps"
backhaul_passphrase = "87654321"
params = {"ssid": "multi-ap-wps-fronthaul", "multi_ap": "2",
"multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
"multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}
params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
passphrase=backhaul_passphrase)
params_backhaul.update({"multi_ap": "1"})
run_multi_ap_wps(dev, apdev, params, params_backhaul)
def test_multi_ap_wps_fail_non_multi_ap(dev, apdev):
"""Multi-AP WPS on non-WPS AP fails"""
params = hostapd.wpa2_params(ssid="non-multi-ap-wps", passphrase="12345678")
params.update({"wps_state": "2", "eap_server": "1"})
hapd = hostapd.add_ap(apdev[0], params)
hapd.request("WPS_PBC")
if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
raise Exception("PBC status not shown correctly")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[0].request("WPS_PBC %s multi_ap=1" % apdev[0]['bssid'])
# Since we will fail to associate and WPS doesn't even get started, there
# isn't much we can do except wait for timeout. For PBC, it is not possible
# to change the timeout from 2 minutes. Instead of waiting for the timeout,
# just check that WPS doesn't finish within reasonable time.
for i in range(2):
ev = dev[0].wait_event(["WPS-SUCCESS", "WPS-FAIL",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev and "WPS-" in ev:
raise Exception("WPS operation completed: " + ev)
dev[0].request("WPS_CANCEL")
diff --git a/tests/hwsim/test_ocv.py b/tests/hwsim/test_ocv.py
index 08e8ba20f47b..e93cea6ffa18 100644
--- a/tests/hwsim/test_ocv.py
+++ b/tests/hwsim/test_ocv.py
@@ -1,1204 +1,1247 @@
# WPA2-Personal OCV tests
# Copyright (c) 2018, Mathy Vanhoef
#
# This software may be distributed under the terms of the BSD license.
# See README for more details
from remotehost import remote_compatible
import binascii, struct
import logging, time
logger = logging.getLogger()
import hostapd
from wpasupplicant import WpaSupplicant
import hwsim_utils
from utils import *
from test_erp import start_erp_as
from test_ap_ft import ft_params1, ft_params2
from test_ap_psk import parse_eapol, build_eapol, pmk_to_ptk, eapol_key_mic, recv_eapol, send_eapol, reply_eapol, build_eapol_key_3_4, aes_wrap, pad_key_data
#TODO: Refuse setting up AP with OCV but without MFP support
#TODO: Refuse to connect to AP that advertises OCV but not MFP
def make_ocikde(op_class, channel, seg1_idx):
WLAN_EID_VENDOR_SPECIFIC = 221
RSN_KEY_DATA_OCI = b"\x00\x0f\xac\x0d"
data = RSN_KEY_DATA_OCI + struct.pack("<BBB", op_class, channel, seg1_idx)
ocikde = struct.pack("<BB", WLAN_EID_VENDOR_SPECIFIC, len(data)) + data
return ocikde
def ocv_setup_ap(apdev, params):
ssid = "test-wpa2-ocv"
passphrase = "qwertyuiop"
params.update(hostapd.wpa2_params(ssid=ssid, passphrase=passphrase))
try:
hapd = hostapd.add_ap(apdev, params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
return hapd, ssid, passphrase
def build_eapol_key_1_2(kck, key_data, replay_counter=3, key_info=0x1382,
extra_len=0, descr_type=2, key_len=16):
msg = {}
msg['version'] = 2
msg['type'] = 3
msg['length'] = 95 + len(key_data) + extra_len
msg['descr_type'] = descr_type
msg['rsn_key_info'] = key_info
msg['rsn_key_len'] = key_len
msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
msg['rsn_key_nonce'] = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')
msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
msg['rsn_key_data_len'] = len(key_data)
msg['rsn_key_data'] = key_data
eapol_key_mic(kck, msg)
return msg
def build_eapol_key_2_2(kck, key_data, replay_counter=3, key_info=0x0302,
extra_len=0, descr_type=2, key_len=16):
return build_eapol_key_1_2(kck, key_data, replay_counter, key_info,
extra_len, descr_type, key_len)
@remote_compatible
def test_wpa2_ocv(dev, apdev):
"""OCV on 2.4 GHz"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv=str(ocv),
ieee80211w="1")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
@remote_compatible
def test_wpa2_ocv_5ghz(dev, apdev):
"""OCV on 5 GHz"""
try:
run_wpa2_ocv_5ghz(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
def run_wpa2_ocv_5ghz(dev, apdev):
params = {"hw_mode": "a",
"channel": "40",
"ieee80211w": "2",
"country_code": "US",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq="5200", ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
@remote_compatible
def test_wpa2_ocv_ht20(dev, apdev):
"""OCV with HT20 channel"""
params = {"channel": "6",
"ieee80211n": "1",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq="2437", ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq="2437", ocv=str(ocv),
ieee80211w="1")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
@remote_compatible
def test_wpa2_ocv_ht40(dev, apdev):
"""OCV with HT40 channel"""
try:
run_wpa2_ocv_ht40(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
def run_wpa2_ocv_ht40(dev, apdev):
for channel, capab, freq, mode in [("6", "[HT40-]", "2437", "g"),
("6", "[HT40+]", "2437", "g"),
("40", "[HT40-]", "5200", "a"),
("36", "[HT40+]", "5180", "a")]:
params = {"hw_mode": mode,
"channel": channel,
"country_code": "US",
"ieee80211n": "1",
"ht_capab": capab,
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
hapd.disable()
@remote_compatible
def test_wpa2_ocv_vht40(dev, apdev):
"""OCV with VHT40 channel"""
try:
run_wpa2_ocv_vht40(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def run_wpa2_ocv_vht40(dev, apdev):
for channel, capab, freq in [("40", "[HT40-]", "5200"),
("36", "[HT40+]", "5180")]:
params = {"hw_mode": "a",
"channel": channel,
"country_code": "US",
"ht_capab": capab,
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "38",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_vht="1")
dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[2].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
dev[2].wait_disconnected()
hapd.disable()
@remote_compatible
def test_wpa2_ocv_vht80(dev, apdev):
"""OCV with VHT80 channel"""
try:
run_wpa2_ocv_vht80(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def run_wpa2_ocv_vht80(dev, apdev):
for channel, capab, freq in [("40", "[HT40-]", "5200"),
("36", "[HT40+]", "5180")]:
params = {"hw_mode": "a",
"channel": channel,
"country_code": "US",
"ht_capab": capab,
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_vht="1")
dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[2].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
dev[2].wait_disconnected()
hapd.disable()
@remote_compatible
def test_wpa2_ocv_vht160(dev, apdev):
"""OCV with VHT160 channel"""
try:
run_wpa2_ocv_vht160(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def run_wpa2_ocv_vht160(dev, apdev):
for channel, capab, freq in [("100", "[HT40+]", "5500"),
("104", "[HT40-]", "5520")]:
params = {"hw_mode": "a",
"channel": channel,
"country_code": "ZA",
"ht_capab": capab,
"vht_capab": "[VHT160]",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "114",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_vht="1")
dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[2].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
dev[2].wait_disconnected()
hapd.disable()
@remote_compatible
def test_wpa2_ocv_vht80plus80(dev, apdev):
"""OCV with VHT80+80 channel"""
try:
run_wpa2_ocv_vht80plus80(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
dev[1].flush_scan_cache()
dev[2].flush_scan_cache()
def run_wpa2_ocv_vht80plus80(dev, apdev):
for channel, capab, freq in [("36", "[HT40+]", "5180"),
("40", "[HT40-]", "5200")]:
params = {"hw_mode": "a",
"channel": channel,
"country_code": "US",
"ht_capab": capab,
"vht_capab": "[VHT160-80PLUS80]",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "3",
"vht_oper_centr_freq_seg0_idx": "42",
"vht_oper_centr_freq_seg1_idx": "155",
"ieee80211w": "1",
"ieee80211d": "1",
"ieee80211h": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_ht="1")
dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1", disable_vht="1")
dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
dev[0].wait_regdom(country_ie=True)
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[2].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[1].wait_disconnected()
dev[2].wait_disconnected()
for i in range(3):
dev[i].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
ieee80211w="1")
if i == 0:
dev[i].wait_regdom(country_ie=True)
hapd.disable()
for i in range(3):
dev[i].request("DISCONNECT")
for i in range(3):
dev[i].disconnect_and_stop_scan()
class APConnection:
def init_params(self):
# Static parameters
self.ssid = "test-wpa2-ocv"
self.passphrase = "qwertyuiop"
self.psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
# Dynamic parameters
self.hapd = None
self.addr = None
self.rsne = None
self.kck = None
self.kek = None
self.msg = None
self.bssid = None
self.anonce = None
self.snonce = None
def __init__(self, apdev, dev, params):
self.init_params()
# By default, OCV is enabled for both the client and AP. The following
# parameters can be used to disable OCV for the client or AP.
ap_ocv = params.pop("ap_ocv", "1")
sta_ocv = params.pop("sta_ocv", "1")
freq = params.pop("freq")
params.update(hostapd.wpa2_params(ssid=self.ssid,
passphrase=self.passphrase))
params["wpa_pairwise_update_count"] = "10"
params["ocv"] = ap_ocv
try:
self.hapd = hostapd.add_ap(apdev, params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
self.hapd.request("SET ext_eapol_frame_io 1")
dev.request("SET ext_eapol_frame_io 1")
self.bssid = apdev['bssid']
pmk = binascii.unhexlify("c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7")
if sta_ocv != "0":
self.rsne = binascii.unhexlify("301a0100000fac040100000fac040100000fac0280400000000fac06")
else:
self.rsne = binascii.unhexlify("301a0100000fac040100000fac040100000fac0280000000000fac06")
self.snonce = binascii.unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
dev.connect(self.ssid, raw_psk=self.psk, scan_freq=freq, ocv=sta_ocv,
ieee80211w="1", wait_connect=False)
if "country_code" in params:
dev.wait_regdom(country_ie=True)
self.addr = dev.p2p_interface_addr()
# Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
self.msg = recv_eapol(self.hapd)
self.anonce = self.msg['rsn_key_nonce']
(ptk, self.kck, self.kek) = pmk_to_ptk(pmk, self.addr, self.bssid,
self.snonce, self.anonce)
# hapd, addr, rsne, kck, msg, anonce, snonce
def test_bad_oci(self, logmsg, op_class, channel, seg1_idx):
logger.debug("Bad OCI element: " + logmsg)
if op_class is None:
ocikde = b''
else:
ocikde = make_ocikde(op_class, channel, seg1_idx)
reply_eapol("2/4", self.hapd, self.addr, self.msg, 0x010a, self.snonce,
self.rsne + ocikde, self.kck)
self.msg = recv_eapol(self.hapd)
if self.anonce != self.msg['rsn_key_nonce'] or self.msg["rsn_key_info"] != 138:
raise Exception("Didn't receive retransmitted 1/4")
def confirm_valid_oci(self, op_class, channel, seg1_idx):
logger.debug("Valid OCI element to complete handshake")
ocikde = make_ocikde(op_class, channel, seg1_idx)
reply_eapol("2/4", self.hapd, self.addr, self.msg, 0x010a, self.snonce,
self.rsne + ocikde, self.kck)
self.msg = recv_eapol(self.hapd)
if self.anonce != self.msg['rsn_key_nonce'] or self.msg["rsn_key_info"] != 5066:
raise Exception("Didn't receive 3/4 in response to valid 2/4")
reply_eapol("4/4", self.hapd, self.addr, self.msg, 0x030a, None, None,
self.kck)
self.hapd.wait_sta(timeout=15)
@remote_compatible
def test_wpa2_ocv_ap_mismatch(dev, apdev):
"""OCV AP mismatch"""
params = {"channel": "1",
"ieee80211w": "1",
"freq": "2412"}
conn = APConnection(apdev[0], dev[0], params)
conn.test_bad_oci("element missing", None, 0, 0)
conn.test_bad_oci("wrong channel number", 81, 6, 0)
conn.test_bad_oci("invalid channel number", 81, 0, 0)
conn.test_bad_oci("wrong operating class", 80, 0, 0)
conn.test_bad_oci("invalid operating class", 0, 0, 0)
conn.confirm_valid_oci(81, 1, 0)
@remote_compatible
def test_wpa2_ocv_ap_ht_mismatch(dev, apdev):
"""OCV AP mismatch (HT)"""
params = {"channel": "6",
"ht_capab": "[HT40-]",
"ieee80211w": "1",
"freq": "2437"}
conn = APConnection(apdev[0], dev[0], params)
conn.test_bad_oci("wrong primary channel", 84, 5, 0)
conn.test_bad_oci("lower bandwidth than negotiated", 81, 6, 0)
conn.test_bad_oci("bad upper/lower channel", 83, 6, 0)
conn.confirm_valid_oci(84, 6, 0)
@remote_compatible
def test_wpa2_ocv_ap_vht80_mismatch(dev, apdev):
"""OCV AP mismatch (VHT80)"""
try:
run_wpa2_ocv_ap_vht80_mismatch(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].flush_scan_cache()
def run_wpa2_ocv_ap_vht80_mismatch(dev, apdev):
params = {"hw_mode": "a",
"channel": "36",
"country_code": "US",
"ht_capab": "[HT40+]",
"ieee80211w": "1",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "1",
"freq": "5180",
"vht_oper_centr_freq_seg0_idx": "42"}
conn = APConnection(apdev[0], dev[0], params)
conn.test_bad_oci("wrong primary channel", 128, 38, 0)
conn.test_bad_oci("wrong primary channel", 128, 32, 0)
conn.test_bad_oci("smaller bandwidth than negotiated", 116, 36, 0)
conn.test_bad_oci("smaller bandwidth than negotiated", 115, 36, 0)
conn.confirm_valid_oci(128, 36, 0)
dev[0].dump_monitor()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
@remote_compatible
def test_wpa2_ocv_ap_vht160_mismatch(dev, apdev):
"""OCV AP mismatch (VHT160)"""
try:
run_wpa2_ocv_ap_vht160_mismatch(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def run_wpa2_ocv_ap_vht160_mismatch(dev, apdev):
params = {"hw_mode": "a",
"channel": "100",
"country_code": "ZA",
"ht_capab": "[HT40+]",
"ieee80211w": "1",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "2",
"freq": "5500",
"vht_oper_centr_freq_seg0_idx": "114",
"ieee80211d": "1",
"ieee80211h": "1"}
conn = APConnection(apdev[0], dev[0], params)
conn.test_bad_oci("wrong primary channel", 129, 36, 0)
conn.test_bad_oci("wrong primary channel", 129, 114, 0)
conn.test_bad_oci("smaller bandwidth (20 Mhz) than negotiated", 121, 100, 0)
conn.test_bad_oci("smaller bandwidth (40 Mhz) than negotiated", 122, 100, 0)
conn.test_bad_oci("smaller bandwidth (80 Mhz) than negotiated", 128, 100, 0)
conn.test_bad_oci("using 80+80 channel instead of 160", 130, 100, 155)
conn.confirm_valid_oci(129, 100, 0)
dev[0].dump_monitor()
if conn.hapd:
conn.hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
@remote_compatible
def test_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev):
"""OCV AP mismatch (VHT80+80)"""
try:
run_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def run_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev):
params = {"hw_mode": "a",
"channel": "36",
"country_code": "US",
"ht_capab": "[HT40+]",
"ieee80211w": "1",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "3",
"freq": "5180",
"vht_oper_centr_freq_seg0_idx": "42",
"ieee80211d": "1",
"vht_oper_centr_freq_seg1_idx": "155",
"ieee80211h": "1"}
conn = APConnection(apdev[0], dev[0], params)
conn.test_bad_oci("using 80 MHz operating class", 128, 36, 155)
conn.test_bad_oci("wrong frequency segment 1", 130, 36, 138)
conn.confirm_valid_oci(130, 36, 155)
dev[0].dump_monitor()
if conn.hapd:
conn.hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
@remote_compatible
def test_wpa2_ocv_ap_unexpected1(dev, apdev):
"""OCV and unexpected OCI KDE from station"""
params = {"channel": "1",
"ieee80211w": "1",
"ap_ocv": "0",
"sta_ocv": "1",
"freq": "2412"}
conn = APConnection(apdev[0], dev[0], params)
logger.debug("Client will send OCI KDE even if it was not negotiated")
conn.confirm_valid_oci(81, 1, 0)
@remote_compatible
def test_wpa2_ocv_ap_unexpected2(dev, apdev):
"""OCV and unexpected OCI KDE from station"""
params = {"channel": "1",
"ieee80211w": "1",
"ap_ocv": "1",
"sta_ocv": "0",
"freq": "2412"}
conn = APConnection(apdev[0], dev[0], params)
logger.debug("Client will send OCI KDE even if it was not negotiated")
conn.confirm_valid_oci(81, 1, 0)
@remote_compatible
def test_wpa2_ocv_ap_retransmit_msg3(dev, apdev):
"""Verify that manually retransmitted msg 3/4 contain a correct OCI"""
bssid = apdev[0]['bssid']
ssid = "test-wpa2-ocv"
passphrase = "qwertyuiop"
psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
params = hostapd.wpa2_params(ssid=ssid)
params["wpa_psk"] = psk
params["ieee80211w"] = "1"
params["ocv"] = "1"
params['wpa_disable_eapol_key_retries'] = "1"
try:
hapd = hostapd.add_ap(apdev[0], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
hapd.request("SET ext_eapol_frame_io 1")
dev[0].request("SET ext_eapol_frame_io 1")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False,
ocv="1", ieee80211w="1")
addr = dev[0].own_addr()
# EAPOL-Key msg 1/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to wpa_supplicant failed")
# EAPOL-Key msg 2/4
ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
if "OK" not in res:
raise Exception("EAPOL_RX to hostapd failed")
# EAPOL-Key msg 3/4
ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
if ev is None:
raise Exception("Timeout on EAPOL-TX from hostapd")
logger.info("Drop the first EAPOL-Key msg 3/4")
# Use normal EAPOL TX/RX to handle retries.
hapd.request("SET ext_eapol_frame_io 0")
dev[0].request("SET ext_eapol_frame_io 0")
# Manually retransmit EAPOL-Key msg 3/4
if "OK" not in hapd.request("RESEND_M3 " + addr):
raise Exception("RESEND_M3 failed")
dev[0].wait_connected()
hwsim_utils.test_connectivity(dev[0], hapd)
def test_wpa2_ocv_ap_group_hs(dev, apdev):
"""OCV group handshake (AP)"""
params = {"channel": "1",
"ieee80211w": "1",
"freq": "2412",
"wpa_strict_rekey": "1"}
conn = APConnection(apdev[0], dev[0], params)
conn.confirm_valid_oci(81, 1, 0)
conn.hapd.request("SET ext_eapol_frame_io 0")
dev[1].connect(conn.ssid, psk=conn.passphrase, scan_freq="2412", ocv="1",
ieee80211w="1")
conn.hapd.wait_sta()
conn.hapd.request("SET ext_eapol_frame_io 1")
# Trigger a group key handshake
dev[1].request("DISCONNECT")
dev[0].dump_monitor()
# Wait for EAPOL-Key msg 1/2
conn.msg = recv_eapol(conn.hapd)
if conn.msg["rsn_key_info"] != 4994:
raise Exception("Didn't receive 1/2 of group key handshake")
# Send a EAPOL-Key msg 2/2 with a bad OCI
logger.info("Bad OCI element")
ocikde = make_ocikde(1, 1, 1)
msg = build_eapol_key_2_2(conn.kck, ocikde, replay_counter=3)
conn.hapd.dump_monitor()
send_eapol(conn.hapd, conn.addr, build_eapol(msg))
# Wait for retransmitted EAPOL-Key msg 1/2
conn.msg = recv_eapol(conn.hapd)
if conn.msg["rsn_key_info"] != 4994:
raise Exception("Didn't receive 1/2 of group key handshake")
# Send a EAPOL-Key msg 2/2 with a good OCI
logger.info("Good OCI element")
ocikde = make_ocikde(81, 1, 0)
msg = build_eapol_key_2_2(conn.kck, ocikde, replay_counter=4)
conn.hapd.dump_monitor()
send_eapol(conn.hapd, conn.addr, build_eapol(msg))
# Verify that group key handshake has completed
ev = conn.hapd.wait_event(["EAPOL-TX"], timeout=1)
if ev is not None:
eapol = binascii.unhexlify(ev.split(' ')[2])
msg = parse_eapol(eapol)
if msg["rsn_key_info"] == 4994:
raise Exception("AP didn't accept 2/2 of group key handshake")
class STAConnection:
def init_params(self):
# Static parameters
self.ssid = "test-wpa2-ocv"
self.passphrase = "qwertyuiop"
self.psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
# Dynamic parameters
self.hapd = None
self.dev = None
self.addr = None
self.rsne = None
self.kck = None
self.kek = None
self.msg = None
self.bssid = None
self.anonce = None
self.snonce = None
self.gtkie = None
self.counter = None
def __init__(self, apdev, dev, params, sta_params=None):
self.init_params()
self.dev = dev
self.bssid = apdev['bssid']
freq = params.pop("freq")
if sta_params is None:
sta_params = dict()
if "ocv" not in sta_params:
sta_params["ocv"] = "1"
if "ieee80211w" not in sta_params:
sta_params["ieee80211w"] = "1"
params.update(hostapd.wpa2_params(ssid=self.ssid,
passphrase=self.passphrase))
params['wpa_pairwise_update_count'] = "10"
try:
self.hapd = hostapd.add_ap(apdev, params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
self.hapd.request("SET ext_eapol_frame_io 1")
self.dev.request("SET ext_eapol_frame_io 1")
pmk = binascii.unhexlify("c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7")
self.gtkie = binascii.unhexlify("dd16000fac010100dc11188831bf4aa4a8678d2b41498618")
if sta_params["ocv"] != "0":
self.rsne = binascii.unhexlify("30140100000fac040100000fac040100000fac028c40")
else:
self.rsne = binascii.unhexlify("30140100000fac040100000fac040100000fac028c00")
self.dev.connect(self.ssid, raw_psk=self.psk, scan_freq=freq,
wait_connect=False, **sta_params)
if "country_code" in params:
self.dev.wait_regdom(country_ie=True)
self.addr = dev.p2p_interface_addr()
# Forward msg 1/4 from AP to STA
self.msg = recv_eapol(self.hapd)
self.anonce = self.msg['rsn_key_nonce']
send_eapol(self.dev, self.bssid, build_eapol(self.msg))
# Capture msg 2/4 from the STA so we can derive the session keys
self.msg = recv_eapol(dev)
self.snonce = self.msg['rsn_key_nonce']
(ptk, self.kck, self.kek) = pmk_to_ptk(pmk, self.addr, self.bssid,
self.snonce, self.anonce)
self.counter = struct.unpack('>Q',
self.msg['rsn_replay_counter'])[0] + 1
def test_bad_oci(self, logmsg, op_class, channel, seg1_idx, errmsg):
logger.info("Bad OCI element: " + logmsg)
if op_class is None:
ocikde = b''
else:
ocikde = make_ocikde(op_class, channel, seg1_idx)
plain = self.rsne + self.gtkie + ocikde
wrapped = aes_wrap(self.kek, pad_key_data(plain))
msg = build_eapol_key_3_4(self.anonce, self.kck, wrapped,
replay_counter=self.counter)
self.dev.dump_monitor()
send_eapol(self.dev, self.bssid, build_eapol(msg))
self.counter += 1
ev = self.dev.wait_event([errmsg], timeout=5)
if ev is None:
raise Exception("Bad OCI not reported")
def confirm_valid_oci(self, op_class, channel, seg1_idx):
logger.debug("Valid OCI element to complete handshake")
ocikde = make_ocikde(op_class, channel, seg1_idx)
plain = self.rsne + self.gtkie + ocikde
wrapped = aes_wrap(self.kek, pad_key_data(plain))
msg = build_eapol_key_3_4(self.anonce, self.kck, wrapped,
replay_counter=self.counter)
self.dev.dump_monitor()
send_eapol(self.dev, self.bssid, build_eapol(msg))
self.counter += 1
self.dev.wait_connected(timeout=1)
@remote_compatible
def test_wpa2_ocv_mismatch_client(dev, apdev):
"""OCV client mismatch"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "1",
"freq": "2412"}
conn = STAConnection(apdev[0], dev[0], params)
conn.test_bad_oci("element missing", None, 0, 0,
"did not receive mandatory OCI")
conn.test_bad_oci("wrong channel number", 81, 6, 0,
"primary channel mismatch")
conn.test_bad_oci("invalid channel number", 81, 0, 0,
"unable to interpret received OCI")
conn.test_bad_oci("wrong operating class", 80, 0, 0,
"unable to interpret received OCI")
conn.test_bad_oci("invalid operating class", 0, 0, 0,
"unable to interpret received OCI")
conn.confirm_valid_oci(81, 1, 0)
@remote_compatible
def test_wpa2_ocv_vht160_mismatch_client(dev, apdev):
"""OCV client mismatch (VHT160)"""
try:
run_wpa2_ocv_vht160_mismatch_client(dev, apdev)
finally:
set_world_reg(apdev[0], apdev[1], dev[0])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def run_wpa2_ocv_vht160_mismatch_client(dev, apdev):
params = {"hw_mode": "a",
"channel": "100",
"country_code": "ZA",
"ht_capab": "[HT40+]",
"ieee80211w": "1",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "2",
"ocv": "1",
"vht_oper_centr_freq_seg0_idx": "114",
"freq": "5500",
"ieee80211d": "1",
"ieee80211h": "1"}
sta_params = {"disable_vht": "1"}
conn = STAConnection(apdev[0], dev[0], params, sta_params)
conn.test_bad_oci("smaller bandwidth (20 Mhz) than negotiated",
121, 100, 0, "channel bandwidth mismatch")
conn.test_bad_oci("wrong frequency, bandwith, and secondary channel",
123, 104, 0, "primary channel mismatch")
conn.test_bad_oci("wrong upper/lower behaviour",
129, 104, 0, "primary channel mismatch")
conn.confirm_valid_oci(122, 100, 0)
dev[0].dump_monitor()
if conn.hapd:
conn.hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
def test_wpa2_ocv_sta_group_hs(dev, apdev):
"""OCV group handshake (STA)"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "1",
"freq": "2412",
"wpa_strict_rekey": "1"}
conn = STAConnection(apdev[0], dev[0], params.copy())
conn.confirm_valid_oci(81, 1, 0)
# Send a EAPOL-Key msg 1/2 with a bad OCI
logger.info("Bad OCI element")
plain = conn.gtkie + make_ocikde(1, 1, 1)
wrapped = aes_wrap(conn.kek, pad_key_data(plain))
msg = build_eapol_key_1_2(conn.kck, wrapped, replay_counter=3)
send_eapol(dev[0], conn.bssid, build_eapol(msg))
# We shouldn't get a EAPOL-Key message back
ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
if ev is not None:
raise Exception("Received response to invalid EAPOL-Key 1/2")
# Reset AP to try with valid OCI
conn.hapd.disable()
conn = STAConnection(apdev[0], dev[0], params.copy())
conn.confirm_valid_oci(81, 1, 0)
# Send a EAPOL-Key msg 1/2 with a good OCI
logger.info("Good OCI element")
plain = conn.gtkie + make_ocikde(81, 1, 0)
wrapped = aes_wrap(conn.kek, pad_key_data(plain))
msg = build_eapol_key_1_2(conn.kck, wrapped, replay_counter=4)
send_eapol(dev[0], conn.bssid, build_eapol(msg))
# Wait for EAPOL-Key msg 2/2
conn.msg = recv_eapol(dev[0])
if conn.msg["rsn_key_info"] != 0x0302:
raise Exception("Didn't receive 2/2 of group key handshake")
def test_wpa2_ocv_auto_enable_pmf(dev, apdev):
"""OCV on 2.4 GHz with PMF getting enabled automatically"""
params = {"channel": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
for ocv in range(2):
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv=str(ocv),
ieee80211w="2")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
def test_wpa2_ocv_sta_override_eapol(dev, apdev):
"""OCV on 2.4 GHz and STA override EAPOL-Key msg 2/4"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
dev[0].set("oci_freq_override_eapol", "2462")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=15)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("No connection result reported")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "reason=15" not in ev:
raise Exception("Unexpected disconnection reason: " + ev)
check_ocv_failure(hapd, "EAPOL-Key msg 2/4", "eapol-key-m2",
dev[0].own_addr())
def test_wpa2_ocv_sta_override_sa_query_req(dev, apdev):
"""OCV on 2.4 GHz and STA override SA Query Request"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2")
hapd.wait_sta()
dev[0].set("oci_freq_override_saquery_req", "2462")
if "OK" not in dev[0].request("UNPROT_DEAUTH"):
raise Exception("Triggering SA Query from the STA failed")
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
if ev is None:
raise Exception("Disconnection after failed SA Query not reported")
dev[0].set("oci_freq_override_saquery_req", "0")
dev[0].wait_connected()
if "OK" not in dev[0].request("UNPROT_DEAUTH"):
raise Exception("Triggering SA Query from the STA failed")
check_ocv_failure(hapd, "SA Query Request", "saqueryreq",
dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
if ev is not None:
raise Exception("SA Query from the STA failed")
def test_wpa2_ocv_sta_override_sa_query_resp(dev, apdev):
"""OCV on 2.4 GHz and STA override SA Query Response"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2")
dev[0].set("oci_freq_override_saquery_resp", "2462")
hapd.wait_sta()
if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
raise Exception("SA_QUERY failed")
check_ocv_failure(hapd, "SA Query Response", "saqueryresp",
dev[0].own_addr())
def check_ocv_failure(dev, frame_txt, frame, addr):
ev = dev.wait_event(["OCV-FAILURE"], timeout=3)
if ev is None:
raise Exception("OCV failure for %s not reported" % frame_txt)
if "addr=" + addr not in ev:
raise Exception("Unexpected OCV failure addr: " + ev)
if "frame=" + frame not in ev:
raise Exception("Unexpected OCV failure frame: " + ev)
if "error=primary channel mismatch" not in ev:
raise Exception("Unexpected OCV failure error: " + ev)
def test_wpa2_ocv_ap_override_eapol_m3(dev, apdev):
"""OCV on 2.4 GHz and AP override EAPOL-Key msg 3/4"""
+ run_wpa2_ocv_ap_override_eapol_m3(dev, apdev)
+
+def test_wpa2_ocv_ap_override_eapol_m3_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key msg 3/4 (post enable)"""
+ run_wpa2_ocv_ap_override_eapol_m3(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_eapol_m3(dev, apdev, post_enable=False):
params = {"channel": "1",
"ieee80211w": "2",
- "ocv": "1",
- "oci_freq_override_eapol_m3": "2462"}
+ "ocv": "1"}
+ if not post_enable:
+ params["oci_freq_override_eapol_m3"] = "2462"
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
bssid = hapd.own_addr()
+ if post_enable:
+ hapd.set("oci_freq_override_eapol_m3", "2462")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2", wait_connect=False)
check_ocv_failure(dev[0], "EAPOL-Key msg 3/4", "eapol-key-m3", bssid)
ev = dev[0].wait_disconnected()
if "reason=15" not in ev:
raise Exception("Unexpected disconnection reason: " + ev)
def test_wpa2_ocv_ap_override_eapol_g1(dev, apdev):
"""OCV on 2.4 GHz and AP override EAPOL-Key group msg 1/2"""
+ run_wpa2_ocv_ap_override_eapol_g1(dev, apdev)
+
+def test_wpa2_ocv_ap_override_eapol_g1_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key group msg 1/2 (post enable)"""
+ run_wpa2_ocv_ap_override_eapol_g1(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_eapol_g1(dev, apdev, post_enable=False):
params = {"channel": "1",
"ieee80211w": "2",
- "ocv": "1",
- "oci_freq_override_eapol_g1": "2462"}
+ "ocv": "1"}
+ if not post_enable:
+ params["oci_freq_override_eapol_g1"] = "2462"
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2")
+ if post_enable:
+ hapd.set("oci_freq_override_eapol_g1", "2462")
if "OK" not in hapd.request("REKEY_GTK"):
raise Exception("REKEY_GTK failed")
check_ocv_failure(dev[0], "EAPOL-Key group msg 1/2", "eapol-key-g1", bssid)
def test_wpa2_ocv_ap_override_saquery_req(dev, apdev):
"""OCV on 2.4 GHz and AP override SA Query Request"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1",
"oci_freq_override_saquery_req": "2462"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2")
if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
raise Exception("SA_QUERY failed")
check_ocv_failure(dev[0], "SA Query Request", "saqueryreq", bssid)
def test_wpa2_ocv_ap_override_saquery_resp(dev, apdev):
"""OCV on 2.4 GHz and AP override SA Query Response"""
params = {"channel": "1",
"ieee80211w": "2",
"ocv": "1",
"oci_freq_override_saquery_resp": "2462"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
ieee80211w="2")
if "OK" not in dev[0].request("UNPROT_DEAUTH"):
raise Exception("Triggering SA Query from the STA failed")
check_ocv_failure(dev[0], "SA Query Response", "saqueryresp", bssid)
def test_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params):
"""OCV on 2.4 GHz and AP override FILS association"""
+ run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params)
+
+def test_wpa2_ocv_ap_override_fils_assoc_post_enable(dev, apdev, params):
+ """OCV on 2.4 GHz and AP override FILS association (post enable)"""
+ run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params, True)
+
+def run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params, post_enable=False):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
ssid = "test-wpa2-ocv"
params = hostapd.wpa2_eap_params(ssid=ssid)
params['wpa_key_mgmt'] = "FILS-SHA256"
params['auth_server_port'] = "18128"
params['erp_send_reauth_start'] = '1'
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['wpa_group_rekey'] = '1'
params["ieee80211w"] = "2"
params["ocv"] = "1"
- params["oci_freq_override_fils_assoc"] = "2462"
+ if not post_enable:
+ params["oci_freq_override_fils_assoc"] = "2462"
try:
hapd = hostapd.add_ap(apdev[0], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
bssid = hapd.own_addr()
+ if post_enable:
+ hapd.set("oci_freq_override_fils_assoc", "2462")
dev[0].request("ERP_FLUSH")
id = dev[0].connect(ssid, key_mgmt="FILS-SHA256",
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412", ocv="1", ieee80211w="2")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
check_ocv_failure(dev[0], "FILS Association Response", "fils-assoc", bssid)
dev[0].request("DISCONNECT")
def test_wpa2_ocv_ap_override_ft_assoc(dev, apdev):
"""OCV on 2.4 GHz and AP override FT reassociation"""
+ run_wpa2_ocv_ap_override_ft_assoc(dev, apdev)
+
+def test_wpa2_ocv_ap_override_ft_assoc_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override FT reassociation (post enable)"""
+ run_wpa2_ocv_ap_override_ft_assoc(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_ft_assoc(dev, apdev, post_enable=False):
ssid = "test-wpa2-ocv"
passphrase = "qwertyuiop"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
- params["oci_freq_override_fils_assoc"] = "2462"
+ if not post_enable:
+ params["oci_freq_override_ft_assoc"] = "2462"
try:
hapd0 = hostapd.add_ap(apdev[0], params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params["ocv"] = "1"
- params["oci_freq_override_ft_assoc"] = "2462"
+ if not post_enable:
+ params["oci_freq_override_ft_assoc"] = "2462"
hapd1 = hostapd.add_ap(apdev[1], params)
+ if post_enable:
+ hapd0.set("oci_freq_override_ft_assoc", "2462")
+ hapd1.set("oci_freq_override_ft_assoc", "2462")
+
dev[0].connect(ssid, key_mgmt="FT-PSK", psk=passphrase,
scan_freq="2412", ocv="1", ieee80211w="2")
bssid = dev[0].get_status_field("bssid")
bssid0 = hapd0.own_addr()
bssid1 = hapd1.own_addr()
target = bssid0 if bssid == bssid1 else bssid1
dev[0].scan_for_bss(target, freq="2412")
if "OK" not in dev[0].request("ROAM " + target):
raise Exception("ROAM failed")
check_ocv_failure(dev[0], "FT Reassociation Response", "ft-assoc", target)
dev[0].request("DISCONNECT")
@remote_compatible
def test_wpa2_ocv_no_pmf(dev, apdev):
"""OCV on 2.4 GHz and no PMF on STA"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
ie = "301a0100000fac040100000fac040100000fac0200400000000fac06"
if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
raise Exception("Could not set TEST_ASSOC_IE")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
ieee80211w="0", wait_connect=False)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT"],
timeout=10)
dev[0].request("DISCONNECT")
if ev is None:
raise Exception("No connection result seen")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if "status_code=31" not in ev:
raise Exception("Unexpected status code: " + ev)
@remote_compatible
def test_wpa2_ocv_no_pmf_workaround(dev, apdev):
"""OCV on 2.4 GHz and no PMF on STA with workaround"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "2"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
ie = "301a0100000fac040100000fac040100000fac0200400000000fac06"
if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
raise Exception("Could not set TEST_ASSOC_IE")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
ieee80211w="0")
@remote_compatible
def test_wpa2_ocv_no_oci(dev, apdev):
"""OCV on 2.4 GHz and no OCI from STA"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
ie = "301a0100000fac040100000fac040100000fac0280400000000fac06"
if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
raise Exception("Could not set TEST_ASSOC_IE")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
ieee80211w="1", wait_connect=False)
ev = hapd.wait_event(["OCV-FAILURE"], timeout=10)
if ev is None:
raise Exception("No OCV failure reported")
if "frame=eapol-key-m2 error=did not receive mandatory OCI" not in ev:
raise Exception("Unexpected error: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
"WPA: 4-Way Handshake failed"], timeout=10)
dev[0].request("DISCONNECT")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("Unexpected connection")
if ev is None:
raise Exception("4-way handshake failure not reported")
@remote_compatible
def test_wpa2_ocv_no_oci_workaround(dev, apdev):
"""OCV on 2.4 GHz and no OCI from STA with workaround"""
params = {"channel": "1",
"ieee80211w": "1",
"ocv": "2"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
ie = "301a0100000fac040100000fac040100000fac0280400000000fac06"
if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
raise Exception("Could not set TEST_ASSOC_IE")
dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
ieee80211w="1")
def test_wpa2_ocv_without_pmf(dev, apdev):
"""OCV without PMF"""
params = {"channel": "6",
"ieee80211n": "1",
"ieee80211w": "1",
"ocv": "1"}
hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
hapd.disable()
hapd.set("ieee80211w", "0")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("OCV without PMF accepted")
diff --git a/tests/hwsim/test_p2p_discovery.py b/tests/hwsim/test_p2p_discovery.py
index 4bce4ecccfbb..0537f02e9e5b 100644
--- a/tests/hwsim/test_p2p_discovery.py
+++ b/tests/hwsim/test_p2p_discovery.py
@@ -1,820 +1,871 @@
# P2P device discovery test cases
# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import logging
logger = logging.getLogger()
import binascii
import os
import struct
import time
import hostapd
import hwsim_utils
from wpasupplicant import WpaSupplicant
from p2p_utils import *
from hwsim import HWSimRadio
from tshark import run_tshark
from test_gas import start_ap
from test_cfg80211 import nl80211_remain_on_channel
from test_p2p_channel import set_country
@remote_compatible
def test_discovery(dev):
"""P2P device discovery and provision discovery"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
logger.info("Start device discovery")
dev[0].p2p_find(social=True, delay=1)
if not dev[1].discover_peer(addr0):
raise Exception("Device discovery timed out")
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
logger.info("Test provision discovery for display")
dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
if ev1 is None:
raise Exception("Provision discovery timed out (display/dev1)")
if addr0 not in ev1:
raise Exception("Dev0 not in provision discovery event")
ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
"P2P-PROV-DISC-FAILURE"], timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (display/dev0)")
if "P2P-PROV-DISC-FAILURE" in ev0:
raise Exception("Provision discovery failed (display/dev0)")
if addr1 not in ev0:
raise Exception("Dev1 not in provision discovery event")
logger.info("Test provision discovery for keypad")
dev[0].global_request("P2P_PROV_DISC " + addr1 + " keypad")
ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
if ev1 is None:
raise Exception("Provision discovery timed out (keypad/dev1)")
if addr0 not in ev1:
raise Exception("Dev0 not in provision discovery event")
ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN",
"P2P-PROV-DISC-FAILURE"],
timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (keypad/dev0)")
if "P2P-PROV-DISC-FAILURE" in ev0:
raise Exception("Provision discovery failed (keypad/dev0)")
if addr1 not in ev0:
raise Exception("Dev1 not in provision discovery event")
logger.info("Test provision discovery for push button")
dev[0].global_request("P2P_PROV_DISC " + addr1 + " pbc")
ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-PBC-REQ"], timeout=15)
if ev1 is None:
raise Exception("Provision discovery timed out (pbc/dev1)")
if addr0 not in ev1:
raise Exception("Dev0 not in provision discovery event")
ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-RESP",
"P2P-PROV-DISC-FAILURE"],
timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (pbc/dev0)")
if "P2P-PROV-DISC-FAILURE" in ev0:
raise Exception("Provision discovery failed (pbc/dev0)")
if addr1 not in ev0:
raise Exception("Dev1 not in provision discovery event")
dev[0].p2p_stop_find()
dev[1].p2p_stop_find()
if "FAIL" not in dev[0].p2p_find(dev_id="foo"):
raise Exception("P2P_FIND with invalid dev_id accepted")
if "FAIL" not in dev[0].p2p_find(dev_type="foo"):
raise Exception("P2P_FIND with invalid dev_type accepted")
if "FAIL" not in dev[0].p2p_find(dev_type="1-foo-2"):
raise Exception("P2P_FIND with invalid dev_type accepted")
if "FAIL" not in dev[0].p2p_find(dev_type="1-11223344"):
raise Exception("P2P_FIND with invalid dev_type accepted")
if "FAIL" not in dev[0].global_request("P2P_PROV_DISC foo pbc"):
raise Exception("Invalid P2P_PROV_DISC accepted")
if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55"):
raise Exception("Invalid P2P_PROV_DISC accepted")
if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 pbc join"):
raise Exception("Invalid P2P_PROV_DISC accepted")
if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 foo"):
raise Exception("Invalid P2P_PROV_DISC accepted")
@remote_compatible
def test_discovery_pd_retries(dev):
"""P2P device discovery and provision discovery retries"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
dev[1].p2p_listen()
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
dev[1].p2p_stop_find()
dev[0].p2p_stop_find()
dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=60)
if ev is None:
raise Exception("No PD failure reported")
def test_discovery_group_client(dev):
"""P2P device discovery for a client in a group"""
logger.info("Start autonomous GO " + dev[0].ifname)
res = dev[0].p2p_start_go(freq="2422")
logger.debug("res: " + str(res))
logger.info("Connect a client to the GO")
pin = dev[1].wps_read_pin()
dev[0].p2p_go_authorize_client(pin)
dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, freq=int(res['freq']),
timeout=60)
logger.info("Client connected")
hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
logger.info("Try to discover a P2P client in a group")
if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
stop_p2p_find_and_wait(dev[2])
if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
stop_p2p_find_and_wait(dev[2])
if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
raise Exception("Could not discover group client")
# This is not really perfect, but something to get a bit more testing
# coverage.. For proper discoverability mechanism validation, the P2P
# client would need to go to sleep to avoid acknowledging the GO Negotiation
# Request frame. Offchannel Listen mode operation on the P2P Client with
# mac80211_hwsim is apparently not enough to avoid the acknowledgement on
# the operating channel, so need to disconnect from the group which removes
# the GO-to-P2P Client part of the discoverability exchange in practice.
pin = dev[2].wps_read_pin()
# make group client non-responsive on operating channel
dev[1].dump_monitor()
dev[1].group_request("DISCONNECT")
ev = dev[1].wait_group_event(["CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("Timeout on waiting disconnection")
dev[2].request("P2P_CONNECT {} {} display".format(dev[1].p2p_dev_addr(),
pin))
ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=2)
if ev:
raise Exception("Unexpected frame RX on P2P client")
# make group client available on operating channe
dev[1].group_request("REASSOCIATE")
ev = dev[1].wait_global_event(["CTRL-EVENT-CONNECTED",
"P2P-GO-NEG-REQUEST"], timeout=10)
if ev is None:
raise Exception("Timeout on reconnection to group")
if "P2P-GO-NEG-REQUEST" not in ev:
ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
if ev is None:
raise Exception("Timeout on waiting for GO Negotiation Request")
def stop_p2p_find_and_wait(dev):
dev.request("P2P_STOP_FIND")
for i in range(10):
res = dev.get_driver_status_field("scan_state")
if "SCAN_STARTED" not in res and "SCAN_REQUESTED" not in res:
break
logger.debug("Waiting for final P2P_FIND scan to complete")
time.sleep(0.02)
def test_discovery_ctrl_char_in_devname(dev):
"""P2P device discovery and control character in Device Name"""
try:
_test_discovery_ctrl_char_in_devname(dev)
finally:
dev[1].global_request("SET device_name Device B")
def _test_discovery_ctrl_char_in_devname(dev):
dev[1].global_request("SET device_name Device\tB")
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
res = dev[0].p2p_start_go(freq=2422)
bssid = dev[0].p2p_interface_addr()
pin = dev[1].wps_read_pin()
dev[0].p2p_go_authorize_client(pin)
dev[1].scan_for_bss(bssid, freq=2422)
dev[1].p2p_connect_group(addr0, pin, timeout=60, freq=2422)
if not dev[2].discover_peer(addr1, social=False, freq=2422, timeout=5):
stop_p2p_find_and_wait(dev[2])
if not dev[2].discover_peer(addr1, social=False, freq=2422, timeout=5):
stop_p2p_find_and_wait(dev[2])
if not dev[2].discover_peer(addr1, social=False, freq=2422,
timeout=5):
raise Exception("Could not discover group client")
devname = dev[2].get_peer(addr1)['device_name']
dev[2].p2p_stop_find()
if devname != "Device_B":
raise Exception("Unexpected device_name from group client: " + devname)
terminate_group(dev[0], dev[1])
dev[2].request("P2P_FLUSH")
dev[1].p2p_listen()
if not dev[2].discover_peer(addr1, social=True, timeout=10):
raise Exception("Could not discover peer")
devname = dev[2].get_peer(addr1)['device_name']
dev[2].p2p_stop_find()
if devname != "Device_B":
raise Exception("Unexpected device_name from peer: " + devname)
@remote_compatible
def test_discovery_dev_type(dev):
"""P2P device discovery with Device Type filter"""
dev[1].request("SET sec_device_type 1-0050F204-2")
dev[1].p2p_listen()
dev[0].p2p_find(social=True, dev_type="5-0050F204-1")
ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
if ev:
raise Exception("Unexpected P2P device found")
dev[0].p2p_find(social=True, dev_type="1-0050F204-2")
ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=2)
if ev is None:
raise Exception("P2P device not found")
peer = dev[0].get_peer(dev[1].p2p_dev_addr())
if "1-0050F204-2" not in peer['sec_dev_type']:
raise Exception("sec_device_type not reported properly")
def test_discovery_dev_type_go(dev):
"""P2P device discovery with Device Type filter on GO"""
addr1 = dev[1].p2p_dev_addr()
dev[1].request("SET sec_device_type 1-0050F204-2")
res = dev[0].p2p_start_go(freq="2412")
pin = dev[1].wps_read_pin()
dev[0].p2p_go_authorize_client(pin)
dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
dev[2].p2p_find(social=True, dev_type="5-0050F204-1")
ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
if ev:
raise Exception("Unexpected P2P device found")
dev[2].p2p_find(social=True, dev_type="1-0050F204-2")
ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
if ev is None:
raise Exception("P2P device not found")
def test_discovery_dev_id(dev):
"""P2P device discovery with Device ID filter"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.request("P2P_LISTEN 1")
status = wpas.global_request("STATUS")
if "p2p_state=LISTEN_ONLY" not in status:
raise Exception("Unexpected status: " + status)
addr1 = dev[1].p2p_dev_addr()
dev[1].p2p_listen()
dev[0].p2p_find(social=True, dev_id="02:03:04:05:06:07")
ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
if ev:
raise Exception("Unexpected P2P device found")
dev[0].p2p_find(social=True, dev_id=addr1)
ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=5)
if ev is None:
raise Exception("P2P device not found")
if addr1 not in ev:
raise Exception("Unexpected P2P peer found")
status = wpas.global_request("STATUS")
for i in range(0, 2):
if "p2p_state=IDLE" in status:
break
time.sleep(0.5)
status = wpas.global_request("STATUS")
if "p2p_state=IDLE" not in status:
raise Exception("Unexpected status: " + status)
def test_discovery_dev_id_go(dev):
"""P2P device discovery with Device ID filter on GO"""
addr1 = dev[1].p2p_dev_addr()
res = dev[0].p2p_start_go(freq="2412")
pin = dev[1].wps_read_pin()
dev[0].p2p_go_authorize_client(pin)
dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
dev[2].p2p_find(social=True, dev_id="02:03:04:05:06:07")
ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
if ev:
raise Exception("Unexpected P2P device found")
dev[2].p2p_find(social=True, dev_id=addr1)
ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
if ev is None:
raise Exception("P2P device not found")
def test_discovery_social_plus_one(dev):
"""P2P device discovery with social-plus-one"""
logger.info("Start autonomous GO " + dev[0].ifname)
dev[1].p2p_find(social=True)
dev[0].p2p_find(progressive=True)
logger.info("Wait for initial progressive find phases")
dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
go = dev[2].p2p_dev_addr()
dev[2].p2p_start_go(freq="2422")
logger.info("Verify whether the GO on non-social channel can be found")
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer not found")
if go not in ev:
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer not found")
ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer not found")
dev[0].p2p_stop_find()
dev[1].p2p_stop_find()
if not dev[0].peer_known(go):
raise Exception("GO not found in progressive scan")
if dev[1].peer_known(go):
raise Exception("GO found in social-only scan")
def _test_discovery_and_interface_disabled(dev, delay=1):
try:
if "OK" not in dev[0].p2p_find():
raise Exception("Failed to start P2P find")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
if ev is None:
raise Exception("Scan did not start")
dev[0].request("DRIVER_EVENT INTERFACE_DISABLED")
time.sleep(delay)
# verify that P2P_FIND is rejected
if "FAIL" not in dev[0].p2p_find():
raise Exception("New P2P_FIND request was accepted unexpectedly")
dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
time.sleep(3)
dev[0].scan(freq="2412")
if "OK" not in dev[0].p2p_find():
raise Exception("Failed to start P2P find")
dev[0].dump_monitor()
dev[1].p2p_listen()
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer not found")
finally:
dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
def test_discovery_and_interface_disabled(dev):
"""P2P device discovery with interface getting disabled"""
_test_discovery_and_interface_disabled(dev, delay=1)
_test_discovery_and_interface_disabled(dev, delay=5)
def test_discovery_auto(dev):
"""P2P device discovery and provision discovery with auto GO/dev selection"""
dev[0].flush_scan_cache()
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
addr2 = dev[2].p2p_dev_addr()
dev[2].p2p_start_go(freq="2412")
logger.info("Start device discovery")
dev[0].p2p_listen()
if not dev[1].discover_peer(addr0):
raise Exception("Device discovery timed out")
dev[1].p2p_listen()
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
if not dev[0].discover_peer(addr2):
raise Exception("Device discovery timed out")
logger.info("Test provision discovery for display (device)")
dev[0].global_request("P2P_PROV_DISC " + addr1 + " display auto")
ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
if ev1 is None:
raise Exception("Provision discovery timed out (display/dev1)")
if addr0 not in ev1:
raise Exception("Dev0 not in provision discovery event")
if " group=" in ev1:
raise Exception("Unexpected group parameter from non-GO")
ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
"P2P-PROV-DISC-FAILURE"], timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (display/dev0)")
if "P2P-PROV-DISC-FAILURE" in ev0:
raise Exception("Provision discovery failed (display/dev0)")
if addr1 not in ev0:
raise Exception("Dev1 not in provision discovery event")
if "peer_go=0" not in ev0:
raise Exception("peer_go incorrect in PD response from non-GO")
logger.info("Test provision discovery for display (GO)")
dev[0].global_request("P2P_PROV_DISC " + addr2 + " display auto")
ev2 = dev[2].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
if ev2 is None:
raise Exception("Provision discovery timed out (display/dev2)")
if addr0 not in ev2:
raise Exception("Dev0 not in provision discovery event")
if " group=" not in ev2:
raise Exception("Group parameter missing from GO")
ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
"P2P-PROV-DISC-FAILURE"], timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (display/dev0)")
if "P2P-PROV-DISC-FAILURE" in ev0:
raise Exception("Provision discovery failed (display/dev0)")
if addr2 not in ev0:
raise Exception("Dev1 not in provision discovery event")
if "peer_go=1" not in ev0:
raise Exception("peer_go incorrect in PD response from GO")
def test_discovery_stop(dev):
"""P2P device discovery and p2p_stop_find"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
dev[1].p2p_listen()
dev[2].p2p_listen()
dev[0].p2p_find(social=False)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
if ev is None:
logger.info("No CTRL-EVENT-SCAN-STARTED event")
dev[0].p2p_stop_find()
ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
if ev is None:
raise Exception("P2P_STOP not reported")
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
if ev is not None:
raise Exception("Peer found unexpectedly: " + ev)
dev[0].p2p_find(social=False)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
if ev is None:
logger.info("No CTRL-EVENT-SCAN-STARTED event")
dev[0].global_request("P2P_FLUSH")
ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
if ev is None:
raise Exception("P2P_STOP not reported")
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
if ev is not None:
raise Exception("Peer found unexpectedly: " + ev)
def test_discovery_restart(dev):
"""P2P device discovery and p2p_find restart"""
autogo(dev[1], freq=2457)
dev[0].p2p_find(social=True)
dev[0].p2p_stop_find()
dev[0].p2p_find(social=False)
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=7)
if ev is None:
dev[0].p2p_find(social=False)
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=7)
if ev is None:
raise Exception("Peer not found")
def test_discovery_restart_progressive(dev):
"""P2P device discovery and p2p_find type=progressive restart"""
try:
set_country("US", dev[1])
autogo(dev[1], freq=5805)
dev[0].p2p_find(social=True)
dev[0].p2p_stop_find()
dev[0].p2p_find(progressive=True)
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=20)
dev[1].remove_group()
if ev is None:
raise Exception("Peer not found")
finally:
set_country("00")
dev[1].flush_scan_cache()
def test_p2p_peer_command(dev):
"""P2P_PEER command"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
addr2 = dev[2].p2p_dev_addr()
dev[1].p2p_listen()
dev[2].p2p_listen()
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
if not dev[0].discover_peer(addr2):
raise Exception("Device discovery timed out")
dev[0].p2p_stop_find()
dev[1].p2p_stop_find()
dev[2].p2p_stop_find()
res0 = dev[0].request("P2P_PEER FIRST")
peer = res0.splitlines()[0]
if peer not in [addr1, addr2]:
raise Exception("Unexpected P2P_PEER FIRST address")
res1 = dev[0].request("P2P_PEER NEXT-" + peer)
peer2 = res1.splitlines()[0]
if peer2 not in [addr1, addr2] or peer == peer2:
raise Exception("Unexpected P2P_PEER NEXT address")
if "FAIL" not in dev[0].request("P2P_PEER NEXT-foo"):
raise Exception("Invalid P2P_PEER command accepted")
if "FAIL" not in dev[0].request("P2P_PEER foo"):
raise Exception("Invalid P2P_PEER command accepted")
if "FAIL" not in dev[0].request("P2P_PEER 00:11:22:33:44:55"):
raise Exception("P2P_PEER command for unknown peer accepted")
def test_p2p_listen_and_offchannel_tx(dev):
"""P2P_LISTEN behavior with offchannel TX"""
addr0 = dev[0].p2p_dev_addr()
addr1 = dev[1].p2p_dev_addr()
addr2 = dev[2].p2p_dev_addr()
dev[1].p2p_listen()
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
dev[0].p2p_listen()
dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
ev = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
if ev is None:
raise Exception("No PD result reported")
dev[1].p2p_stop_find()
if not dev[2].discover_peer(addr0):
raise Exception("Device discovery timed out after PD exchange")
dev[2].p2p_stop_find()
dev[0].p2p_stop_find()
@remote_compatible
def test_p2p_listen_and_scan(dev):
"""P2P_LISTEN and scan"""
dev[0].p2p_listen()
if "OK" not in dev[0].request("SCAN freq=2412"):
raise Exception("Failed to request a scan")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 3)
if ev is not None:
raise Exception("Unexpected scan results")
dev[0].p2p_stop_find()
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
if ev is None:
raise Exception("Scan timed out")
def test_p2p_config_methods(dev):
"""P2P and WPS config method update"""
addr0 = dev[0].p2p_dev_addr()
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
addr1 = wpas.p2p_dev_addr()
if "OK" not in wpas.request("SET config_methods keypad virtual_push_button"):
raise Exception("Failed to set config_methods")
wpas.p2p_listen()
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
dev[0].p2p_stop_find()
peer = dev[0].get_peer(addr1)
if peer['config_methods'] != '0x180':
raise Exception("Unexpected peer config methods(1): " + peer['config_methods'])
dev[0].global_request("P2P_FLUSH")
if "OK" not in wpas.request("SET config_methods virtual_display"):
raise Exception("Failed to set config_methods")
if not dev[0].discover_peer(addr1):
raise Exception("Device discovery timed out")
dev[0].p2p_stop_find()
peer = dev[0].get_peer(addr1)
if peer['config_methods'] != '0x8':
raise Exception("Unexpected peer config methods(2): " + peer['config_methods'])
wpas.p2p_stop_find()
@remote_compatible
def test_discovery_after_gas(dev, apdev):
"""P2P device discovery after GAS/ANQP exchange"""
hapd = start_ap(apdev[0])
hapd.set("gas_frag_limit", "50")
dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
dev[0].request("FETCH_ANQP")
ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
if ev is None:
raise Exception("No ANQP-QUERY-DONE event")
dev[0].dump_monitor()
start = os.times()[4]
dev[0].p2p_listen()
dev[1].p2p_find(social=True)
ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
if ev is None:
raise Exception("Peer not discovered")
end = os.times()[4]
dev[0].dump_monitor()
dev[0].p2p_stop_find()
dev[1].p2p_stop_find()
logger.info("Device discovery after fragmented GAS took %f seconds" % (end - start))
if end - start > 1.3:
raise Exception("Device discovery took unexpectedly long time")
@remote_compatible
def test_discovery_listen_find(dev):
"""P2P_LISTEN immediately followed by P2P_FIND"""
# Request an external remain-on-channel operation to delay start of the ROC
# for the following p2p_listen() enough to get p2p_find() processed before
# the ROC started event shows up. This is done to test a code path where the
# p2p_find() needs to clear the wait for the pending listen operation
# (p2p->pending_listen_freq).
ifindex = int(dev[0].get_driver_status_field("ifindex"))
nl80211_remain_on_channel(dev[0], ifindex, 2417, 200)
addr0 = dev[0].p2p_dev_addr()
dev[0].p2p_listen()
dev[0].p2p_find(social=True)
time.sleep(0.4)
dev[1].p2p_listen()
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=1.2)
if not dev[1].discover_peer(addr0):
raise Exception("Device discovery timed out")
if ev is None:
raise Exception("Did not find peer quickly enough after stopped P2P_LISTEN")
def test_discovery_long_listen(dev):
"""Long P2P_LISTEN and offchannel TX"""
addr0 = dev[0].p2p_dev_addr()
dev[0].p2p_listen()
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
addr = wpas.p2p_dev_addr()
if not wpas.discover_peer(addr0):
raise Exception("Device discovery timed out")
peer = wpas.get_peer(addr0)
chan = '1' if peer['listen_freq'] == '2462' else '11'
wpas.request("P2P_SET listen_channel " + chan)
wpas.request("P2P_LISTEN 10")
if not dev[0].discover_peer(addr):
raise Exception("Device discovery timed out (2)")
time.sleep(0.1)
wpas.global_request("P2P_PROV_DISC " + addr0 + " display")
ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
if ev is None:
raise Exception("Provision discovery timed out")
dev[0].p2p_stop_find()
# Verify that the long listen period is still continuing after off-channel
# TX of Provision Discovery frames.
if not dev[1].discover_peer(addr):
raise Exception("Device discovery timed out (3)")
dev[1].p2p_stop_find()
wpas.p2p_stop_find()
def test_discovery_long_listen2(dev):
"""Long P2P_LISTEN longer than remain-on-channel time"""
with HWSimRadio(use_p2p_device=True) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
addr = wpas.p2p_dev_addr()
wpas.request("P2P_LISTEN 15")
# Wait for remain maximum remain-on-channel time to pass
time.sleep(7)
if not dev[0].discover_peer(addr):
raise Exception("Device discovery timed out")
dev[0].p2p_stop_find()
wpas.p2p_stop_find()
def pd_test(dev, addr):
if not dev.discover_peer(addr, freq=2412):
raise Exception("Device discovery timed out")
dev.global_request("P2P_PROV_DISC " + addr + " display")
ev0 = dev.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
if ev0 is None:
raise Exception("Provision discovery timed out (display)")
dev.p2p_stop_find()
def run_discovery_while_go(wpas, dev, params):
wpas.request("P2P_SET listen_channel 1")
wpas.p2p_start_go(freq="2412")
addr = wpas.p2p_dev_addr()
pin = dev[0].wps_read_pin()
wpas.p2p_go_authorize_client(pin)
dev[1].p2p_connect_group(addr, pin, freq=2412, timeout=30)
pd_test(dev[0], addr)
wpas.p2p_listen()
pd_test(dev[2], addr)
wpas.p2p_stop_find()
terminate_group(wpas, dev[1])
out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
"wifi_p2p.public_action.subtype == 8", ["wlan.da"])
da = out.splitlines()
logger.info("PD Response DAs: " + str(da))
if len(da) != 3:
raise Exception("Unexpected DA count for PD Response")
def test_discovery_while_go(dev, apdev, params):
"""P2P provision discovery from GO"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
run_discovery_while_go(wpas, dev, params)
def test_discovery_while_go_p2p_dev(dev, apdev, params):
"""P2P provision discovery from GO (using P2P Device interface)"""
with HWSimRadio(use_p2p_device=True) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
run_discovery_while_go(wpas, dev, params)
def run_discovery_while_cli(wpas, dev, params):
wpas.request("P2P_SET listen_channel 1")
dev[1].p2p_start_go(freq="2412")
addr = wpas.p2p_dev_addr()
pin = wpas.wps_read_pin()
dev[1].p2p_go_authorize_client(pin)
wpas.p2p_connect_group(dev[1].p2p_dev_addr(), pin, freq=2412, timeout=30)
pd_test(dev[0], addr)
wpas.p2p_listen()
pd_test(dev[2], addr)
wpas.p2p_stop_find()
terminate_group(dev[1], wpas)
out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
"wifi_p2p.public_action.subtype == 8", ["wlan.da"])
da = out.splitlines()
logger.info("PD Response DAs: " + str(da))
if len(da) != 3:
raise Exception("Unexpected DA count for PD Response")
def test_discovery_while_cli(dev, apdev, params):
"""P2P provision discovery from CLI"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
run_discovery_while_cli(wpas, dev, params)
def test_discovery_while_cli_p2p_dev(dev, apdev, params):
"""P2P provision discovery from CLI (using P2P Device interface)"""
with HWSimRadio(use_p2p_device=True) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
run_discovery_while_cli(wpas, dev, params)
def test_discovery_device_name_change(dev):
"""P2P device discovery and peer changing device name"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.set("device_name", "test-a")
wpas.p2p_listen()
dev[0].p2p_find(social=True)
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer not found")
if "new=1" not in ev:
raise Exception("Incorrect new event: " + ev)
if "name='test-a'" not in ev:
raise Exception("Unexpected device name(1): " + ev)
# Verify that new P2P-DEVICE-FOUND event is indicated when the peer changes
# its device name.
wpas.set("device_name", "test-b")
ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
if ev is None:
raise Exception("Peer update not seen")
if "new=0" not in ev:
raise Exception("Incorrect update event: " + ev)
if "name='test-b'" not in ev:
raise Exception("Unexpected device name(2): " + ev)
wpas.p2p_stop_find()
dev[0].p2p_stop_find()
def test_p2p_group_cli_invalid(dev, apdev):
"""P2P device discovery with invalid group client info"""
attr = struct.pack('<BHBB', 2, 2, 0x25, 0x09)
attr += struct.pack('<BH', 3, 6) + "\x02\x02\x02\x02\x02\x00".encode()
cli = bytes()
cli += "\x02\x02\x02\x02\x02\x03".encode()
cli += "\x02\x02\x02\x02\x02\x04".encode()
cli += struct.pack('>BH', 0, 0x3148)
dev_type = "\x00\x00\x00\x00\x00\x00\x00\x01".encode()
cli += dev_type
num_sec = 25
cli += struct.pack('B', num_sec)
cli += num_sec * dev_type
name = "TEST".encode()
cli += struct.pack('>HH', 0x1011, len(name)) + name
desc = struct.pack('B', len(cli)) + cli
attr += struct.pack('<BH', 14, len(desc)) + desc
p2p_ie = struct.pack('>BBL', 0xdd, 4 + len(attr), 0x506f9a09) + attr
ie = binascii.hexlify(p2p_ie).decode()
params = {"ssid": "DIRECT-test",
"eap_server": "1",
"wps_state": "2",
"wpa_passphrase": "12345678",
"wpa": "2",
"wpa_key_mgmt": "WPA-PSK",
"rsn_pairwise": "CCMP",
"vendor_elements": ie}
hapd = hostapd.add_ap(apdev[0], params)
for i in range(2):
dev[i].p2p_find(social=True)
ev = dev[i].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
if not ev:
raise Exception("P2P device not found")
+
+def test_discovery_max_peers(dev):
+ """P2P device discovery and maximum peer limit exceeded"""
+ dev[0].p2p_listen()
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ probereq1 = "40000000ffffffffffff"
+ probereq2 = "ffffffffffff000000074449524543542d01080c1218243048606c0301012d1afe131bffff000000000000000000000100000000000000000000ff16230178c812400000bfce0000000000000000fafffaffdd730050f204104a000110103a00010110080002314810470010572cf82fc95756539b16b5cfb298abf1105400080000000000000000103c0001031002000200001009000200001012000200001021000120102300012010240001201011000844657669636520421049000900372a000120030101dd11506f9a0902020025000605005858045106"
+
+ # Fill the P2P peer table with max+1 entries based on Probe Request frames
+ # to verify correct behavior on# removing the oldest entry when running out
+ # of room.
+ for i in range(101):
+ addr = "0202020202%02x" % i
+ probereq = probereq1 + addr + probereq2
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=60 ssi_signal=-30 frame=" + probereq):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ res = dev[0].global_request("P2P_PEER FIRST")
+ addr = res.splitlines()[0]
+ peers = [addr]
+ limit = 200
+ while limit > 0:
+ res = dev[0].global_request("P2P_PEER NEXT-" + addr)
+ addr = res.splitlines()[0]
+ if addr == "FAIL":
+ break
+ peers.append(addr)
+ limit -= 1
+ logger.info("Peers: " + str(peers))
+
+ if len(peers) != 100:
+ raise Exception("Unexpected number of peer entries")
+ oldest = "02:02:02:02:02:00"
+ if oldest in peers:
+ raise Exception("Oldest entry is still present")
+ for i in range(101):
+ addr = "02:02:02:02:02:%02x" % i
+ if addr == oldest:
+ continue
+ if addr not in peers:
+ raise Exception("Peer missing from table: " + addr)
+
+ # Provision Discovery Request from the oldest peer (SA) using internally
+ # different P2P Device Address as a regression test for incorrect processing
+ # for this corner case.
+ dst = dev[0].own_addr().replace(':', '')
+ src = peers[99].replace(':', '')
+ devaddr = "0202020202ff"
+ pdreq = "d0004000" + dst + src + dst + "d0000409506f9a090701dd29506f9a0902020025000d1d00" + devaddr + "1108000000000000000000101100084465766963652041dd0a0050f204100800020008"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=60 ssi_signal=-30 frame=" + pdreq):
+ raise Exception("MGMT_RX_PROCESS failed")
diff --git a/tests/hwsim/test_pasn.py b/tests/hwsim/test_pasn.py
index f2c7b3b221b6..c8bcd63f6ac7 100644
--- a/tests/hwsim/test_pasn.py
+++ b/tests/hwsim/test_pasn.py
@@ -1,683 +1,850 @@
# Test cases for PASN
# Copyright (C) 2019 Intel Corporation
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import binascii
import os
import time
import logging
logger = logging.getLogger()
import socket
import struct
import subprocess
+import re
import hwsim_utils
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
from hwsim import HWSimRadio
from test_erp import start_erp_as
from test_ap_ft import run_roams, ft_params1, ft_params2
def check_pasn_capab(dev):
if "PASN" not in dev.get_capability("auth_alg"):
raise HwsimSkip("PASN not supported")
def pasn_ap_params(akmp="PASN", cipher="CCMP", group="19"):
params = {"ssid": "test-wpa2-pasn",
"wpa_passphrase": "12345678",
"wpa": "2",
"ieee80211w": "2",
"wpa_key_mgmt": "WPA-PSK " + akmp,
"rsn_pairwise": cipher,
"pasn_groups" : group}
return params
def start_pasn_ap(apdev, params):
try:
return hostapd.add_ap(apdev, params)
except Exception as e:
if "Failed to set hostapd parameter wpa_key_mgmt" in str(e) or \
"Failed to set hostapd parameter force_kdk_derivation" in str(e):
raise HwsimSkip("PASN not supported")
raise
def check_pasn_ptk(dev, hapd, cipher, fail_ptk=False, clear_keys=True):
sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher)
ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher)
if not (sta_ptksa and ap_ptksa):
if fail_ptk:
return
raise Exception("Could not get PTKSA entry")
logger.info("sta: TK: %s KDK: %s" % (sta_ptksa['tk'], sta_ptksa['kdk']))
logger.info("ap : TK: %s KDK: %s" % (ap_ptksa['tk'], ap_ptksa['kdk']))
if sta_ptksa['tk'] != ap_ptksa['tk'] or sta_ptksa['kdk'] != ap_ptksa['kdk']:
raise Exception("TK/KDK mismatch")
elif fail_ptk:
raise Exception("TK/KDK match although key derivation should have failed")
elif clear_keys:
cmd = "PASN_DEAUTH bssid=%s" % hapd.own_addr()
dev.request(cmd)
# Wait a little to let the AP process the deauth
time.sleep(0.2)
sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher)
ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher)
if sta_ptksa or ap_ptksa:
raise Exception("TK/KDK not deleted as expected")
def check_pasn_akmp_cipher(dev, hapd, akmp="PASN", cipher="CCMP",
group="19", status=0, fail=0, nid="",
fail_ptk=False):
dev.flush_scan_cache()
dev.scan(type="ONLY", freq=2412)
cmd = "PASN_START bssid=%s akmp=%s cipher=%s group=%s" % (hapd.own_addr(), akmp, cipher, group)
if nid != "":
cmd += " nid=%s" % nid
resp = dev.request(cmd)
if fail:
if "OK" in resp:
raise Exception("Unexpected success to start PASN authentication")
return
if "OK" not in resp:
raise Exception("Failed to start PASN authentication")
ev = dev.wait_event(["PASN-AUTH-STATUS"], 3)
if not ev:
raise Exception("PASN: PASN-AUTH-STATUS not seen")
if hapd.own_addr() + " akmp=" + akmp + ", status=" + str(status) not in ev:
raise Exception("PASN: unexpected status")
if status:
return
check_pasn_ptk(dev, hapd, cipher, fail_ptk)
@remote_compatible
def test_pasn_ccmp(dev, apdev):
"""PASN authentication with WPA2/CCMP AP"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "CCMP", "19")
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
@remote_compatible
def test_pasn_gcmp(dev, apdev):
"""PASN authentication with WPA2/GCMP AP"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "GCMP", "19")
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP")
@remote_compatible
def test_pasn_ccmp_256(dev, apdev):
"""PASN authentication with WPA2/CCMP256 AP"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "CCMP-256", "19")
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP-256")
@remote_compatible
def test_pasn_gcmp_256(dev, apdev):
"""PASN authentication with WPA2/GCMP-256 AP"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "GCMP-256", "19")
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP-256")
@remote_compatible
def test_pasn_group_mismatch(dev, apdev):
"""PASN authentication with WPA2/CCMP AP with group mismatch"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "CCMP", "20")
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", status=77)
@remote_compatible
def test_pasn_channel_mismatch(dev, apdev):
"""PASN authentication with WPA2/CCMP AP with channel mismatch"""
check_pasn_capab(dev[0])
params = pasn_ap_params("PASN", "CCMP")
params['channel'] = "6"
hapd = start_pasn_ap(apdev[0], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1)
@remote_compatible
def test_pasn_while_connected_same_channel(dev, apdev):
"""PASN authentication with WPA2/CCMP AP while connected same channel"""
check_pasn_capab(dev[0])
ssid = "test-wpa2-psk"
psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
params = hostapd.wpa2_params(ssid=ssid)
params['wpa_psk'] = psk
hapd = start_pasn_ap(apdev[0], params)
dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
params = pasn_ap_params("PASN", "CCMP")
hapd = start_pasn_ap(apdev[1], params)
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
@remote_compatible
def test_pasn_while_connected_same_ap(dev, apdev):
"""PASN authentication with WPA2/CCMP AP while connected to it"""
check_pasn_capab(dev[0])
params = hostapd.wpa2_params(ssid="test-wpa2-psk",
passphrase="12345678")
hapd = start_pasn_ap(apdev[0], params)
dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1)
@remote_compatible
def test_pasn_while_connected_diff_channel(dev, apdev):
"""PASN authentication with WPA2/CCMP AP while connected diff channel"""
check_pasn_capab(dev[0])
with HWSimRadio(n_channels=2) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
if wpas.get_mcc() < 2:
raise HwsimSkip("PASN: New radio does not support MCC")
params = hostapd.wpa2_params(ssid="test-wpa2-psk",
passphrase="12345678")
params['channel'] = "6"
hapd = start_pasn_ap(apdev[0], params)
wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2437")
params = pasn_ap_params("PASN", "CCMP")
hapd2 = start_pasn_ap(apdev[1], params)
check_pasn_akmp_cipher(wpas, hapd2, "PASN", "CCMP")
@remote_compatible
def test_pasn_sae_pmksa_cache(dev, apdev):
"""PASN authentication with SAE AP with PMKSA caching"""
check_pasn_capab(dev[0])
check_sae_capab(dev[0])
params = hostapd.wpa2_params(ssid="test-sae",
passphrase="12345678")
params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
hapd = start_pasn_ap(apdev[0], params)
- dev[0].set("sae_groups", "19")
- dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412")
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412")
- hapd.wait_sta()
- hwsim_utils.test_connectivity(dev[0], hapd)
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
- dev[0].request("DISCONNECT")
- dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
- check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+ finally:
+ dev[0].set("sae_pwe", "0")
def check_pasn_fils_pmksa_cache(dev, apdev, params, key_mgmt):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
check_pasn_capab(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = key_mgmt + " PASN"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
hapd = start_pasn_ap(apdev[0], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
id = dev[0].connect("fils", key_mgmt=key_mgmt,
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP")
@remote_compatible
def test_pasn_fils_sha256_pmksa_cache(dev, apdev, params):
"""PASN authentication with FILS-SHA256 with PMKSA caching"""
check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA256")
@remote_compatible
def test_pasn_fils_sha384_pmksa_cache(dev, apdev, params):
"""PASN authentication with FILS-SHA384 with PMKSA caching"""
check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA384")
@remote_compatible
def test_pasn_sae_kdk(dev, apdev):
"""Station authentication with SAE AP with KDK derivation during connection"""
check_pasn_capab(dev[0])
check_sae_capab(dev[0])
try:
params = hostapd.wpa2_params(ssid="test-sae",
passphrase="12345678")
params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
params['force_kdk_derivation'] = "1"
hapd = start_pasn_ap(apdev[0], params)
dev[0].set("force_kdk_derivation", "1")
+ dev[0].set("sae_pwe", "2")
dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
scan_freq="2412")
check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
finally:
dev[0].set("force_kdk_derivation", "0")
+ dev[0].set("sae_pwe", "0")
def check_pasn_fils_kdk(dev, apdev, params, key_mgmt):
check_fils_capa(dev[0])
check_erp_capa(dev[0])
check_pasn_capab(dev[0])
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
try:
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = key_mgmt
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
params['force_kdk_derivation'] = "1"
hapd = start_pasn_ap(apdev[0], params)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].request("ERP_FLUSH")
dev[0].set("force_kdk_derivation", "1")
id = dev[0].connect("fils", key_mgmt=key_mgmt,
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].select_network(id, freq=2412)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"EVENT-ASSOC-REJECT",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Connection using FILS/ERP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
if "EVENT-ASSOC-REJECT" in ev:
raise Exception("Association failed")
hapd.wait_sta()
hwsim_utils.test_connectivity(dev[0], hapd)
check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
finally:
dev[0].set("force_kdk_derivation", "0")
@remote_compatible
def test_pasn_fils_sha256_kdk(dev, apdev, params):
"""Station authentication with FILS-SHA256 with KDK derivation during connection"""
check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA256")
@remote_compatible
def test_pasn_fils_sha384_kdk(dev, apdev, params):
"""Station authentication with FILS-SHA384 with KDK derivation during connection"""
check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA384")
@remote_compatible
def test_pasn_sae(dev, apdev):
"""PASN authentication with SAE AP with PMK derivation + PMKSA caching"""
check_pasn_capab(dev[0])
check_sae_capab(dev[0])
params = hostapd.wpa2_params(ssid="test-pasn-sae",
passphrase="12345678")
params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
hapd = start_pasn_ap(apdev[0], params)
- dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412",
- only_add_network=True)
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
- # first test with a valid PSK
- check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0")
+ # first test with a valid PSK
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0")
- # And now with PMKSA caching
- check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+ # And now with PMKSA caching
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
- # And now with a wrong passphrase
- if "FAIL" in dev[0].request("PMKSA_FLUSH"):
- raise Exception("PMKSA_FLUSH failed")
+ # And now with a wrong passphrase
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
- dev[0].set_network_quoted(0, "psk", "12345678787")
- check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0")
+ dev[0].set_network_quoted(0, "psk", "12345678787")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0")
+ finally:
+ dev[0].set("sae_pwe", "0")
@remote_compatible
def test_pasn_sae_while_connected_same_channel(dev, apdev):
"""PASN SAE authentication while connected same channel"""
check_pasn_capab(dev[0])
check_sae_capab(dev[0])
params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk",
passphrase="12345678")
hapd = hostapd.add_ap(apdev[0], params)
- dev[0].connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2412")
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2412")
- params = hostapd.wpa2_params(ssid="test-pasn-sae",
- passphrase="12345678")
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
- params['wpa_key_mgmt'] = 'SAE PASN'
- hapd = start_pasn_ap(apdev[1], params)
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[1], params)
- dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
- scan_freq="2412", only_add_network=True)
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
- check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="1")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="1")
+ finally:
+ dev[0].set("sae_pwe", "0")
@remote_compatible
def test_pasn_sae_while_connected_diff_channel(dev, apdev):
"""PASN SAE authentication while connected diff channel"""
check_pasn_capab(dev[0])
check_sae_capab(dev[0])
with HWSimRadio(n_channels=2) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
if wpas.get_mcc() < 2:
raise HwsimSkip("PASN: New radio does not support MCC")
params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk",
passphrase="12345678")
params['channel'] = "6"
hapd = hostapd.add_ap(apdev[0], params)
- wpas.connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2437")
+ try:
+ wpas.set("sae_pwe", "2")
+ wpas.connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2437")
- params = hostapd.wpa2_params(ssid="test-pasn-sae",
- passphrase="12345678")
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
- params['wpa_key_mgmt'] = 'SAE PASN'
- hapd = start_pasn_ap(apdev[1], params)
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[1], params)
- wpas.connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
- scan_freq="2412", only_add_network=True)
+ wpas.connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
- check_pasn_akmp_cipher(wpas, hapd, "SAE", "CCMP", nid="1")
+ check_pasn_akmp_cipher(wpas, hapd, "SAE", "CCMP", nid="1")
+ finally:
+ wpas.set("sae_pwe", "0")
def pasn_fils_setup(wpas, apdev, params, key_mgmt):
check_fils_capa(wpas)
check_erp_capa(wpas)
wpas.flush_scan_cache()
start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
bssid = apdev[0]['bssid']
params = hostapd.wpa2_eap_params(ssid="fils")
params['wpa_key_mgmt'] = key_mgmt + " PASN"
params['auth_server_port'] = "18128"
params['erp_domain'] = 'example.com'
params['fils_realm'] = 'example.com'
params['disable_pmksa_caching'] = '1'
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
id = wpas.connect("fils", key_mgmt=key_mgmt,
eap="PSK", identity="psk.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
erp="1", scan_freq="2412")
wpas.request("DISCONNECT")
wpas.wait_disconnected()
wpas.dump_monitor()
if "FAIL" in wpas.request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
return hapd
def check_pasn_fils(dev, apdev, params, key_mgmt):
check_pasn_capab(dev[0])
hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt);
check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0")
@remote_compatible
def test_pasn_fils_sha256(dev, apdev, params):
"""PASN FILS authentication using SHA-256"""
check_pasn_fils(dev, apdev, params, "FILS-SHA256")
@remote_compatible
def test_pasn_fils_sha384(dev, apdev, params):
"""PASN FILS authentication using SHA-384"""
check_pasn_fils(dev, apdev, params, "FILS-SHA384")
def check_pasn_fils_connected_same_channel(dev, apdev, params, key_mgmt):
check_pasn_capab(dev[0])
hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt);
# Connect to another AP on the same channel
hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
bg_scan_period="0")
hwsim_utils.test_connectivity(dev[0], hapd1)
# And perform the PASN authentication with FILS
check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0")
@remote_compatible
def test_pasn_fils_sha256_connected_same_channel(dev, apdev, params):
"""PASN FILS authentication using SHA-256 while connected same channel"""
check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA256")
@remote_compatible
def test_pasn_fils_sha384_connected_same_channel(dev, apdev, params):
"""PASN FILS authentication using SHA-384 while connected same channel"""
check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA384")
def check_pasn_fils_connected_diff_channel(dev, apdev, params, key_mgmt):
check_pasn_capab(dev[0])
with HWSimRadio(n_channels=2) as (radio, iface):
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add(iface)
if wpas.get_mcc() < 2:
raise Exception("New radio does not support MCC")
hapd = pasn_fils_setup(wpas, apdev, params, key_mgmt);
# Connect to another AP on a different channel
hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open", "channel" : "6"})
wpas.connect("open", key_mgmt="NONE", scan_freq="2437",
bg_scan_period="0")
hwsim_utils.test_connectivity(wpas, hapd1)
# And perform the PASN authentication with FILS
check_pasn_akmp_cipher(wpas, hapd, key_mgmt, "CCMP", nid="0")
@remote_compatible
def test_pasn_fils_sha256_connected_diff_channel(dev, apdev, params):
"""PASN FILS authentication using SHA-256 while connected diff channel"""
check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA256")
@remote_compatible
def test_pasn_fils_sha384_connected_diff_channel(dev, apdev, params):
"""PASN FILS authentication using SHA-384 while connected diff channel"""
check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA384")
def test_pasn_ft_psk(dev, apdev):
"""PASN authentication with FT-PSK"""
check_pasn_capab(dev[0])
ssid = "test-pasn-ft-psk"
passphrase = "12345678"
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] += " PASN"
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] += " PASN"
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
pasn_hapd = hapd1
else:
pasn_hapd = hapd0
check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP")
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, only_one_way=1)
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
pasn_hapd = hapd1
else:
pasn_hapd = hapd0
check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP")
def test_pasn_ft_eap(dev, apdev):
"""PASN authentication with FT-EAP"""
check_pasn_capab(dev[0])
ssid = "test-pasn-ft-psk"
passphrase = "12345678"
identity = "gpsk user"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-EAP PASN"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params['wpa_key_mgmt'] = "FT-EAP PASN"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
eap_identity=identity)
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
pasn_hapd = hapd1
else:
pasn_hapd = hapd0
check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP", "CCMP")
def test_pasn_ft_eap_sha384(dev, apdev):
"""PASN authentication with FT-EAP-SHA-384"""
check_pasn_capab(dev[0])
ssid = "test-pasn-ft-psk"
passphrase = "12345678"
identity = "gpsk user"
radius = hostapd.radius_params()
params = ft_params1(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd0 = hostapd.add_ap(apdev[0], params)
params = ft_params2(ssid=ssid, passphrase=passphrase)
params["ieee80211w"] = "2"
params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN"
params["ieee8021x"] = "1"
params = dict(list(radius.items()) + list(params.items()))
hapd1 = hostapd.add_ap(apdev[1], params)
run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
sha384=True)
if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
pasn_hapd = hapd1
else:
pasn_hapd = hapd0
check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP-SHA384", "CCMP")
def test_pasn_sta_mic_error(dev, apdev):
"""PASN authentication with WPA2/CCMP AP with corrupted MIC on station"""
params = pasn_ap_params("PASN", "CCMP", "19")
hapd = hostapd.add_ap(apdev[0], params)
try:
# When forcing MIC corruption, the exchange would be still successful
# on the station side, but the AP would fail the exchange and would not
# store the keys.
dev[0].set("pasn_corrupt_mic", "1")
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail_ptk=True)
finally:
dev[0].set("pasn_corrupt_mic", "0")
# Now verify the successful case
check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
def test_pasn_ap_mic_error(dev, apdev):
"""PASN authentication with WPA2/CCMP AP with corrupted MIC on AP"""
params = pasn_ap_params("PASN", "CCMP", "19")
hapd0 = hostapd.add_ap(apdev[0], params)
params['pasn_corrupt_mic'] = "1"
hapd1 = hostapd.add_ap(apdev[1], params)
check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP", status=1)
check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_comeback(dev, apdev, params):
+ """PASN authentication with comeback flow"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ params['sae_anti_clogging_threshold'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan(type="ONLY", freq=2412)
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19" % bssid
+
+ resp = dev[0].request(cmd)
+ if "OK" not in resp:
+ raise Exception("Failed to start PASN authentication")
+
+ ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=30 comeback_after=" not in ev:
+ raise Exception("PASN: unexpected status")
+
+ comeback = re.split("comeback=", ev)[1]
+
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19 comeback=%s" % \
+ (bssid, comeback)
+
+ resp = dev[0].request(cmd)
+ if "OK" not in resp:
+ raise Exception("Failed to start PASN authentication")
+
+ ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=0" not in ev:
+ raise Exception("PASN: unexpected status with comeback token")
+
+ check_pasn_ptk(dev[0], hapd, "CCMP")
+
+@remote_compatible
+def test_pasn_comeback_after_0(dev, apdev, params):
+ """PASN authentication with comeback flow with comeback after set to 0"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ params['anti_clogging_threshold'] = '0'
+ params['pasn_comeback_after'] = '0'
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_comeback_after_0_sae(dev, apdev):
+ """PASN authentication with SAE, with comeback flow where comeback after is set to 0"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['anti_clogging_threshold'] = '0'
+ params['pasn_comeback_after'] = '0'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ # first test with a valid PSK
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0")
+
+ # And now with PMKSA caching
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+
+ # And now with a wrong passphrase
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ dev[0].set_network_quoted(0, "psk", "12345678787")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+@remote_compatible
+def test_pasn_comeback_multi(dev, apdev):
+ """PASN authentication with SAE, with multiple stations with comeback"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['anti_clogging_threshold'] = '1'
+ params['pasn_comeback_after'] = '0'
+ hapd = start_pasn_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ id = {}
+ for i in range(0, 2):
+ dev[i].flush_scan_cache()
+ dev[i].scan(type="ONLY", freq=2412)
+ id[i] = dev[i].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ for i in range(0, 2):
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19, nid=%s" % (bssid, id[i])
+ resp = dev[i].request(cmd)
+
+ if "OK" not in resp:
+ raise Exception("Failed to start pasn authentication")
+
+ for i in range(0, 2):
+ ev = dev[i].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=0" not in ev:
+ raise Exception("PASN: unexpected status")
+
+ check_pasn_ptk(dev[i], hapd, "CCMP")
+
+def test_pasn_kdk_derivation(dev, apdev):
+ """PASN authentication with forced KDK derivation"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ hapd0 = start_pasn_ap(apdev[0], params)
+
+ params['force_kdk_derivation'] = "1"
+ hapd1 = start_pasn_ap(apdev[1], params)
+
+ try:
+ check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP")
+ dev[0].set("force_kdk_derivation", "1")
+ check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP")
+ finally:
+ dev[0].set("force_kdk_derivation", "0")
diff --git a/tests/hwsim/test_pmksa_cache.py b/tests/hwsim/test_pmksa_cache.py
index 526f7f455685..10d76a394f8d 100644
--- a/tests/hwsim/test_pmksa_cache.py
+++ b/tests/hwsim/test_pmksa_cache.py
@@ -1,1253 +1,1267 @@
# WPA2-Enterprise PMKSA caching tests
# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import binascii
import logging
logger = logging.getLogger()
import socket
import struct
import subprocess
import time
import hostapd
import hwsim_utils
from wpasupplicant import WpaSupplicant
from utils import alloc_fail, HwsimSkip, wait_fail_trigger
from test_ap_eap import eap_connect
def test_pmksa_cache_on_roam_back(dev, apdev):
"""PMKSA cache to skip EAP on reassociation back to same AP"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
if pmksa['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
# It can take some time for the second AP to become ready to reply to Probe
# Request frames especially under heavy CPU load, so allow couple of rounds
# of scanning to avoid reporting errors incorrectly just because of scans
# not having seen the target AP.
for i in range(0, 10):
dev[0].scan(freq="2412")
if dev[0].get_bss(bssid2) is not None:
break
logger.info("Scan again to find target AP")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10, error="Roaming timed out")
pmksa2 = dev[0].get_pmksa(bssid2)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa2['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa1b = dev[0].get_pmksa(bssid)
if pmksa1b is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa1b['pmkid']:
raise Exception("Unexpected PMKID change for AP1")
dev[0].dump_monitor()
if "FAIL" in dev[0].request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
if dev[0].get_pmksa(bssid) is not None or dev[0].get_pmksa(bssid2) is not None:
raise Exception("PMKSA_FLUSH did not remove PMKSA entries")
dev[0].wait_disconnected(timeout=5)
dev[0].wait_connected(timeout=15, error="Reconnection timed out")
def test_pmksa_cache_and_reauth(dev, apdev):
"""PMKSA caching and EAPOL reauthentication"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
# It can take some time for the second AP to become ready to reply to Probe
# Request frames especially under heavy CPU load, so allow couple of rounds
# of scanning to avoid reporting errors incorrectly just because of scans
# not having seen the target AP.
for i in range(0, 10):
dev[0].scan(freq="2412")
if dev[0].get_bss(bssid2) is not None:
break
logger.info("Scan again to find target AP")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10, error="Roaming timed out")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
# Verify EAPOL reauthentication after PMKSA caching
hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not start")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
if ev is None:
raise Exception("EAP authentication did not succeed")
def test_pmksa_cache_and_ptk_rekey_ap(dev, apdev):
"""PMKSA caching and PTK rekey triggered by AP"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['wpa_ptk_rekey'] = '2'
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
# It can take some time for the second AP to become ready to reply to Probe
# Request frames especially under heavy CPU load, so allow couple of rounds
# of scanning to avoid reporting errors incorrectly just because of scans
# not having seen the target AP.
for i in range(0, 10):
dev[0].scan(freq="2412")
if dev[0].get_bss(bssid2) is not None:
break
logger.info("Scan again to find target AP")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10, error="Roaming timed out")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
# Verify PTK rekeying after PMKSA caching
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
if ev is None:
raise Exception("PTK rekey timed out")
def test_pmksa_cache_opportunistic_only_on_sta(dev, apdev):
"""Opportunistic PMKSA caching enabled only on station"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef", okc=True,
scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
if pmksa['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10, error="Roaming timed out")
pmksa2 = dev[0].get_pmksa(bssid2)
if pmksa2 is None:
raise Exception("No PMKSA cache entry found")
if pmksa2['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa1b = dev[0].get_pmksa(bssid)
if pmksa1b is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa1b['pmkid']:
raise Exception("Unexpected PMKID change for AP1")
def test_pmksa_cache_opportunistic(dev, apdev):
"""Opportunistic PMKSA caching"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['okc'] = "1"
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef", okc=True,
scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
if pmksa['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa2 = dev[0].get_pmksa(bssid2)
if pmksa2 is None:
raise Exception("No PMKSA cache entry created")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa1b = dev[0].get_pmksa(bssid)
if pmksa1b is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa1b['pmkid']:
raise Exception("Unexpected PMKID change for AP1")
def test_pmksa_cache_opportunistic_connect(dev, apdev):
"""Opportunistic PMKSA caching with connect API"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['okc'] = "1"
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef", okc=True,
scan_freq="2412")
pmksa = wpas.get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
if pmksa['opportunistic'] != '0':
raise Exception("Unexpected opportunistic PMKSA cache entry")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
wpas.dump_monitor()
logger.info("Roam to AP2")
wpas.scan_for_bss(bssid2, freq="2412", force_scan=True)
wpas.request("ROAM " + bssid2)
ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa2 = wpas.get_pmksa(bssid2)
if pmksa2 is None:
raise Exception("No PMKSA cache entry created")
wpas.dump_monitor()
logger.info("Roam back to AP1")
wpas.scan(freq="2412")
wpas.request("ROAM " + bssid)
ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa1b = wpas.get_pmksa(bssid)
if pmksa1b is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa1b['pmkid']:
raise Exception("Unexpected PMKID change for AP1")
def test_pmksa_cache_expiration(dev, apdev):
"""PMKSA cache entry expiration"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].request("SET dot11RSNAConfigPMKLifetime 10")
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
hapd.wait_sta()
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
logger.info("Wait for PMKSA cache entry to expire")
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"], timeout=15)
if ev is None:
raise Exception("No EAP reauthentication seen")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Unexpected disconnection")
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa['pmkid'] == pmksa2['pmkid']:
raise Exception("PMKID did not change")
hapd.wait_ptkinitdone(dev[0].own_addr())
hwsim_utils.test_connectivity(dev[0], hapd)
def test_pmksa_cache_expiration_disconnect(dev, apdev):
"""PMKSA cache entry expiration (disconnect)"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].request("SET dot11RSNAConfigPMKLifetime 2")
dev[0].request("SET dot11RSNAConfigPMKReauthThreshold 100")
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
hapd.request("SET auth_server_shared_secret incorrect")
logger.info("Wait for PMKSA cache entry to expire")
ev = dev[0].wait_event(["WPA: Key negotiation completed",
"CTRL-EVENT-DISCONNECTED"], timeout=15)
if ev is None:
raise Exception("No EAP reauthentication seen")
if "CTRL-EVENT-DISCONNECTED" not in ev:
raise Exception("Missing disconnection")
hapd.request("SET auth_server_shared_secret radius")
ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=15)
if ev is None:
raise Exception("No EAP reauthentication seen")
pmksa2 = dev[0].get_pmksa(bssid)
if pmksa['pmkid'] == pmksa2['pmkid']:
raise Exception("PMKID did not change")
def test_pmksa_cache_and_cui(dev, apdev):
"""PMKSA cache and Chargeable-User-Identity"""
params = hostapd.wpa2_eap_params(ssid="cui")
params['radius_request_cui'] = '1'
params['acct_server_addr'] = "127.0.0.1"
params['acct_server_port'] = "1813"
params['acct_server_shared_secret'] = "radius"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("cui", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk-cui",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
pmksa = dev[0].get_pmksa(bssid)
if pmksa is None:
raise Exception("No PMKSA cache entry created")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
dev[0].dump_monitor()
logger.info("Disconnect and reconnect to the same AP")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Reconnect timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa1b = dev[0].get_pmksa(bssid)
if pmksa1b is None:
raise Exception("No PMKSA cache entry found")
if pmksa['pmkid'] != pmksa1b['pmkid']:
raise Exception("Unexpected PMKID change for AP1")
dev[0].request("REAUTHENTICATE")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
for i in range(0, 20):
state = dev[0].get_status_field("wpa_state")
if state == "COMPLETED":
break
time.sleep(0.1)
if state != "COMPLETED":
raise Exception("Reauthentication did not complete")
def test_pmksa_cache_preauth_auto(dev, apdev):
"""RSN pre-authentication based on pre-connection scan results"""
try:
run_pmksa_cache_preauth_auto(dev, apdev)
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev',
'ap-br0', 'down', '2>', '/dev/null'],
shell=True)
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0',
'2>', '/dev/null'], shell=True)
def run_pmksa_cache_preauth_auto(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['bridge'] = 'ap-br0'
params['rsn_preauth'] = '1'
params['rsn_preauth_interfaces'] = 'ap-br0'
hapd = hostapd.add_ap(apdev[0], params)
hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
hapd2 = hostapd.add_ap(apdev[1], params)
eap_connect(dev[0], None, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
found = False
for i in range(20):
time.sleep(0.5)
res1 = dev[0].get_pmksa(apdev[0]['bssid'])
res2 = dev[0].get_pmksa(apdev[1]['bssid'])
if res1 and res2:
found = True
break
if not found:
raise Exception("The expected PMKSA cache entries not found")
def generic_pmksa_cache_preauth(dev, apdev, extraparams, identity, databridge,
force_disconnect=False):
if not extraparams:
extraparams = [{}, {}]
try:
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['bridge'] = 'ap-br0'
for key, value in extraparams[0].items():
params[key] = value
hapd = hostapd.add_ap(apdev[0], params)
hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
eap_connect(dev[0], hapd, "PAX", identity,
password_hex="0123456789abcdef0123456789abcdef")
# Verify connectivity in the correct VLAN
hwsim_utils.test_connectivity_iface(dev[0], hapd, databridge)
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['bridge'] = 'ap-br0'
params['rsn_preauth'] = '1'
params['rsn_preauth_interfaces'] = databridge
for key, value in extraparams[1].items():
params[key] = value
hapd1 = hostapd.add_ap(apdev[1], params)
bssid1 = apdev[1]['bssid']
dev[0].scan(freq="2412")
success = False
status_seen = False
for i in range(0, 50):
if not status_seen:
status = dev[0].request("STATUS")
if "Pre-authentication EAPOL state machines:" in status:
status_seen = True
time.sleep(0.1)
pmksa = dev[0].get_pmksa(bssid1)
if pmksa:
success = True
break
if not success:
raise Exception("No PMKSA cache entry created from pre-authentication")
if not status_seen:
raise Exception("Pre-authentication EAPOL status was not available")
dev[0].scan(freq="2412")
if "[WPA2-EAP-CCMP-preauth]" not in dev[0].request("SCAN_RESULTS"):
raise Exception("Scan results missing RSN element info")
dev[0].request("ROAM " + bssid1)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa2 = dev[0].get_pmksa(bssid1)
if pmksa2 is None:
raise Exception("No PMKSA cache entry")
if pmksa['pmkid'] != pmksa2['pmkid']:
raise Exception("Unexpected PMKID change")
hapd1.wait_sta()
# Verify connectivity in the correct VLAN
hwsim_utils.test_connectivity_iface(dev[0], hapd, databridge)
if not force_disconnect:
return
# Disconnect the STA from both APs to avoid forceful ifdown by the
# test script on a VLAN that this has an associated STA. That used to
# trigger a mac80211 warning.
dev[0].request("DISCONNECT")
hapd.request("DISABLE")
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev',
'ap-br0', 'down', '2>', '/dev/null'],
shell=True)
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0',
'2>', '/dev/null'], shell=True)
def test_pmksa_cache_preauth(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry"""
generic_pmksa_cache_preauth(dev, apdev, None,
"pax.user@example.com", "ap-br0")
def test_pmksa_cache_preauth_per_sta_vif(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry with per_sta_vif"""
extraparams = [{}, {}]
extraparams[0]['per_sta_vif'] = "1"
extraparams[1]['per_sta_vif'] = "1"
generic_pmksa_cache_preauth(dev, apdev, extraparams,
"pax.user@example.com", "ap-br0")
def test_pmksa_cache_preauth_vlan_enabled(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry (dynamic_vlan optional but station without VLAN set)"""
extraparams = [{}, {}]
extraparams[0]['dynamic_vlan'] = '1'
extraparams[1]['dynamic_vlan'] = '1'
generic_pmksa_cache_preauth(dev, apdev, extraparams,
"pax.user@example.com", "ap-br0")
def test_pmksa_cache_preauth_vlan_enabled_per_sta_vif(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry (dynamic_vlan optional but station without VLAN set, with per_sta_vif enabled)"""
extraparams = [{}, {}]
extraparams[0]['per_sta_vif'] = "1"
extraparams[1]['per_sta_vif'] = "1"
extraparams[0]['dynamic_vlan'] = '1'
extraparams[1]['dynamic_vlan'] = '1'
generic_pmksa_cache_preauth(dev, apdev, extraparams,
"pax.user@example.com", "ap-br0")
def test_pmksa_cache_preauth_vlan_used(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry (station with VLAN set)"""
run_pmksa_cache_preauth_vlan_used(dev, apdev, None, force_disconnect=True)
def run_pmksa_cache_preauth_vlan_used(dev, apdev, extraparams=None,
force_disconnect=False):
try:
subprocess.call(['brctl', 'addbr', 'brvlan1'])
subprocess.call(['brctl', 'setfd', 'brvlan1', '0'])
if not extraparams:
extraparams = [{}, {}]
extraparams[0]['dynamic_vlan'] = '1'
extraparams[0]['vlan_file'] = 'hostapd.wlan3.vlan'
extraparams[1]['dynamic_vlan'] = '1'
extraparams[1]['vlan_file'] = 'hostapd.wlan4.vlan'
generic_pmksa_cache_preauth(dev, apdev, extraparams,
"vlan1", "brvlan1",
force_disconnect=force_disconnect)
finally:
subprocess.call(['ip', 'link', 'set', 'dev', 'brvlan1', 'down'])
subprocess.call(['ip', 'link', 'set', 'dev', 'wlan3.1', 'down'],
stderr=open('/dev/null', 'w'))
subprocess.call(['ip', 'link', 'set', 'dev', 'wlan4.1', 'down'],
stderr=open('/dev/null', 'w'))
subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan3.1'],
stderr=open('/dev/null', 'w'))
subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan4.1'],
stderr=open('/dev/null', 'w'))
subprocess.call(['brctl', 'delbr', 'brvlan1'])
def test_pmksa_cache_preauth_vlan_used_per_sta_vif(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry (station with VLAN set, per_sta_vif=1)"""
extraparams = [{}, {}]
extraparams[0]['per_sta_vif'] = "1"
extraparams[1]['per_sta_vif'] = "1"
run_pmksa_cache_preauth_vlan_used(dev, apdev, extraparams)
def test_pmksa_cache_disabled(dev, apdev):
"""PMKSA cache disabling on AP"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['disable_pmksa_caching'] = '1'
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].dump_monitor()
logger.info("Roam to AP2")
dev[0].scan_for_bss(bssid2, freq="2412")
dev[0].request("ROAM " + bssid2)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
dev[0].wait_connected(timeout=10, error="Roaming timed out")
dev[0].dump_monitor()
logger.info("Roam back to AP1")
dev[0].scan(freq="2412")
dev[0].request("ROAM " + bssid)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=20)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("EAP exchange missing")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=20)
if ev is None:
raise Exception("Roaming with the AP timed out")
def test_pmksa_cache_ap_expiration(dev, apdev):
"""PMKSA cache entry expiring on AP"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
'set', 'power_save', 'off'])
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk-user-session-timeout",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
hapd.dump_monitor()
dev[0].request("DISCONNECT")
ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
if ev is None:
raise Exception("No disconnection event received from hostapd")
dev[0].wait_disconnected()
# Wait for session timeout to remove PMKSA cache entry
time.sleep(5)
dev[0].dump_monitor()
hapd.dump_monitor()
dev[0].request("RECONNECT")
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=20)
if ev is None:
raise Exception("Reconnection with the AP timed out")
if "CTRL-EVENT-CONNECTED" in ev:
raise Exception("EAP exchange missing")
dev[0].wait_connected(timeout=20, error="Reconnect timed out")
dev[0].dump_monitor()
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd [2]")
hapd.dump_monitor()
# Wait for session timeout
ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("No disconnection event received from hostapd [2]")
dev[0].wait_disconnected(timeout=20)
dev[0].wait_connected(timeout=20, error="Reassociation timed out")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd [3]")
hapd.dump_monitor()
dev[0].dump_monitor()
def test_pmksa_cache_multiple_sta(dev, apdev):
"""PMKSA cache with multiple stations"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
for d in dev:
d.flush_scan_cache()
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk-user-session-timeout",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[1].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
dev[2].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk-user-session-timeout",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.flush_scan_cache()
wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
logger.info("Roam to AP2")
for sta in [dev[1], dev[0], dev[2], wpas]:
sta.dump_monitor()
sta.scan_for_bss(bssid2, freq="2412")
if "OK" not in sta.request("ROAM " + bssid2):
raise Exception("ROAM command failed (" + sta.ifname + ")")
ev = sta.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
if ev is None:
raise Exception("EAP success timed out")
sta.wait_connected(timeout=10, error="Roaming timed out")
sta.dump_monitor()
logger.info("Roam back to AP1")
for sta in [dev[1], wpas, dev[0], dev[2]]:
sta.dump_monitor()
sta.scan(freq="2412")
sta.dump_monitor()
sta.request("ROAM " + bssid)
sta.wait_connected(timeout=10, error="Roaming timed out")
sta.dump_monitor()
time.sleep(4)
logger.info("Roam back to AP2")
for sta in [dev[1], wpas, dev[0], dev[2]]:
sta.dump_monitor()
sta.scan(freq="2412")
sta.dump_monitor()
sta.request("ROAM " + bssid2)
sta.wait_connected(timeout=10, error="Roaming timed out")
sta.dump_monitor()
def test_pmksa_cache_opportunistic_multiple_sta(dev, apdev):
"""Opportunistic PMKSA caching with multiple stations"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['okc'] = "1"
hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
for d in dev:
d.flush_scan_cache()
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.flush_scan_cache()
for sta in [dev[0], dev[1], dev[2], wpas]:
sta.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef", okc=True,
scan_freq="2412")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
logger.info("Roam to AP2")
for sta in [dev[2], dev[0], wpas, dev[1]]:
sta.dump_monitor()
sta.scan_for_bss(bssid2, freq="2412")
if "OK" not in sta.request("ROAM " + bssid2):
raise Exception("ROAM command failed")
ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
pmksa2 = sta.get_pmksa(bssid2)
if pmksa2 is None:
raise Exception("No PMKSA cache entry created")
sta.dump_monitor()
logger.info("Roam back to AP1")
for sta in [dev[0], dev[1], dev[2], wpas]:
sta.dump_monitor()
sta.scan_for_bss(bssid, freq="2412")
sta.request("ROAM " + bssid)
ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=10)
if ev is None:
raise Exception("Roaming with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange")
def test_pmksa_cache_preauth_oom(dev, apdev):
"""RSN pre-authentication to generate PMKSA cache entry and OOM"""
try:
_test_pmksa_cache_preauth_oom(dev, apdev)
finally:
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
'down'])
hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0'])
def _test_pmksa_cache_preauth_oom(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['bridge'] = 'ap-br0'
hapd = hostapd.add_ap(apdev[0], params)
hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', 'ap-br0', '0'])
hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
bssid=apdev[0]['bssid'])
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['bridge'] = 'ap-br0'
params['rsn_preauth'] = '1'
params['rsn_preauth_interfaces'] = 'ap-br0'
hapd = hostapd.add_ap(apdev[1], params)
bssid1 = apdev[1]['bssid']
tests = [(1, "rsn_preauth_receive"),
(2, "rsn_preauth_receive"),
(1, "rsn_preauth_send"),
(1, "wpa_auth_pmksa_add_preauth;rsn_preauth_finished")]
for test in tests:
hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff")
with alloc_fail(hapd, test[0], test[1]):
dev[0].scan_for_bss(bssid1, freq="2412")
if "OK" not in dev[0].request("PREAUTH " + bssid1):
raise Exception("PREAUTH failed")
success = False
count = 0
for i in range(50):
time.sleep(0.1)
pmksa = dev[0].get_pmksa(bssid1)
if pmksa:
success = True
break
state = hapd.request('GET_ALLOC_FAIL')
if state.startswith('0:'):
count += 1
if count > 2:
break
logger.info("PMKSA cache success: " + str(success))
dev[0].request("PMKSA_FLUSH")
dev[0].wait_disconnected()
dev[0].wait_connected()
dev[0].dump_monitor()
def test_pmksa_cache_size_limit(dev, apdev):
"""PMKSA cache size limit in wpa_supplicant"""
try:
_test_pmksa_cache_size_limit(dev, apdev)
finally:
try:
hapd = hostapd.HostapdGlobal(apdev[0])
hapd.flush()
hapd.remove(apdev[0]['ifname'])
except:
pass
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
bssid = apdev[0]['bssid']
params['bssid'] = bssid
hostapd.add_ap(apdev[0], params)
def _test_pmksa_cache_size_limit(dev, apdev):
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412", only_add_network=True)
for i in range(33):
bssid = apdev[0]['bssid'][0:15] + "%02x" % i
logger.info("Iteration with BSSID " + bssid)
params['bssid'] = bssid
hostapd.add_ap(apdev[0], params)
dev[0].request("BSS_FLUSH 0")
dev[0].scan_for_bss(bssid, freq=2412, only_new=True)
dev[0].select_network(id)
dev[0].wait_connected()
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
entries = len(dev[0].request("PMKSA").splitlines()) - 1
if i == 32:
if entries != 32:
raise Exception("Unexpected number of PMKSA entries after expected removal of the oldest entry")
elif i + 1 != entries:
raise Exception("Unexpected number of PMKSA entries")
hapd = hostapd.HostapdGlobal(apdev[0])
hapd.flush()
hapd.remove(apdev[0]['ifname'])
def test_pmksa_cache_preauth_timeout(dev, apdev):
"""RSN pre-authentication timing out"""
try:
_test_pmksa_cache_preauth_timeout(dev, apdev)
finally:
dev[0].request("SET dot11RSNAConfigSATimeout 60")
def _test_pmksa_cache_preauth_timeout(dev, apdev):
dev[0].request("SET dot11RSNAConfigSATimeout 1")
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
bssid=apdev[0]['bssid'])
if "OK" not in dev[0].request("PREAUTH f2:11:22:33:44:55"):
raise Exception("PREAUTH failed")
ev = dev[0].wait_event(["RSN: pre-authentication with"], timeout=5)
if ev is None:
raise Exception("No timeout event seen")
if "timed out" not in ev:
raise Exception("Unexpected event: " + ev)
def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
"""RSN pre-authentication OOM in wpa_supplicant"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
hapd = hostapd.add_ap(apdev[0], params)
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef",
bssid=apdev[0]['bssid'])
for i in range(1, 11):
with alloc_fail(dev[0], i, "rsn_preauth_init"):
res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
for j in range(10):
state = dev[0].request('GET_ALLOC_FAIL')
if state.startswith('0:'):
break
time.sleep(0.05)
def test_pmksa_cache_ctrl(dev, apdev):
"""PMKSA cache control interface operations"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
addr = dev[0].own_addr()
dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
pmksa_sta = dev[0].get_pmksa(bssid)
if pmksa_sta is None:
raise Exception("No PMKSA cache entry created on STA")
pmksa_ap = hapd.get_pmksa(addr)
if pmksa_ap is None:
raise Exception("No PMKSA cache entry created on AP")
if pmksa_sta['pmkid'] != pmksa_ap['pmkid']:
raise Exception("PMKID mismatch in PMKSA cache entries")
if "OK" not in hapd.request("PMKSA_FLUSH"):
raise Exception("PMKSA_FLUSH failed")
pmksa_ap = hapd.get_pmksa(addr)
if pmksa_ap is not None:
raise Exception("PMKSA cache entry was not removed on AP")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].request("RECONNECT")
dev[0].wait_connected()
pmksa_sta2 = dev[0].get_pmksa(bssid)
if pmksa_sta2 is None:
raise Exception("No PMKSA cache entry created on STA after reconnect")
pmksa_ap2 = hapd.get_pmksa(addr)
if pmksa_ap2 is None:
raise Exception("No PMKSA cache entry created on AP after reconnect")
if pmksa_sta2['pmkid'] != pmksa_ap2['pmkid']:
raise Exception("PMKID mismatch in PMKSA cache entries after reconnect")
if pmksa_sta2['pmkid'] == pmksa_sta['pmkid']:
raise Exception("PMKID did not change after reconnect")
def test_pmksa_cache_ctrl_events(dev, apdev):
"""PMKSA cache control interface events"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412", wait_connect=False)
ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=15)
if ev is None:
raise Exception("No PMKSA-CACHE-ADDED event")
dev[0].wait_connected()
items = ev.split(' ')
if items[1] != bssid:
raise Exception("BSSID mismatch: " + ev)
if int(items[2]) != id:
raise Exception("network_id mismatch: " + ev)
dev[0].request("PMKSA_FLUSH")
ev = dev[0].wait_event(["PMKSA-CACHE-REMOVED"], timeout=15)
if ev is None:
raise Exception("No PMKSA-CACHE-REMOVED event")
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
items = ev.split(' ')
if items[1] != bssid:
raise Exception("BSSID mismatch: " + ev)
if int(items[2]) != id:
raise Exception("network_id mismatch: " + ev)
def test_pmksa_cache_ctrl_ext(dev, apdev):
"""PMKSA cache control interface for external management"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
res1 = dev[0].request("PMKSA_GET %d" % id)
logger.info("PMKSA_GET: " + res1)
if "UNKNOWN COMMAND" in res1:
raise HwsimSkip("PMKSA_GET not supported in the build")
if bssid not in res1:
raise Exception("PMKSA cache entry missing")
hostapd.add_ap(apdev[1], params)
bssid2 = apdev[1]['bssid']
dev[0].scan_for_bss(bssid2, freq=2412, force_scan=True)
dev[0].request("ROAM " + bssid2)
dev[0].wait_connected()
res2 = dev[0].request("PMKSA_GET %d" % id)
logger.info("PMKSA_GET: " + res2)
if bssid not in res2:
raise Exception("PMKSA cache entry 1 missing")
if bssid2 not in res2:
raise Exception("PMKSA cache entry 2 missing")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].request("PMKSA_FLUSH")
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412", only_add_network=True)
res3 = dev[0].request("PMKSA_GET %d" % id)
if res3 != '':
raise Exception("Unexpected PMKSA cache entry remains: " + res3)
res4 = dev[0].request("PMKSA_GET %d" % (id + 1234))
if not res4.startswith('FAIL'):
raise Exception("Unexpected PMKSA cache entry for unknown network: " + res4)
for entry in res2.splitlines():
if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
raise Exception("Failed to add PMKSA entry")
dev[0].select_network(id)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Connection with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange after external PMKSA cache restore")
def test_pmksa_cache_ctrl_ext_ft(dev, apdev):
"""PMKSA cache control interface for external management (FT)"""
params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
params['wpa_key_mgmt'] = "FT-EAP"
params['nas_identifier'] = "nas.w1.fi"
params['r1_key_holder'] = "000102030406"
params["mobility_domain"] = "a1b2"
hapd = hostapd.add_ap(apdev[0], params)
bssid = apdev[0]['bssid']
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="FT-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
scan_freq="2412")
res1 = dev[0].request("PMKSA_GET %d" % id)
logger.info("PMKSA_GET: " + res1)
if "UNKNOWN COMMAND" in res1:
raise HwsimSkip("PMKSA_GET not supported in the build")
if bssid not in res1:
raise Exception("PMKSA cache entry missing")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].request("PMKSA_FLUSH")
id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="FT-EAP",
eap="GPSK", identity="gpsk user",
password="abcdefghijklmnop0123456789abcdef",
ft_eap_pmksa_caching="1",
scan_freq="2412", only_add_network=True)
res3 = dev[0].request("PMKSA_GET %d" % id)
if res3 != '':
raise Exception("Unexpected PMKSA cache entry remains: " + res3)
for entry in res1.splitlines():
if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
raise Exception("Failed to add PMKSA entry")
dev[0].select_network(id)
ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
"CTRL-EVENT-CONNECTED"], timeout=15)
if ev is None:
raise Exception("Connection with the AP timed out")
if "CTRL-EVENT-EAP-STARTED" in ev:
raise Exception("Unexpected EAP exchange after external PMKSA cache restore")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
dev[0].dump_monitor()
dev[0].request("PMKSA_FLUSH")
# Add a PMKSA cache entry for FT-EAP with PMKSA caching disabled to confirm
# that the PMKID is not configured to the driver (this part requires manual
# check of the debug log currently).
dev[0].set_network(id, "ft_eap_pmksa_caching", "0")
for entry in res1.splitlines():
if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
raise Exception("Failed to add PMKSA entry")
def test_rsn_preauth_processing(dev, apdev):
"""RSN pre-authentication processing on AP"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['rsn_preauth'] = '1'
params['rsn_preauth_interfaces'] = "lo"
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
_bssid = binascii.unhexlify(bssid.replace(':', ''))
eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
password_hex="0123456789abcdef0123456789abcdef")
addr = dev[0].own_addr()
_addr = binascii.unhexlify(addr.replace(':', ''))
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(0x88c7))
sock.bind(("lo", socket.htons(0x88c7)))
foreign = b"\x02\x03\x04\x05\x06\x07"
proto = b"\x88\xc7"
tests = []
# RSN: too short pre-auth packet (len=14)
tests += [_bssid + foreign + proto]
# Not EAPOL-Start
tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 0, 0)]
# RSN: pre-auth for foreign address 02:03:04:05:06:07
tests += [foreign + foreign + proto + struct.pack('>BBH', 0, 0, 0)]
# RSN: pre-auth for already association STA 02:00:00:00:00:00
tests += [_bssid + _addr + proto + struct.pack('>BBH', 0, 0, 0)]
# New STA
tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 1)]
# IEEE 802.1X: received EAPOL-Start from STA
tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 0)]
# frame too short for this IEEE 802.1X packet
tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 1)]
# EAPOL-Key - Dropped key data from unauthorized Supplicant
tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 3, 0)]
# EAPOL-Encapsulated-ASF-Alert
tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 4, 0)]
# unknown IEEE 802.1X packet type
tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 255, 0)]
for t in tests:
sock.send(t)
def test_rsn_preauth_local_errors(dev, apdev):
"""RSN pre-authentication and local errors on AP"""
params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
params['rsn_preauth'] = '1'
params['rsn_preauth_interfaces'] = "lo"
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
_bssid = binascii.unhexlify(bssid.replace(':', ''))
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(0x88c7))
sock.bind(("lo", socket.htons(0x88c7)))
foreign = b"\x02\x03\x04\x05\x06\x07"
foreign2 = b"\x02\x03\x04\x05\x06\x08"
proto = b"\x88\xc7"
with alloc_fail(hapd, 1, "ap_sta_add;rsn_preauth_receive"):
sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
with alloc_fail(hapd, 1, "eapol_auth_alloc;rsn_preauth_receive"):
sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
with alloc_fail(hapd, 1, "eap_server_sm_init;ieee802_1x_new_station;rsn_preauth_receive"):
sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
hapd.request("DISABLE")
tests = [(1, "=rsn_preauth_iface_add"),
(2, "=rsn_preauth_iface_add"),
(1, "l2_packet_init;rsn_preauth_iface_add"),
(1, "rsn_preauth_iface_init"),
(1, "rsn_preauth_iface_init")]
for count, func in tests:
with alloc_fail(hapd, count, func):
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("ENABLE succeeded unexpectedly")
hapd.set("rsn_preauth_interfaces", "lo lo lo does-not-exist lo ")
if "FAIL" not in hapd.request("ENABLE"):
raise Exception("ENABLE succeeded unexpectedly")
hapd.set("rsn_preauth_interfaces", " lo lo ")
if "OK" not in hapd.request("ENABLE"):
raise Exception("ENABLE failed")
sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
+
+def test_pmksa_cache_add_failure(dev, apdev):
+ """PMKSA cache add failure"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ with alloc_fail(dev[0], 1, "pmksa_cache_add"):
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
diff --git a/tests/hwsim/test_rrm.py b/tests/hwsim/test_rrm.py
index 84318f6cb64f..9111a357eaca 100644
--- a/tests/hwsim/test_rrm.py
+++ b/tests/hwsim/test_rrm.py
@@ -1,2128 +1,2142 @@
# Radio measurement
# Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
# Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
# Copyright (c) 2017, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import binascii
import re
import logging
logger = logging.getLogger()
import struct
import subprocess
import time
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
from remotehost import remote_compatible
def check_rrm_support(dev):
rrm = int(dev.get_driver_status_field("capa.rrm_flags"), 16)
if rrm & 0x5 != 0x5 and rrm & 0x10 != 0x10:
raise HwsimSkip("Required RRM capabilities are not supported")
def check_tx_power_support(dev):
rrm = int(dev.get_driver_status_field("capa.rrm_flags"), 16)
if rrm & 0x8 != 0x8:
raise HwsimSkip("Required RRM capabilities are not supported")
nr = "00112233445500000000510107"
lci = "01000800101298c0b512926666f6c2f1001c00004104050000c00012"
civic = "01000b0011223344556677889900998877665544332211aabbccddeeff"
def check_nr_results(dev, bssids=None, lci=False, civic=False):
if bssids is None:
ev = dev.wait_event(["RRM-NEIGHBOR-REP-REQUEST-FAILED"], timeout=10)
if ev is None:
raise Exception("RRM neighbor report failure not received")
return
received = []
for bssid in bssids:
ev = dev.wait_event(["RRM-NEIGHBOR-REP-RECEIVED"], timeout=10)
if ev is None:
raise Exception("RRM report result not indicated")
received.append(ev)
for bssid in bssids:
found = False
for r in received:
if "RRM-NEIGHBOR-REP-RECEIVED bssid=" + bssid in r:
if lci and "lci=" not in r:
raise Exception("LCI data not reported for %s" % bssid)
if civic and "civic=" not in r:
raise Exception("civic data not reported for %s" % bssid)
received.remove(r)
found = True
break
if not found:
raise Exception("RRM report result for %s not indicated" % bssid)
def test_rrm_neighbor_db(dev, apdev):
"""hostapd ctrl_iface SET_NEIGHBOR"""
params = {"ssid": "test", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
params = {"ssid": "test2", "rrm_neighbor_report": "1"}
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
res = hapd.request("SHOW_NEIGHBOR")
if len(res.splitlines()) != 1:
raise Exception("Unexpected SHOW_NEIGHBOR output(1): " + res)
if apdev[0]['bssid'] not in res:
raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
if "OK" not in hapd2.request("SET_NEIGHBOR " + res.strip()):
raise Exception("Failed to copy neighbor entry to another hostapd")
res2 = hapd2.request("SHOW_NEIGHBOR")
if len(res2.splitlines()) != 2:
raise Exception("Unexpected SHOW_NEIGHBOR output: " + res2)
if res not in res2:
raise Exception("Copied entry not visible")
# Bad BSSID
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:gg ssid=\"test1\" nr=" + nr):
raise Exception("Set neighbor succeeded unexpectedly")
# Bad SSID
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=test1 nr=" + nr):
raise Exception("Set neighbor succeeded unexpectedly")
# Bad SSID end
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1 nr=" + nr):
raise Exception("Set neighbor succeeded unexpectedly")
# No SSID
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 nr=" + nr):
raise Exception("Set neighbor succeeded unexpectedly")
# No NR
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
raise Exception("Set neighbor succeeded unexpectedly")
# Odd length of NR
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr[:-1]):
raise Exception("Set neighbor succeeded unexpectedly")
# Invalid lci
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=1"):
raise Exception("Set neighbor succeeded unexpectedly")
# Invalid civic
if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " civic=1"):
raise Exception("Set neighbor succeeded unexpectedly")
# No entry yet in database
if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
raise Exception("Remove neighbor succeeded unexpectedly")
# Add a neighbor entry
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
res = hapd.request("SHOW_NEIGHBOR")
if len(res.splitlines()) != 2:
raise Exception("Unexpected SHOW_NEIGHBOR output(2): " + res)
if apdev[0]['bssid'] not in res:
raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
if "00:11:22:33:44:55" not in res:
raise Exception("Added BSS not visible in SHOW_NEIGHBOR output")
# Another BSSID with the same SSID
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
res = hapd.request("SHOW_NEIGHBOR")
if len(res.splitlines()) != 3:
raise Exception("Unexpected SHOW_NEIGHBOR output(3): " + res)
if apdev[0]['bssid'] not in res:
raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
if "00:11:22:33:44:55" not in res:
raise Exception("Added BSS not visible in SHOW_NEIGHBOR output")
if "00:11:22:33:44:56" not in res:
raise Exception("Second added BSS not visible in SHOW_NEIGHBOR output")
# Fewer parameters
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr):
raise Exception("Set neighbor failed")
# SSID in hex format
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=7465737431 nr=" + nr):
raise Exception("Set neighbor failed")
# With more parameters
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " civic=" + civic):
raise Exception("Set neighbor failed")
# With all parameters
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
# Another SSID on the same BSSID
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test2\" nr=" + nr + " lci=" + lci):
raise Exception("Set neighbor failed")
if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
raise Exception("Remove neighbor failed")
if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:56 ssid=\"test1\""):
raise Exception("Remove neighbor failed")
if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test2\""):
raise Exception("Remove neighbor failed")
# Double remove
if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
raise Exception("Remove neighbor succeeded unexpectedly")
# Stationary AP
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\" nr=" + nr + " lci=" + lci + " civic=" + civic + " stat"):
raise Exception("Set neighbor failed")
res = hapd.request("SHOW_NEIGHBOR")
if len(res.splitlines()) != 2:
raise Exception("Unexpected SHOW_NEIGHBOR output(4): " + res)
if "00:11:22:33:44:55" not in res or " stat" not in res:
raise Exception("Unexpected SHOW_NEIGHBOR output(4b): " + res)
if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\""):
raise Exception("Remove neighbor failed")
# Add an entry for following REMOVE_NEIGHBOR tests
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=7465737431 nr=" + nr):
raise Exception("Set neighbor failed")
# Invalid remove - bad BSSID
if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:5 ssid=\"test1\""):
raise Exception("Remove neighbor succeeded unexpectedly")
# Invalid remove - bad SSID
if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1"):
raise Exception("Remove neighbor succeeded unexpectedly")
# Remove without specifying SSID
if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55"):
raise Exception("Remove neighbor without SSID failed")
res = hapd.request("SHOW_NEIGHBOR")
if len(res.splitlines()) != 1:
raise Exception("Unexpected SHOW_NEIGHBOR output(5): " + res)
if apdev[0]['bssid'] not in res:
raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
+def test_rrm_neighbor_db_failures(dev, apdev):
+ """hostapd ctrl_iface SET_NEIGHBOR failures"""
+ params = {"ssid": "test", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ cmd = "SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic
+ tests = [(1, "hostapd_neighbor_add"),
+ (1, "wpabuf_dup;hostapd_neighbor_set"),
+ (2, "wpabuf_dup;hostapd_neighbor_set"),
+ (3, "wpabuf_dup;hostapd_neighbor_set")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request(cmd):
+ raise Exception("Set neighbor succeeded")
+
def test_rrm_neighbor_db_disabled(dev, apdev):
"""hostapd ctrl_iface SHOW_NEIGHBOR while neighbor report disabled"""
params = {"ssid": "test"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
if "FAIL" not in hapd.request("SHOW_NEIGHBOR"):
raise Exception("SHOW_NEIGHBOR accepted")
def test_rrm_neighbor_rep_req(dev, apdev):
"""wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST"""
check_rrm_support(dev[0])
nr1 = "00112233445500000000510107"
nr2 = "00112233445600000000510107"
nr3 = "dd112233445500000000510107"
params = {"ssid": "test"}
hostapd.add_ap(apdev[0]['ifname'], params)
params = {"ssid": "test2", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[1]['ifname'], params)
bssid1 = apdev[1]['bssid']
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request succeeded unexpectedly (AP without RRM)")
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"abcdef\""):
raise Exception("Request succeeded unexpectedly (AP without RRM 2)")
dev[0].request("DISCONNECT")
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
check_nr_results(dev[0], [bssid1])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST lci"):
raise Exception("Request failed")
check_nr_results(dev[0], [bssid1])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST lci civic"):
raise Exception("Request failed")
check_nr_results(dev[0], [bssid1])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\""):
raise Exception("Request failed")
check_nr_results(dev[0])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci civic"):
raise Exception("Request failed")
check_nr_results(dev[0])
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\" nr=" + nr1 + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test3\" nr=" + nr2 + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test4\" nr=" + nr2 + " lci=" + lci + " civic=" + civic):
raise Exception("Set neighbor failed")
if "OK" not in hapd.request("SET_NEIGHBOR dd:11:22:33:44:55 ssid=\"test5\" nr=" + nr3 + " lci=" + lci):
raise Exception("Set neighbor failed")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\""):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
lci=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
civic=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
lci=True, civic=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\""):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:56"])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" lci"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:56"], lci=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:56"], civic=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" lci civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["00:11:22:33:44:56"], lci=True, civic=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\""):
raise Exception("Request failed")
check_nr_results(dev[0], ["dd:11:22:33:44:55"])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" lci"):
raise Exception("Request failed")
check_nr_results(dev[0], ["dd:11:22:33:44:55"], lci=True)
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["dd:11:22:33:44:55"])
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" lci civic"):
raise Exception("Request failed")
check_nr_results(dev[0], ["dd:11:22:33:44:55"], lci=True)
def test_rrm_neighbor_rep_oom(dev, apdev):
"""hostapd neighbor report OOM"""
check_rrm_support(dev[0])
nr1 = "00112233445500000000510107"
nr2 = "00112233445600000000510107"
nr3 = "dd112233445500000000510107"
params = {"ssid": "test", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(hapd, 1, "hostapd_send_nei_report_resp"):
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
ev = dev[0].wait_event(["RRM-NEIGHBOR-REP-REQUEST-FAILED"], timeout=5)
if ev is None:
raise Exception("Neighbor report failure not reported")
def test_rrm_lci_req(dev, apdev):
"""hostapd lci request"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
# station not specified
if "FAIL" not in hapd.request("REQ_LCI "):
raise Exception("REQ_LCI with no station succeeded unexpectedly")
# station that is not connected specified
if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI succeeded unexpectedly (station not connected)")
dev[0].request("SET LCI ")
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
# station connected without LCI
if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI succeeded unexpectedly (station without lci)")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected(timeout=2)
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
# station connected with LCI
if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI failed unexpectedly")
def test_rrm_lci_req_timeout(dev, apdev):
"""hostapd lci request timeout"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hapd.set("ext_mgmt_frame_handling", "1")
if "OK" not in hapd.request("REQ_LCI " + addr):
raise Exception("REQ_LCI failed unexpectedly")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("No response seen at the AP")
# Ignore response and wait for HOSTAPD_RRM_REQUEST_TIMEOUT
time.sleep(5.1)
# Process response after timeout
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % ev.split(' ')[1]):
raise Exception("MGMT_RX_PROCESS failed")
for i in range(257):
if "OK" not in hapd.request("REQ_LCI " + addr):
raise Exception("REQ_LCI failed unexpectedly")
dev[0].dump_monitor()
hapd.dump_monitor()
hapd.set("ext_mgmt_frame_handling", "0")
dev[0].request("DISCONNECT")
dev[0].wait_disconnected()
def test_rrm_lci_req_oom(dev, apdev):
"""LCI report generation OOM"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_build_lci_report"):
if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI failed unexpectedly")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
dev[0].request("SET LCI ")
# This in in wpas_rrm_build_lci_report(), but backtrace may not always work
# for the "reject" label there.
with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_handle_msr_req_element"):
if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI failed unexpectedly")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
def test_rrm_lci_req_ap_oom(dev, apdev):
"""LCI report generation AP OOM and failure"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_lci_req"):
if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI succeeded during OOM")
with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_lci_req"):
if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI succeeded during failure testing")
def test_rrm_lci_req_get_reltime_failure(dev, apdev):
"""LCI report generation and os_get_reltime() failure"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
with fail_test(dev[0], 1, "os_get_reltime;wpas_rrm_build_lci_report"):
if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
raise Exception("REQ_LCI failed unexpectedly")
wait_fail_trigger(dev[0], "GET_FAIL")
def test_rrm_neighbor_rep_req_from_conf(dev, apdev):
"""wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST and hostapd config"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_neighbor_report": "1",
"stationary_ap": "1", "lci": lci, "civic": civic}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = apdev[0]['bssid']
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
check_nr_results(dev[0], [bssid])
def test_rrm_neighbor_rep_req_timeout(dev, apdev):
"""wpa_supplicant behavior on NEIGHBOR_REP_REQUEST response timeout"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_neighbor_report": "1",
"stationary_ap": "1", "lci": lci, "civic": civic}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
hapd.set("ext_mgmt_frame_handling", "1")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
msg = hapd.mgmt_rx()
if msg is None:
raise Exception("Neighbor report request not seen")
check_nr_results(dev[0])
def test_rrm_neighbor_rep_req_oom(dev, apdev):
"""wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST OOM"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_neighbor_report": "1",
"stationary_ap": "1", "lci": lci, "civic": civic}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(dev[0], 1, "wpabuf_alloc;wpas_rrm_process_neighbor_rep"):
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
with fail_test(dev[0], 1,
"wpa_driver_nl80211_send_action;wpas_rrm_send_neighbor_rep_request"):
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request succeeded unexpectedly")
with alloc_fail(dev[0], 1,
"wpabuf_alloc;wpas_rrm_send_neighbor_rep_request"):
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request succeeded unexpectedly")
def test_rrm_neighbor_rep_req_disconnect(dev, apdev):
"""wpa_supplicant behavior on disconnection during NEIGHBOR_REP_REQUEST"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_neighbor_report": "1",
"stationary_ap": "1", "lci": lci, "civic": civic}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request accepted while disconnected")
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
hapd.set("ext_mgmt_frame_handling", "1")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
msg = hapd.mgmt_rx()
if msg is None:
raise Exception("Neighbor report request not seen")
dev[0].request("DISCONNECT")
check_nr_results(dev[0])
def test_rrm_neighbor_rep_req_not_supported(dev, apdev):
"""NEIGHBOR_REP_REQUEST for AP not supporting neighbor report"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request accepted unexpectedly")
def test_rrm_neighbor_rep_req_busy(dev, apdev):
"""wpa_supplicant and concurrent NEIGHBOR_REP_REQUEST commands"""
check_rrm_support(dev[0])
params = {"ssid": "test2", "rrm_neighbor_report": "1",
"stationary_ap": "1", "lci": lci, "civic": civic}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
hapd.set("ext_mgmt_frame_handling", "1")
if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request failed")
msg = hapd.mgmt_rx()
if msg is None:
raise Exception("Neighbor report request not seen")
if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
raise Exception("Request accepted while disconnected")
def test_rrm_ftm_range_req(dev, apdev):
"""hostapd FTM range request command"""
check_rrm_support(dev[0])
try:
run_rrm_ftm_range_req(dev, apdev)
finally:
dev[1].request("VENDOR_ELEM_REMOVE 13 *")
def run_rrm_ftm_range_req(dev, apdev):
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
# station not specified
if "FAIL" not in hapd.request("REQ_RANGE "):
raise Exception("REQ_RANGE with no station succeeded unexpectedly")
# station that is not connected specified
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr()):
raise Exception("REQ_RANGE succeeded unexpectedly (station not connected)")
# No responders specified
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10"):
raise Exception("REQ_RANGE succeeded unexpectedly (no responder)")
# Bad responder address
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:"):
raise Exception("REQ_RANGE succeeded unexpectedly (bad responder address)")
# Bad responder address
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:55 00:11:22:33:44"):
raise Exception("REQ_RANGE succeeded unexpectedly (bad responder address 2)")
# Bad min_ap value
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 300 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (invalid min_ap value)")
# Bad rand value
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " -1 10 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (invalid rand value)")
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 65536 10 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (invalid rand value)")
# Missing min_ap value
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10"):
raise Exception("REQ_RANGE succeeded unexpectedly (missing min_ap value)")
# Too many responders
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10" + 20*" 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (too many responders)")
# Wrong min AP count
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
# Override RM capabilities to include FTM range report
dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
# Request range: Destination address is not connected
if "FAIL" not in hapd.request("REQ_RANGE 11:22:33:44:55:66 10 1 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
# Responder not in database
# Note: this check would pass since the station does not support FTM range
# request and not because the responder is not in the database.
if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 1 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
# Missing neighbor report for 00:11:22:33:44:55
if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 00:11:22:33:44:55"):
raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
# Send request
if "OK" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
raise Exception("REQ_RANGE failed unexpectedly")
# Too long range request
if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1" + 16*(" " + bssid)):
raise Exception("REQ_RANGE accepted for too long range request")
time.sleep(0.1)
dev[0].request("DISCONNECT")
dev[1].request("DISCONNECT")
dev[1].wait_disconnected()
def test_rrm_ftm_range_req_timeout(dev, apdev):
"""hostapd FTM range request timeout"""
check_rrm_support(dev[0])
try:
run_rrm_ftm_range_req_timeout(dev, apdev)
finally:
dev[1].request("VENDOR_ELEM_REMOVE 13 *")
def run_rrm_ftm_range_req_timeout(dev, apdev):
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
# Override RM capabilities to include FTM range report
dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[1].own_addr()
hapd.set("ext_mgmt_frame_handling", "1")
if "OK" not in hapd.request("REQ_RANGE " + addr + " 10 1 " + bssid):
raise Exception("REQ_RANGE failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("No response seen at the AP")
# Ignore response and wait for HOSTAPD_RRM_REQUEST_TIMEOUT
time.sleep(5.1)
# Process response after timeout
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % ev.split(' ')[1]):
raise Exception("MGMT_RX_PROCESS failed")
for i in range(257):
if "OK" not in hapd.request("REQ_RANGE " + addr + " 10 1 " + bssid):
raise Exception("REQ_RANGE failed")
dev[1].dump_monitor()
hapd.dump_monitor()
hapd.set("ext_mgmt_frame_handling", "0")
dev[1].request("DISCONNECT")
dev[1].wait_disconnected()
def test_rrm_ftm_range_req_failure(dev, apdev):
"""hostapd FTM range request failure"""
check_rrm_support(dev[0])
try:
run_rrm_ftm_range_req_failure(dev, apdev)
finally:
dev[1].request("VENDOR_ELEM_REMOVE 13 *")
def run_rrm_ftm_range_req_failure(dev, apdev):
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
# Override RM capabilities to include FTM range report
dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_range_req"):
if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
raise Exception("REQ_RANGE succeeded during OOM")
with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_range_req"):
if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
raise Exception("REQ_RANGE succeeded during failure testing")
dev[1].request("DISCONNECT")
dev[1].wait_disconnected()
def test_rrm_ftm_capa_indication(dev, apdev):
"""FTM capability indication"""
try:
_test_rrm_ftm_capa_indication(dev, apdev)
finally:
dev[0].request("SET ftm_initiator 0")
dev[0].request("SET ftm_responder 0")
def _test_rrm_ftm_capa_indication(dev, apdev):
params = {"ssid": "ftm",
"ftm_responder": "1",
"ftm_initiator": "1",}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
if "OK" not in dev[0].request("SET ftm_initiator 1"):
raise Exception("could not set ftm_initiator")
if "OK" not in dev[0].request("SET ftm_responder 1"):
raise Exception("could not set ftm_responder")
dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True)
class BeaconReport:
def __init__(self, report):
self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni = struct.unpack("<BBQHBBB", report[0:15])
report = report[15:]
self.bssid = report[0:6]
self.bssid_str = "%02x:%02x:%02x:%02x:%02x:%02x" % (struct.unpack('6B', self.bssid))
report = report[6:]
self.antenna_id, self.parent_tsf = struct.unpack("<BI", report[0:5])
report = report[5:]
self.subelems = report
self.frame_body = None
self.frame_body_fragment_id = None
self.last_indication = None
while len(report) >= 2:
eid, elen = struct.unpack('BB', report[0:2])
report = report[2:]
if len(report) < elen:
raise Exception("Invalid subelement in beacon report")
if eid == 1:
# Reported Frame Body
# Contents depends on the reporting detail request:
# 0 = no Reported Frame Body subelement
# 1 = all fixed fields and any elements identified in Request
# element
# 2 = all fixed fields and all elements
# Fixed fields: Timestamp[8] BeaconInt[2] CapabInfo[2]
self.frame_body = report[0:elen]
if eid == 2:
self.frame_body_fragment_id = report[0:elen]
if eid == 164:
self.last_indication = report[0:elen]
report = report[elen:]
def __str__(self):
txt = "opclass={} channel={} start={} duration={} frame_info={} rcpi={} rsni={} bssid={} antenna_id={} parent_tsf={}".format(self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni, self.bssid_str, self.antenna_id, self.parent_tsf)
if self.frame_body:
txt += " frame_body=" + binascii.hexlify(self.frame_body).decode()
if self.frame_body_fragment_id:
txt += " fragment_id=" + binascii.hexlify(self.frame_body_fragment_id).decode()
if self.last_indication:
txt += " last_indication=" + binascii.hexlify(self.last_indication).decode()
return txt
def run_req_beacon(hapd, addr, request):
token = hapd.request("REQ_BEACON " + addr + " " + request)
if "FAIL" in token:
raise Exception("REQ_BEACON failed")
ev = hapd.wait_event(["BEACON-REQ-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("No TX status event for beacon request received")
fields = ev.split(' ')
if fields[1] != addr:
raise Exception("Unexpected STA address in TX status: " + fields[1])
if fields[2] != token:
raise Exception("Unexpected dialog token in TX status: " + fields[2] + " (expected " + token + ")")
if fields[3] != "ack=1":
raise Exception("Unexected ACK status in TX status: " + fields[3])
return token
@remote_compatible
def test_rrm_beacon_req_table(dev, apdev):
"""Beacon request - beacon table mode"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
tests = ["REQ_BEACON ",
"REQ_BEACON q",
"REQ_BEACON 11:22:33:44:55:66",
"REQ_BEACON 11:22:33:44:55:66 req_mode=q",
"REQ_BEACON 11:22:33:44:55:66 req_mode=11",
"REQ_BEACON 11:22:33:44:55:66 1",
"REQ_BEACON 11:22:33:44:55:66 1q",
"REQ_BEACON 11:22:33:44:55:66 11223344556677889900aabbccddeeff"]
for t in tests:
if "FAIL" not in hapd.request(t):
raise Exception("Invalid command accepted: " + t)
dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if fields[1] != addr:
raise Exception("Unexpected STA address in beacon report response: " + fields[1])
if fields[2] != token:
raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
if fields[3] != "00":
raise Exception("Unexpected measurement report mode")
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
# Default reporting detail is 2, i.e., all fixed fields and elements.
if not report.frame_body:
raise Exception("Reported Frame Body subelement missing")
if len(report.frame_body) <= 12:
raise Exception("Too short Reported Frame Body subelement")
def test_rrm_beacon_req_frame_body_fragmentation(dev, apdev):
"""Beacon request - beacon table mode - frame body fragmentation"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd.set('vendor_elements', ("dd051122330203dd0400137400dd04001374ffdd0511"
"22330203dd0400137400dd04001374ffdd051122330203dd0400137400dd04001"
"374ffdd051122330203dd0400137400dd04001374ffdd051122330203dd040013"
"7400dd04001374ffdd051122330203dd0400137400dd04001374ffdd051122330"
"203dd0400137400dd04001374ffdd051122330203dd0400137400dd04001374ff"
"dd051122330203dd0400137400dd04001374ff"))
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
# 2 beacon reports elements are expected because of fragmentation
for i in range(0, 2):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if fields[1] != addr:
raise Exception("Unexpected STA address in beacon report response: " + fields[1])
if fields[2] != token:
raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
if fields[3] != "00":
raise Exception("Unexpected measurement report mode")
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
# Default reporting detail is 2, i.e., all fixed fields and elements.
if not report.frame_body_fragment_id:
raise Exception("Reported Frame Body Fragment ID subelement missing")
fragment_id = binascii.hexlify(report.frame_body_fragment_id)
frag_number = int(fragment_id[2:], 16) & int(0x7f)
if frag_number != i:
raise Exception("Incorrect fragment number: %d" % frag_number)
more_frags = int(fragment_id[2:], 16) >> 7
if i == 0 and more_frags != 1:
raise Exception("more fragments bit is not set on first fragment")
if i == 1 and more_frags != 0:
raise Exception("more fragments bit is set on last fragment")
def test_rrm_beacon_req_last_frame_indication(dev, apdev):
"""Beacon request - beacon table mode - last frame indication"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
# The request contains the last beacon report indication subelement
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffffa40101")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if fields[1] != addr:
raise Exception("Unexpected STA address in beacon report response: " + fields[1])
if fields[2] != token:
raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
if fields[3] != "00":
raise Exception("Unexpected measurement report mode")
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if not report.last_indication:
raise Exception("Last Beacon Report Indication subelement missing")
last = binascii.hexlify(report.last_indication).decode()
if (i == 2 and last != '01') or (i != 2 and last != '00'):
raise Exception("last beacon report indication is not set on last frame")
# The request does not contain the last beacon report indication subelement
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if fields[1] != addr:
raise Exception("Unexpected STA address in beacon report response: " + fields[1])
if fields[2] != token:
raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
if fields[3] != "00":
raise Exception("Unexpected measurement report mode")
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.last_indication:
raise Exception("Last Beacon Report Indication subelement present but not requested")
@remote_compatible
def test_rrm_beacon_req_table_detail(dev, apdev):
"""Beacon request - beacon table mode - reporting detail"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
logger.info("Reporting Detail 0")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020100")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.frame_body:
raise Exception("Reported Frame Body subelement included with Reporting Detail 0")
hapd.dump_monitor()
logger.info("Reporting Detail 1")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if not report.frame_body:
raise Exception("Reported Frame Body subelement missing")
if len(report.frame_body) != 12:
raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1")
hapd.dump_monitor()
logger.info("Reporting Detail 2")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if not report.frame_body:
raise Exception("Reported Frame Body subelement missing")
if len(report.frame_body) <= 12:
raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 2")
hapd.dump_monitor()
logger.info("Reporting Detail 3 (invalid)")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020103")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response to invalid reporting detail 3")
hapd.dump_monitor()
logger.info("Reporting Detail (too short)")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0200")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response to invalid reporting detail")
hapd.dump_monitor()
@remote_compatible
def test_rrm_beacon_req_table_request(dev, apdev):
"""Beacon request - beacon table mode - request element"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].flush_scan_cache()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if not report.frame_body:
raise Exception("Reported Frame Body subelement missing")
if len(report.frame_body) != 12 + 5 + 10:
raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested elements SSID + SuppRates")
hapd.dump_monitor()
logger.info("Incorrect reporting detail with request subelement")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102" + "0a03000106")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (invalid reporting detail)")
hapd.dump_monitor()
logger.info("Invalid request subelement length")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a00")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (invalid request subelement length)")
hapd.dump_monitor()
logger.info("Multiple request subelements")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0100" + "0a0101")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (multiple request subelements)")
hapd.dump_monitor()
@remote_compatible
def test_rrm_beacon_req_table_request_oom(dev, apdev):
"""Beacon request - beacon table mode - request element OOM"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with alloc_fail(dev[0], 1,
"bitfield_alloc;wpas_rm_handle_beacon_req_subelem"):
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response received (OOM)")
with alloc_fail(dev[0], 1,
"wpabuf_alloc;wpas_rrm_send_msr_report_mpdu"):
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response received (OOM)")
with fail_test(dev[0], 1,
"wpa_driver_nl80211_send_action;wpas_rrm_send_msr_report_mpdu"):
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response received (OOM)")
with alloc_fail(dev[0], 1,
"wpabuf_resize;wpas_add_beacon_rep"):
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received (OOM -> empty report)")
fields = ev.split(' ')
if len(fields[4]) > 0:
raise Exception("Unexpected beacon report received")
@remote_compatible
def test_rrm_beacon_req_table_bssid(dev, apdev):
"""Beacon request - beacon table mode - specific BSSID"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
bssid2 = hapd2.own_addr()
token = run_req_beacon(hapd, addr, "51000000000002" + bssid2.replace(':', ''))
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if "bssid=" + bssid2 not in str(report):
raise Exception("Report for unexpected BSS")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response")
@remote_compatible
def test_rrm_beacon_req_table_ssid(dev, apdev):
"""Beacon request - beacon table mode - specific SSID"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
bssid2 = hapd2.own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0007" + binascii.hexlify(b"another").decode())
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if "bssid=" + bssid2 not in str(report):
raise Exception("Report for unexpected BSS")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response")
hapd.dump_monitor()
logger.info("Wildcard SSID")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0000")
for i in range(2):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
hapd.dump_monitor()
logger.info("Too long SSID")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0021" + 33*"00")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (invalid SSID subelement in request)")
hapd.dump_monitor()
@remote_compatible
def test_rrm_beacon_req_table_info(dev, apdev):
"""Beacon request - beacon table mode - Reporting Information subelement"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
logger.info("Unsupported reporting information 1")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "01020100")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response (incapable) is not received")
fields = ev.split(' ')
if fields[3] != "02":
raise Exception("Beacon report response - unexpected mode (" + fields[3] + ")")
hapd.dump_monitor()
logger.info("Invalid reporting information length")
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "010100")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (invalid reporting information length)")
hapd.dump_monitor()
@remote_compatible
def test_rrm_beacon_req_table_unknown_subelem(dev, apdev):
"""Beacon request - beacon table mode - unknown subelement"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "330101" + "fe00")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
@remote_compatible
def test_rrm_beacon_req_table_truncated_subelem(dev, apdev):
"""Beacon request - beacon table mode - Truncated subelement"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0001")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response (truncated subelement)")
hapd.dump_monitor()
@remote_compatible
def test_rrm_beacon_req_table_rsne(dev, apdev):
"""Beacon request - beacon table mode - RSNE reporting"""
params = hostapd.wpa2_params(ssid="rrm-rsn", passphrase="12345678")
params["rrm_beacon_report"] = "1"
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm-rsn", psk="12345678", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0130")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if not report.frame_body:
raise Exception("Reported Frame Body subelement missing")
if len(report.frame_body) != 12 + 22:
raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested element RSNE")
if binascii.unhexlify("30140100000fac040100000fac040100000fac020c00") not in report.frame_body:
raise Exception("Full RSNE not found")
def test_rrm_beacon_req_table_vht(dev, apdev):
"""Beacon request - beacon table mode - VHT"""
clear_scan_cache(apdev[0])
try:
hapd = None
params = {"ssid": "rrm-vht",
"country_code": "FI",
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
params = {"ssid": "test-vht40",
"country_code": "FI",
"hw_mode": "a",
"channel": "48",
"ieee80211n": "1",
"ieee80211ac": "1",
"ht_capab": "[HT40-]",
"vht_capab": "",
"vht_oper_chwidth": "0",
"vht_oper_centr_freq_seg0_idx": "0",
}
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq=5240)
dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5180")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "f0000000000002ffffffffffff")
for i in range(2):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[0]['bssid']:
if report.opclass != 128 or report.channel != 36:
raise Exception("Incorrect opclass/channel for AP0")
elif report.bssid_str == apdev[1]['bssid']:
if report.opclass != 117 or report.channel != 48:
raise Exception("Incorrect opclass/channel for AP1")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not vht_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
dev[0].request("DISCONNECT")
disable_hapd(hapd)
disable_hapd(hapd2)
clear_regdom_dev(dev)
@remote_compatible
def test_rrm_beacon_req_active(dev, apdev):
"""Beacon request - active scan mode"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000640001ffffffffffff")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[0]['bssid']:
if report.opclass != 81 or report.channel != 1:
raise Exception("Incorrect opclass/channel for AP0")
elif report.bssid_str == apdev[1]['bssid']:
if report.opclass != 81 or report.channel != 11:
raise Exception("Incorrect opclass/channel for AP1")
@remote_compatible
def test_rrm_beacon_req_active_ignore_old_result(dev, apdev):
"""Beacon request - active scan mode and old scan result"""
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
hapd2.disable()
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51010000640001ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[1]['bssid']:
raise Exception("Old BSS reported")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response")
def start_ap(dev):
id = dev.add_network()
dev.set_network(id, "mode", "2")
dev.set_network_quoted(id, "ssid", 32*'A')
dev.set_network_quoted(id, "psk", "1234567890")
dev.set_network(id, "frequency", "2412")
dev.set_network(id, "scan_freq", "2412")
dev.select_network(id)
dev.wait_connected()
def test_rrm_beacon_req_active_many(dev, apdev):
"""Beacon request - active scan mode and many BSSs"""
for i in range(1, 7):
ifname = apdev[0]['ifname'] if i == 1 else apdev[0]['ifname'] + "-%d" % i
hapd1 = hostapd.add_bss(apdev[0], ifname, 'bss-%i.conf' % i)
hapd1.set('vendor_elements', "dd50" + 80*'bb')
hapd1.request("UPDATE_BEACON")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
wpas.request("SET device_name " + 20*'a')
start_ap(wpas)
start_ap(dev[1])
start_ap(dev[2])
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
params['vendor_elements'] = "dd50" + 80*'aa'
hapd = hostapd.add_ap(apdev[1]['ifname'], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
ok = False
for j in range(3):
token = run_req_beacon(hapd, addr, "51010000640001ffffffffffff")
for i in range(10):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if len(fields[4]) == 0:
break
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if i == 9:
ok = True
if ok:
break
@remote_compatible
def test_rrm_beacon_req_active_ap_channels(dev, apdev):
"""Beacon request - active scan mode with AP Channel Report subelement"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "dd0111" + "330351010b" + "dd0111")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[0]['bssid']:
if report.opclass != 81 or report.channel != 1:
raise Exception("Incorrect opclass/channel for AP0")
elif report.bssid_str == apdev[1]['bssid']:
if report.opclass != 81 or report.channel != 11:
raise Exception("Incorrect opclass/channel for AP1")
@remote_compatible
def test_rrm_beacon_req_passive_ap_channels(dev, apdev):
"""Beacon request - passive scan mode with AP Channel Report subelement"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51ff0000640000ffffffffffff" + "330351010b" + "3300" + "dd00")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[0]['bssid']:
if report.opclass != 81 or report.channel != 1:
raise Exception("Incorrect opclass/channel for AP0")
elif report.bssid_str == apdev[1]['bssid']:
if report.opclass != 81 or report.channel != 11:
raise Exception("Incorrect opclass/channel for AP1")
@remote_compatible
def test_rrm_beacon_req_active_single_channel(dev, apdev):
"""Beacon request - active scan mode with single channel"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "510b0000640001ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
@remote_compatible
def test_rrm_beacon_req_active_ap_channels_unknown_opclass(dev, apdev):
"""Beacon request - active scan mode with AP Channel Report subelement and unknown opclass"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "3303ff010b")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response (refused) not received")
fields = ev.split(' ')
if fields[3] != "04":
raise Exception("Unexpected beacon report mode: " + fields[3])
@remote_compatible
def test_rrm_beacon_req_active_ap_channel_oom(dev, apdev):
"""Beacon request - AP Channel Report subelement and OOM"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with alloc_fail(dev[0], 1, "wpas_add_channels"):
token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "330351010b")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
# allow either not to respond or send refused response
if ev is not None:
fields = ev.split(' ')
if fields[3] != "04":
raise Exception("Unexpected Beacon report during OOM with mode: " + fields[3])
@remote_compatible
def test_rrm_beacon_req_active_scan_fail(dev, apdev):
"""Beacon request - Active scan failure"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with alloc_fail(dev[0], 1, "wpa_supplicant_trigger_scan"):
token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "330351010b")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("No Beacon report")
fields = ev.split(' ')
if fields[3] != "04":
raise Exception("Unexpected Beacon report contents: " + ev)
@remote_compatible
def test_rrm_beacon_req_active_zero_duration(dev, apdev):
"""Beacon request - Action scan and zero duration"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000000001ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected Beacon report")
@remote_compatible
def test_rrm_beacon_req_active_fail_random(dev, apdev):
"""Beacon request - active scan mode os_get_random failure"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with fail_test(dev[0], 1, "os_get_random;wpas_rm_handle_beacon_req"):
token = run_req_beacon(hapd, addr, "51000000640001ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
@remote_compatible
def test_rrm_beacon_req_passive(dev, apdev):
"""Beacon request - passive scan mode"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51000000640000ffffffffffff")
for i in range(1, 3):
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.bssid_str == apdev[0]['bssid']:
if report.opclass != 81 or report.channel != 1:
raise Exception("Incorrect opclass/channel for AP0")
elif report.bssid_str == apdev[1]['bssid']:
if report.opclass != 81 or report.channel != 11:
raise Exception("Incorrect opclass/channel for AP1")
@remote_compatible
def test_rrm_beacon_req_passive_no_match(dev, apdev):
"""Beacon request - passive scan mode and no matching BSS"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "51010000640000021122334455")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if len(fields[4]) > 0:
raise Exception("Unexpected beacon report BSS")
@remote_compatible
def test_rrm_beacon_req_passive_no_match_oom(dev, apdev):
"""Beacon request - passive scan mode and no matching BSS (OOM)"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_beacon_rep_scan_process"):
token = run_req_beacon(hapd, addr, "51010000640000021122334455")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected Beacon report response during OOM")
# verify reporting is still functional
token = run_req_beacon(hapd, addr, "51010000640000021122334455")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report %d response not received" % i)
fields = ev.split(' ')
if len(fields[4]) > 0:
raise Exception("Unexpected beacon report BSS")
@remote_compatible
def test_rrm_beacon_req_active_duration_mandatory(dev, apdev):
"""Beacon request - Action scan and duration mandatory"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "req_mode=10 51000000640001ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("No Beacon report response")
fields = ev.split(' ')
rrm = int(dev[0].get_driver_status_field("capa.rrm_flags"), 16)
if rrm & 0x20 == 0x20:
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
else:
# Driver does not support scan dwell time setting, so wpa_supplicant
# rejects the measurement request due to the mandatory duration using
# Measurement Report Mode field Incapable=1.
if fields[3] != '02':
raise Exception("Unexpected Measurement Report Mode: " + fields[3])
if len(fields[4]) > 0:
raise Exception("Unexpected beacon report received")
def test_rrm_beacon_req_passive_scan_vht(dev, apdev):
"""Beacon request - passive scan mode - VHT"""
clear_scan_cache(apdev[0])
try:
hapd = None
params = {"ssid": "rrm-vht",
"country_code": "FI",
'ieee80211d': '1',
"hw_mode": "a",
"channel": "36",
"ht_capab": "[HT40+]",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "1",
"vht_oper_centr_freq_seg0_idx": "42",
"rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq=5180)
dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5180")
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "80000000640000ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.opclass != 128 or report.channel != 36:
raise Exception("Incorrect opclass/channel for AP")
token = run_req_beacon(hapd, addr, "82000000640000ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.opclass != 128 or report.channel != 36:
raise Exception("Incorrect opclass/channel for AP")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
if not vht_supported():
raise HwsimSkip("80 MHz channel not supported in regulatory information")
raise
finally:
clear_regdom(hapd, dev)
def test_rrm_beacon_req_passive_scan_vht160(dev, apdev):
"""Beacon request - passive scan mode - VHT160"""
clear_scan_cache(apdev[0])
try:
hapd = None
params = {"ssid": "rrm-vht",
"country_code": "ZA",
'ieee80211d': '1',
"hw_mode": "a",
"channel": "104",
"ht_capab": "[HT40-]",
"vht_capab": "[VHT160]",
"ieee80211n": "1",
"ieee80211ac": "1",
"vht_oper_chwidth": "2",
"vht_oper_centr_freq_seg0_idx": "114",
"rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
dev[0].scan_for_bss(apdev[0]['bssid'], freq=5520)
dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5520")
sig = dev[0].request("SIGNAL_POLL").splitlines()
if "WIDTH=160 MHz" not in sig:
raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
addr = dev[0].own_addr()
token = run_req_beacon(hapd, addr, "81000000640000ffffffffffff")
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received")
fields = ev.split(' ')
report = BeaconReport(binascii.unhexlify(fields[4]))
logger.info("Received beacon report: " + str(report))
if report.opclass != 129 or report.channel != 104:
raise Exception("Incorrect opclass/channel for AP")
except Exception as e:
if isinstance(e, Exception) and str(e) == "AP startup failed":
raise HwsimSkip("ZA regulatory rule likely did not have DFS requirement removed")
raise
finally:
clear_regdom(hapd, dev)
def test_rrm_beacon_req_ap_errors(dev, apdev):
"""Beacon request - AP error cases"""
try:
run_rrm_beacon_req_ap_errors(dev, apdev)
finally:
dev[1].request("VENDOR_ELEM_REMOVE 13 *")
def run_rrm_beacon_req_ap_errors(dev, apdev):
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
# Override RM capabilities (remove all)
dev[1].request("VENDOR_ELEM_ADD 13 46050000000000")
dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr1 = dev[1].own_addr()
# Beacon request: Too short request data
if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 11"):
raise Exception("Invalid REQ_BEACON accepted")
# Beacon request: 02:00:00:00:01:00 does not support table beacon report
if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 51000000000002ffffffffffff"):
raise Exception("Invalid REQ_BEACON accepted")
# Beacon request: 02:00:00:00:01:00 does not support active beacon report
if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 51000000640001ffffffffffff"):
raise Exception("Invalid REQ_BEACON accepted")
# Beacon request: 02:00:00:00:01:00 does not support passive beacon report
if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 510b0000640000ffffffffffff"):
raise Exception("Invalid REQ_BEACON accepted")
# Beacon request: Unknown measurement mode 3
if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 510b0000640003ffffffffffff"):
raise Exception("Invalid REQ_BEACON accepted")
for i in range(257):
if "FAIL" in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
raise Exception("REQ_BEACON failed")
dev[0].dump_monitor()
hapd.dump_monitor()
with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_beacon_req"):
if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
raise Exception("REQ_BEACON accepted during OOM")
with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_beacon_req"):
if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
raise Exception("REQ_BEACON accepted during failure testing")
def test_rrm_req_reject_oom(dev, apdev):
"""Radio measurement request - OOM while rejecting a request"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("SET ext_mgmt_frame_handling 1")
with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_handle_msr_req_element"):
# "RRM: Parallel measurements are not supported, reject"
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "05000100002603010105"):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response during OOM")
def test_rrm_req_when_rrm_not_used(dev, apdev):
"""Radio/link measurement request for non-RRM association"""
params = {"ssid": "rrm"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("SET ext_mgmt_frame_handling 1")
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "050001000026030100fe"):
raise Exception("MGMT_RX_PROCESS failed")
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected beacon report response when RRM is disabled")
dev[0].request("REMOVE_NETWORK all")
dev[0].wait_disconnected()
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "050001000026030100fe"):
raise Exception("MGMT_RX_PROCESS failed")
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
raise Exception("MGMT_RX_PROCESS failed")
@remote_compatible
def test_rrm_req_proto(dev, apdev):
"""Radio measurement request - protocol testing"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0], params)
bssid = hapd.own_addr()
dev[0].request("SET LCI ")
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("SET ext_mgmt_frame_handling 1")
tests = []
# "RRM: Ignoring too short radio measurement request"
tests += ["0500", "050001", "05000100"]
# No measurement request element at all
tests += ["0500010000"]
# "RRM: Truncated element"
tests += ["050001000026"]
# "RRM: Element length too short"
tests += ["05000100002600", "0500010000260111", "050001000026021122"]
# "RRM: Element length too long"
tests += ["05000100002603", "0500010000260311", "050001000026031122"]
# "RRM: Enable bit not supported, ignore"
tests += ["05000100002603010200"]
# "RRM: Measurement report failed. TX power insertion not supported"
# OR
# "RRM: Link measurement report failed. Request too short"
tests += ["0502"]
# Too short LCI request
tests += ["05000100002603010008"]
# Too short neighbor report response
tests += ["0505"]
# Unexpected neighbor report response
tests += ["050500", "050501", "050502", "050503", "050504", "050505"]
# Too short beacon request
tests += ["05000100002603010005",
"0500010000260f010005112233445566778899aabbcc"]
# Unknown beacon report mode
tests += ["05000100002610010005112233445566778899aabbccdd"]
# "RRM: Expected Measurement Request element, but EID is 0"
tests += ["05000100000000"]
for t in tests:
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected response seen at the AP: " + ev)
tests = []
# "RRM: Parallel measurements are not supported, reject"
tests += ["05000100002603010105"]
# "RRM: Unsupported radio measurement type 254"
tests += ["050001000026030100fe"]
# Reject LCI request
tests += ["0500010000260701000811223344"]
# Beacon report info subelement; no valid channels
tests += ["05000100002614010005112233445566008899aabbccdd01020000"]
for t in tests:
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("No response seen at the AP")
hapd.dump_monitor()
dev[0].request("SET LCI " + lci)
tests = []
# "Not building LCI report - bad location subject"
tests += ["0500010000260701000811223344"]
for t in tests:
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
if ev is not None:
raise Exception("Unexpected response seen at the AP: " + ev)
tests = []
# LCI report or reject
tests += ["0500010000260701000801223344",
"05000100002607010008010402ff",
"05000100002608010008010402ffff"]
for t in tests:
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("No response seen at the AP")
hapd.dump_monitor()
# Verify rejection of a group-addressed request frame
hdr = "d0003a01" + "ffffffffffff" + 2*bssid.replace(':', '') + "1000"
# "RRM: Parallel measurements are not supported, reject"
t = "05000100002603010105"
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected response seen at the AP (broadcast request rejected)")
hapd.dump_monitor()
hapd.set("ext_mgmt_frame_handling", "0")
dev[0].request("SET ext_mgmt_frame_handling 0")
dev[0].request("SET LCI ")
def test_rrm_link_measurement(dev, apdev):
"""Radio measurement request - link measurement"""
check_tx_power_support(dev[0])
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("SET ext_mgmt_frame_handling 1")
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
raise Exception("MGMT_RX_PROCESS failed")
ev = hapd.wait_event(["MGMT-RX"], timeout=5)
if ev is None:
raise Exception("No link measurement report seen")
def test_rrm_link_measurement_oom(dev, apdev):
"""Radio measurement request - link measurement OOM"""
check_tx_power_support(dev[0])
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
dev[0].request("SET ext_mgmt_frame_handling 1")
with alloc_fail(dev[0], 1, "wpabuf_alloc;wpas_rrm_handle_link_measurement_request"):
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
with fail_test(dev[0], 1, "wpas_rrm_handle_link_measurement_request"):
if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
raise Exception("MGMT_RX_PROCESS failed")
wait_fail_trigger(dev[0], "GET_FAIL")
ev = hapd.wait_event(["MGMT-RX"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected beacon report response during OOM")
def test_rrm_rep_parse_proto(dev, apdev):
"""hostapd rrm report parsing protocol testing"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].request("SET LCI " + lci)
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
tests = ["0501",
"05ff01",
"0501012703fffffe2700",
"0501012703ffff05",
"05010127ffffff05" + 252*"00",
"0504012603ffffff2600",
"0504012603ffff08",
"0504012608ffff08ffffffffff",
"0504012608ffff08ff04021234",
"0504012608ffff08ff04020100",
"0504012608ffff08ff0402ffff"]
for t in tests:
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed for " + t)
if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"rrm\" nr=" + nr + " lci=" + lci):
raise Exception("Set neighbor failed")
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0504012608ffff08ff04021000"):
raise Exception("MGMT_RX_PROCESS failed")
def test_rrm_unexpected(dev, apdev):
"""hostapd unexpected rrm"""
check_rrm_support(dev[0])
params = {"ssid": "rrm", "rrm_neighbor_report": "0"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
tests = ["050401"]
for t in tests:
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed for " + t)
def check_beacon_req(hapd, addr, idx):
request = "51000000000002ffffffffffff" + "020100"
token = hapd.request("REQ_BEACON " + addr + " " + request)
if "FAIL" in token:
raise Exception("REQ_BEACON failed (%d)" % idx)
ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
if ev is None:
raise Exception("Beacon report response not received (%d)" % idx)
def test_rrm_reassociation(dev, apdev):
"""Radio measurement request - reassociation"""
params = {"ssid": "rrm", "rrm_beacon_report": "1"}
hapd = hostapd.add_ap(apdev[0]['ifname'], params)
bssid = hapd.own_addr()
addr = dev[0].own_addr()
dev[0].flush_scan_cache()
dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
check_beacon_req(hapd, addr, 1)
dev[0].request("REASSOCIATE")
dev[0].wait_connected()
check_beacon_req(hapd, addr, 1)
hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
bssid2 = hapd2.own_addr()
dev[0].scan_for_bss(bssid2, freq=2412)
dev[0].roam(bssid2)
check_beacon_req(hapd2, addr, 2)
dev[0].scan_for_bss(bssid, freq=2412)
dev[0].roam(bssid)
check_beacon_req(hapd, addr, 3)
diff --git a/tests/hwsim/test_wnm.py b/tests/hwsim/test_wnm.py
index 10b07559b9ce..354822327210 100644
--- a/tests/hwsim/test_wnm.py
+++ b/tests/hwsim/test_wnm.py
@@ -1,1951 +1,1984 @@
# WNM tests
# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import binascii
import struct
import time
import logging
logger = logging.getLogger()
import subprocess
import hostapd
from wpasupplicant import WpaSupplicant
from utils import *
from wlantest import Wlantest
from datetime import datetime
def clear_regdom_state(dev, hapd, hapd2):
for i in range(0, 3):
ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
if ev is None or "init=COUNTRY_IE" in ev:
break
if hapd:
hapd.request("DISABLE")
if hapd2:
hapd2.request("DISABLE")
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def start_wnm_ap(apdev, bss_transition=True, time_adv=False, ssid=None,
wnm_sleep_mode=False, wnm_sleep_mode_no_keys=False, rsn=False,
ocv=False, ap_max_inactivity=0, coloc_intf_reporting=False,
hw_mode=None, channel=None, country_code=None, country3=None,
- pmf=True, passphrase=None, ht=True, vht=False, mbo=False):
+ pmf=True, passphrase=None, ht=True, vht=False, mbo=False,
+ beacon_prot=False):
if rsn:
if not ssid:
ssid = "test-wnm-rsn"
if not passphrase:
passphrase = "12345678"
params = hostapd.wpa2_params(ssid, passphrase)
if pmf:
params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
else:
params = {"ssid": "test-wnm"}
if bss_transition:
params["bss_transition"] = "1"
if time_adv:
params["time_advertisement"] = "2"
params["time_zone"] = "EST5"
if wnm_sleep_mode:
params["wnm_sleep_mode"] = "1"
if wnm_sleep_mode_no_keys:
params["wnm_sleep_mode_no_keys"] = "1"
if ocv:
params["ocv"] = "1"
if ap_max_inactivity:
params["ap_max_inactivity"] = str(ap_max_inactivity)
if coloc_intf_reporting:
params["coloc_intf_reporting"] = "1"
if hw_mode:
params["hw_mode"] = hw_mode
if channel:
params["channel"] = channel
if country_code:
params["country_code"] = country_code
params["ieee80211d"] = "1"
if country3:
params["country3"] = country3
if not ht:
params['ieee80211n'] = '0'
if vht:
params['ieee80211ac'] = "1"
params["vht_oper_chwidth"] = "0"
params["vht_oper_centr_freq_seg0_idx"] = "0"
if mbo:
params["mbo"] = "1"
try:
hapd = hostapd.add_ap(apdev, params)
except Exception as e:
if "Failed to set hostapd parameter ocv" in str(e):
raise HwsimSkip("OCV not supported")
raise
if rsn:
Wlantest.setup(hapd)
wt = Wlantest()
wt.flush()
wt.add_passphrase("12345678")
return hapd
@remote_compatible
def test_wnm_bss_transition_mgmt(dev, apdev):
"""WNM BSS Transition Management"""
start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].request("WNM_BSS_QUERY 0")
def test_wnm_bss_transition_mgmt_oom(dev, apdev):
"""WNM BSS Transition Management OOM"""
hapd = start_wnm_ap(apdev[0])
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
with alloc_fail(hapd, 1, "ieee802_11_send_bss_trans_mgmt_request"):
dev[0].request("WNM_BSS_QUERY 0")
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
@remote_compatible
def test_wnm_disassoc_imminent(dev, apdev):
"""WNM Disassociation Imminent"""
hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].p2p_interface_addr()
hapd.request("DISASSOC_IMMINENT " + addr + " 10")
ev = dev[0].wait_event(["WNM: Disassociation Imminent"])
if ev is None:
raise Exception("Timeout while waiting for disassociation imminent")
if "Disassociation Timer 10" not in ev:
raise Exception("Unexpected disassociation imminent contents")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Timeout while waiting for re-connection scan")
def test_wnm_disassoc_imminent_fail(dev, apdev):
"""WNM Disassociation Imminent failure"""
hapd = start_wnm_ap(apdev[0])
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
with fail_test(hapd, 1, "wnm_send_disassoc_imminent"):
if "FAIL" not in hapd.request("DISASSOC_IMMINENT " + addr + " 10"):
raise Exception("DISASSOC_IMMINENT succeeded during failure testing")
@remote_compatible
def test_wnm_ess_disassoc_imminent(dev, apdev):
"""WNM ESS Disassociation Imminent"""
hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].p2p_interface_addr()
hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
if ev is None:
raise Exception("Timeout while waiting for ESS disassociation imminent")
if "0 1024 http://example.com/session-info" not in ev:
raise Exception("Unexpected ESS disassociation imminent message contents")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Timeout while waiting for re-connection scan")
def test_wnm_ess_disassoc_imminent_fail(dev, apdev):
"""WNM ESS Disassociation Imminent failure"""
hapd = start_wnm_ap(apdev[0])
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 10 http://" + 256*'a'):
raise Exception("Invalid ESS_DISASSOC URL accepted")
with fail_test(hapd, 1, "wnm_send_ess_disassoc_imminent"):
if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info"):
raise Exception("ESS_DISASSOC succeeded during failure testing")
def test_wnm_ess_disassoc_imminent_reject(dev, apdev):
"""WNM ESS Disassociation Imminent getting rejected"""
hapd = start_wnm_ap(apdev[0])
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
if "OK" not in dev[0].request("SET reject_btm_req_reason 123"):
raise Exception("Failed to set reject_btm_req_reason")
hapd.request("ESS_DISASSOC " + addr + " 1 http://example.com/session-info")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=10)
if ev is None:
raise Exception("BSS-TM-RESP not seen")
if "status_code=123" not in ev:
raise Exception("Unexpected response status: " + ev)
dev[0].wait_disconnected()
dev[0].request("DISCONNECT")
@remote_compatible
def test_wnm_ess_disassoc_imminent_pmf(dev, apdev):
"""WNM ESS Disassociation Imminent"""
hapd = start_wnm_ap(apdev[0], rsn=True)
dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
addr = dev[0].p2p_interface_addr()
hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
if ev is None:
raise Exception("Timeout while waiting for ESS disassociation imminent")
if "1 1024 http://example.com/session-info" not in ev:
raise Exception("Unexpected ESS disassociation imminent message contents")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
if ev is None:
raise Exception("Timeout while waiting for re-connection scan")
-def check_wnm_sleep_mode_enter_exit(hapd, dev, interval=None, tfs_req=None):
+def check_wnm_sleep_mode_enter_exit(hapd, dev, interval=None, tfs_req=None,
+ rekey=False):
addr = dev.p2p_interface_addr()
sta = hapd.get_sta(addr)
if "[WNM_SLEEP_MODE]" in sta['flags']:
raise Exception("Station unexpectedly in WNM-Sleep Mode")
logger.info("Going to WNM Sleep Mode")
extra = ""
if interval is not None:
extra += " interval=" + str(interval)
if tfs_req:
extra += " tfs_req=" + tfs_req
if "OK" not in dev.request("WNM_SLEEP enter" + extra):
raise Exception("WNM_SLEEP failed")
ok = False
for i in range(20):
time.sleep(0.1)
sta = hapd.get_sta(addr)
if "[WNM_SLEEP_MODE]" in sta['flags']:
ok = True
break
if not ok:
raise Exception("Station failed to enter WNM-Sleep Mode")
+ if rekey:
+ time.sleep(0.1)
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ ev = dev.wait_event(["WPA: Group rekeying completed"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected report of GTK rekey during WNM-Sleep Mode")
+
logger.info("Waking up from WNM Sleep Mode")
ok = False
dev.request("WNM_SLEEP exit")
for i in range(20):
time.sleep(0.1)
sta = hapd.get_sta(addr)
if "[WNM_SLEEP_MODE]" not in sta['flags']:
ok = True
break
if not ok:
raise Exception("Station failed to exit WNM-Sleep Mode")
+ if rekey:
+ time.sleep(0.1)
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ ev = dev.wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+
@remote_compatible
def test_wnm_sleep_mode_open(dev, apdev):
"""WNM Sleep Mode - open"""
hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
check_wnm_sleep_mode_enter_exit(hapd, dev[0])
check_wnm_sleep_mode_enter_exit(hapd, dev[0], interval=100)
check_wnm_sleep_mode_enter_exit(hapd, dev[0], tfs_req="5b17010001130e110000071122334455661122334455661234")
cmds = ["foo",
"exit tfs_req=123 interval=10",
"enter tfs_req=qq interval=10"]
for cmd in cmds:
if "FAIL" not in dev[0].request("WNM_SLEEP " + cmd):
raise Exception("Invalid WNM_SLEEP accepted")
def test_wnm_sleep_mode_open_fail(dev, apdev):
"""WNM Sleep Mode - open (fail)"""
hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
with fail_test(hapd, 1, "nl80211_send_frame_cmd;ieee802_11_send_wnmsleep_resp"):
dev[0].request("WNM_SLEEP enter")
wait_fail_trigger(hapd, "GET_FAIL")
def test_wnm_sleep_mode_disabled_on_ap(dev, apdev):
"""WNM Sleep Mode disabled on AP"""
hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=False)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
# Ignore WNM-Sleep Mode Request from 02:00:00:00:00:00 since WNM-Sleep Mode is disabled
dev[0].request("WNM_SLEEP enter")
time.sleep(0.1)
@remote_compatible
def test_wnm_sleep_mode_rsn(dev, apdev):
"""WNM Sleep Mode - RSN"""
hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True, rsn=True,
pmf=False)
dev[0].connect("test-wnm-rsn", psk="12345678", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
check_wnm_sleep_mode_enter_exit(hapd, dev[0])
@remote_compatible
def test_wnm_sleep_mode_ap_oom(dev, apdev):
"""WNM Sleep Mode - AP side OOM"""
hapd = start_wnm_ap(apdev[0], bss_transition=False, wnm_sleep_mode=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
with alloc_fail(hapd, 1, "ieee802_11_send_wnmsleep_resp"):
dev[0].request("WNM_SLEEP enter")
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
with alloc_fail(hapd, 2, "ieee802_11_send_wnmsleep_resp"):
dev[0].request("WNM_SLEEP exit")
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
@remote_compatible
def test_wnm_sleep_mode_rsn_pmf(dev, apdev):
"""WNM Sleep Mode - RSN with PMF"""
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, time_adv=True)
dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+def test_wnm_sleep_mode_rsn_beacon_prot(dev, apdev):
+ """WNM Sleep Mode - RSN with PMF and beacon protection"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, time_adv=True,
+ beacon_prot=True)
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+ beacon_prot="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0], rekey=True)
+
@remote_compatible
def test_wnm_sleep_mode_rsn_ocv(dev, apdev):
"""WNM Sleep Mode - RSN with OCV"""
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
time_adv=True, ocv=True)
dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2", ocv="1",
key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
check_wnm_sleep_mode_enter_exit(hapd, dev[0])
# Check if OCV succeeded or failed
ev = dev[0].wait_event(["OCV failed"], timeout=1)
if ev is not None:
raise Exception("OCI verification failed: " + ev)
@remote_compatible
def test_wnm_sleep_mode_rsn_badocv(dev, apdev):
"""WNM Sleep Mode - RSN with OCV and bad OCI elements"""
ssid = "test-wnm-rsn"
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, ocv=True)
bssid = apdev[0]['bssid']
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256", ocv="1",
proto="WPA2", ieee80211w="2", scan_freq="2412")
dev[0].request("WNM_SLEEP enter")
time.sleep(0.1)
msg = {'fc': MGMT_SUBTYPE_ACTION << 4,
'da': bssid,
'sa': dev[0].own_addr(),
'bssid': bssid}
logger.debug("WNM Sleep Mode Request - Missing OCI element")
msg['payload'] = struct.pack("<BBBBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_REQ, 0,
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT, 0, 0,
WLAN_EID_TFS_REQ, 0)
mgmt_tx(dev[0], "MGMT_TX {} {} freq=2412 wait_time=200 no_cck=1 action={}".format(
msg['da'], msg['bssid'], binascii.hexlify(msg['payload']).decode()))
ev = hapd.wait_event(["OCV failed"], timeout=5)
if ev is None:
raise Exception("AP did not report missing OCI element")
logger.debug("WNM Sleep Mode Request - Bad OCI element")
msg['payload'] = struct.pack("<BBBBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_REQ, 0,
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT, 0,
0,
WLAN_EID_TFS_REQ, 0)
oci_ie = struct.pack("<BBB", 81, 2, 0)
msg['payload'] += struct.pack("<BBB", WLAN_EID_EXTENSION, 1 + len(oci_ie),
WLAN_EID_EXT_OCV_OCI) + oci_ie
mgmt_tx(dev[0], "MGMT_TX {} {} freq=2412 wait_time=200 no_cck=1 action={}".format(
msg['da'], msg['bssid'], binascii.hexlify(msg['payload']).decode()))
ev = hapd.wait_event(["OCV failed"], timeout=5)
if ev is None:
raise Exception("AP did not report bad OCI element")
msg = {'fc': MGMT_SUBTYPE_ACTION << 4,
'da': dev[0].own_addr(),
'sa': bssid,
'bssid': bssid}
hapd.set("ext_mgmt_frame_handling", "1")
logger.debug("WNM Sleep Mode Response - Missing OCI element")
msg['payload'] = struct.pack("<BBBHBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0,
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
dev[0].request("WNM_SLEEP exit")
hapd.mgmt_tx(msg)
expect_ack(hapd)
ev = dev[0].wait_event(["OCV failed"], timeout=5)
if ev is None:
raise Exception("STA did not report missing OCI element")
logger.debug("WNM Sleep Mode Response - Bad OCI element")
msg['payload'] = struct.pack("<BBBHBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0,
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
oci_ie = struct.pack("<BBB", 81, 2, 0)
msg['payload'] += struct.pack("<BBB", WLAN_EID_EXTENSION, 1 + len(oci_ie),
WLAN_EID_EXT_OCV_OCI) + oci_ie
hapd.mgmt_tx(msg)
expect_ack(hapd)
ev = dev[0].wait_event(["OCV failed"], timeout=5)
if ev is None:
raise Exception("STA did not report bad OCI element")
def test_wnm_sleep_mode_rsn_ocv_failure(dev, apdev):
"""WNM Sleep Mode - RSN with OCV - local failure"""
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
time_adv=True, ocv=True)
dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2", ocv="1",
key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
# Failed to allocate buffer for OCI element in WNM-Sleep Mode frame
with alloc_fail(hapd, 2, "ieee802_11_send_wnmsleep_resp"):
if "OK" not in dev[0].request("WNM_SLEEP enter"):
raise Exception("WNM_SLEEP failed")
wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
def test_wnm_sleep_mode_rsn_pmf_key_workaround(dev, apdev):
"""WNM Sleep Mode - RSN with PMF and GTK/IGTK workaround"""
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
wnm_sleep_mode_no_keys=True,
time_adv=True, ocv=True)
dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
if ev is None:
raise Exception("No connection event received from hostapd")
check_wnm_sleep_mode_enter_exit(hapd, dev[0])
def test_wnm_sleep_mode_proto(dev, apdev):
"""WNM Sleep Mode - protocol testing"""
hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=True, bss_transition=False)
bssid = hapd.own_addr()
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
tests = ["0a10",
"0a1001",
"0a10015d00",
"0a10015d01",
"0a10015d0400000000",
"0a1001" + 7*("5bff" + 255*"00") + "5d00",
"0a1001ff00"]
for t in tests:
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
hapd.set("ext_mgmt_frame_handling", "0")
MGMT_SUBTYPE_ACTION = 13
ACTION_CATEG_WNM = 10
WNM_ACT_BSS_TM_REQ = 7
WNM_ACT_BSS_TM_RESP = 8
WNM_ACT_SLEEP_MODE_REQ = 16
WNM_ACT_SLEEP_MODE_RESP = 17
WNM_ACT_NOTIFICATION_REQ = 26
WNM_ACT_NOTIFICATION_RESP = 27
WNM_NOTIF_TYPE_FW_UPGRADE = 0
WNM_NOTIF_TYPE_WFA = 1
WLAN_EID_TFS_REQ = 91
WLAN_EID_TFS_RESP = 92
WLAN_EID_WNMSLEEP = 93
WLAN_EID_EXTENSION = 255
WLAN_EID_EXT_OCV_OCI = 54
WNM_SLEEP_MODE_ENTER = 0
WNM_SLEEP_MODE_EXIT = 1
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_SUBELEM_GTK = 0
WNM_SLEEP_SUBELEM_IGTK = 1
def bss_tm_req(dst, src, dialog_token=1, req_mode=0, disassoc_timer=0,
validity_interval=1):
msg = {}
msg['fc'] = MGMT_SUBTYPE_ACTION << 4
msg['da'] = dst
msg['sa'] = src
msg['bssid'] = src
msg['payload'] = struct.pack("<BBBBHB",
ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
dialog_token, req_mode, disassoc_timer,
validity_interval)
return msg
def rx_bss_tm_resp(hapd, expect_dialog=None, expect_status=None):
for i in range(0, 100):
resp = hapd.mgmt_rx()
if resp is None:
raise Exception("No BSS TM Response received")
if resp['subtype'] == MGMT_SUBTYPE_ACTION:
break
if i == 99:
raise Exception("Not an Action frame")
payload = resp['payload']
if len(payload) < 2 + 3:
raise Exception("Too short payload")
(category, action) = struct.unpack('BB', payload[0:2])
if category != ACTION_CATEG_WNM or action != WNM_ACT_BSS_TM_RESP:
raise Exception("Not a BSS TM Response")
pos = payload[2:]
(dialog, status, bss_term_delay) = struct.unpack('BBB', pos[0:3])
resp['dialog'] = dialog
resp['status'] = status
resp['bss_term_delay'] = bss_term_delay
pos = pos[3:]
if len(pos) >= 6 and status == 0:
resp['target_bssid'] = binascii.hexlify(pos[0:6])
pos = pos[6:]
resp['candidates'] = pos
if expect_dialog is not None and dialog != expect_dialog:
raise Exception("Unexpected dialog token")
if expect_status is not None and status != expect_status:
raise Exception("Unexpected status code %d" % status)
return resp
def expect_ack(hapd):
ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
if ev is None:
raise Exception("Missing TX status")
if "ok=1" not in ev:
raise Exception("Action frame not acknowledged")
def mgmt_tx(dev, msg):
if "FAIL" in dev.request(msg):
raise Exception("Failed to send Action frame")
ev = dev.wait_event(["MGMT-TX-STATUS"], timeout=10)
if ev is None:
raise Exception("Timeout on MGMT-TX-STATUS")
if "result=SUCCESS" not in ev:
raise Exception("Peer did not ack Action frame")
@remote_compatible
def test_wnm_bss_tm_req(dev, apdev):
"""BSS Transition Management Request"""
hapd = start_wnm_ap(apdev[0])
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hapd.set("ext_mgmt_frame_handling", "1")
# truncated BSS TM Request
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x08)
req['payload'] = struct.pack("<BBBBH",
ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
1, 0, 0)
hapd.mgmt_tx(req)
expect_ack(hapd)
dev[0].dump_monitor()
# no disassociation and no candidate list
req = bss_tm_req(addr, apdev[0]['bssid'],
dialog_token=2)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=2, expect_status=1)
dev[0].dump_monitor()
# truncated BSS Termination Duration
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x08)
hapd.mgmt_tx(req)
expect_ack(hapd)
dev[0].dump_monitor()
# BSS Termination Duration with TSF=0 and Duration=10
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x08, dialog_token=3)
req['payload'] += struct.pack("<BBQH", 4, 10, 0, 10)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=3, expect_status=1)
dev[0].dump_monitor()
# truncated Session Information URL
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x10)
hapd.mgmt_tx(req)
expect_ack(hapd)
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x10)
req['payload'] += struct.pack("<BBB", 3, 65, 66)
hapd.mgmt_tx(req)
expect_ack(hapd)
dev[0].dump_monitor()
# Session Information URL
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x10, dialog_token=4)
req['payload'] += struct.pack("<BBB", 2, 65, 66)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=4, expect_status=0)
dev[0].dump_monitor()
# Preferred Candidate List without any entries
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=5)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=5, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with a truncated entry
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01)
req['payload'] += struct.pack("<BB", 52, 1)
hapd.mgmt_tx(req)
expect_ack(hapd)
dev[0].dump_monitor()
# Preferred Candidate List with a too short entry
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=6)
req['payload'] += struct.pack("<BB", 52, 0)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with a non-matching entry
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=6)
req['payload'] += struct.pack("<BB6BLBBB", 52, 13,
1, 2, 3, 4, 5, 6,
0, 81, 1, 7)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with a truncated subelement
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=7)
req['payload'] += struct.pack("<BB6BLBBBBB", 52, 13 + 2,
1, 2, 3, 4, 5, 6,
0, 81, 1, 7,
1, 1)
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=7, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with lots of invalid optional subelements
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=8)
subelems = struct.pack("<BBHB", 1, 3, 0, 100)
subelems += struct.pack("<BBB", 2, 1, 65)
subelems += struct.pack("<BB", 3, 0)
subelems += struct.pack("<BBQB", 4, 9, 0, 10)
subelems += struct.pack("<BBHLB", 5, 7, 0, 0, 0)
subelems += struct.pack("<BB", 66, 0)
subelems += struct.pack("<BBBBBB", 70, 4, 0, 0, 0, 0)
subelems += struct.pack("<BB", 71, 0)
req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
1, 2, 3, 4, 5, 6,
0, 81, 1, 7) + subelems
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with lots of valid optional subelements (twice)
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=8)
# TSF Information
subelems = struct.pack("<BBHH", 1, 4, 0, 100)
# Condensed Country String
subelems += struct.pack("<BBBB", 2, 2, 65, 66)
# BSS Transition Candidate Preference
subelems += struct.pack("<BBB", 3, 1, 100)
# BSS Termination Duration
subelems += struct.pack("<BBQH", 4, 10, 0, 10)
# Bearing
subelems += struct.pack("<BBHLH", 5, 8, 0, 0, 0)
# Measurement Pilot Transmission
subelems += struct.pack("<BBBBB", 66, 3, 0, 0, 0)
# RM Enabled Capabilities
subelems += struct.pack("<BBBBBBB", 70, 5, 0, 0, 0, 0, 0)
# Multiple BSSID
subelems += struct.pack("<BBBB", 71, 2, 0, 0)
req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems) * 2,
1, 2, 3, 4, 5, 6,
0, 81, 1, 7) + subelems + subelems
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List with truncated BSS Termination Duration
# WNM: Too short BSS termination duration
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=8)
# BSS Termination Duration (truncated)
subelems = struct.pack("<BBQB", 4, 9, 0, 10)
req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
1, 2, 3, 4, 5, 6,
0, 81, 1, 7) + subelems
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
dev[0].dump_monitor()
# Preferred Candidate List followed by vendor element
req = bss_tm_req(addr, apdev[0]['bssid'],
req_mode=0x01, dialog_token=8)
subelems = b''
req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
1, 2, 3, 4, 5, 6,
0, 81, 1, 7) + subelems
req['payload'] += binascii.unhexlify("DD0411223344")
hapd.mgmt_tx(req)
resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
dev[0].dump_monitor()
@remote_compatible
def test_wnm_bss_keep_alive(dev, apdev):
"""WNM keep-alive"""
hapd = start_wnm_ap(apdev[0], bss_transition=False, ap_max_inactivity=1)
addr = dev[0].p2p_interface_addr()
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
start = hapd.get_sta(addr)
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=2)
if ev is not None:
raise Exception("Unexpected disconnection")
end = hapd.get_sta(addr)
if int(end['rx_packets']) <= int(start['rx_packets']):
raise Exception("No keep-alive packets received")
try:
# Disable client keep-alive so that hostapd will verify connection
# with client poll
dev[0].request("SET no_keep_alive 1")
for i in range(60):
sta = hapd.get_sta(addr)
logger.info("timeout_next=%s rx_packets=%s tx_packets=%s" % (sta['timeout_next'], sta['rx_packets'], sta['tx_packets']))
if i > 1 and sta['timeout_next'] != "NULLFUNC POLL" and int(sta['tx_packets']) > int(end['tx_packets']):
break
ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected disconnection (client poll expected)")
finally:
dev[0].request("SET no_keep_alive 0")
if int(sta['tx_packets']) <= int(end['tx_packets']):
raise Exception("No client poll packet seen")
def test_wnm_bss_tm(dev, apdev):
"""WNM BSS Transition Management"""
try:
hapd = None
hapd2 = None
hapd = start_wnm_ap(apdev[0], country_code="FI")
id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
channel="36")
addr = dev[0].p2p_interface_addr()
dev[0].dump_monitor()
logger.info("No neighbor list entries")
if "OK" not in hapd.request("BSS_TM_REQ " + addr):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if addr not in ev:
raise Exception("Unexpected BSS Transition Management Response address")
if "status_code=0" in ev:
raise Exception("BSS transition accepted unexpectedly")
dev[0].dump_monitor()
logger.info("Neighbor list entry, but not claimed as Preferred Candidate List")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " neighbor=11:22:33:44:55:66,0x0000,81,3,7"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" in ev:
raise Exception("BSS transition accepted unexpectedly")
dev[0].dump_monitor()
logger.info("Preferred Candidate List (no matching neighbor) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=22:33:44:55:66:77,0x0000,1,44,7 neighbor=00:11:22:33:44:55,0x0000,81,4,7,03010a"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" in ev:
raise Exception("BSS transition accepted unexpectedly")
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
if ev is None:
raise Exception("No scan started")
dev[0].dump_monitor()
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
dev[0].wait_connected(timeout=15, error="No reassociation seen")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected reassociation target: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected scan started")
dev[0].dump_monitor()
logger.info("Preferred Candidate List with two matches, no roam needed")
if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected scan started")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected reassociation")
logger.info("Preferred Candidate List with two matches and extra frequency (160 MHz), no roam needed")
if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff" + ' neighbor=00:11:22:33:44:55,0x0000,129,36,7'):
raise Exception("BSS_TM_REQ command failed")
ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected scan started")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
if ev is not None:
raise Exception("Unexpected reassociation")
finally:
clear_regdom_state(dev, hapd, hapd2)
def test_wnm_bss_tm_steering_timeout(dev, apdev):
"""WNM BSS Transition Management and steering timeout"""
hapd = start_wnm_ap(apdev[0])
id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
hapd2 = start_wnm_ap(apdev[1])
dev[0].scan_for_bss(apdev[1]['bssid'], 2412)
hapd2.disable()
addr = dev[0].own_addr()
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,81,1,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=5)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
# Wait for the ap_sta_reset_steer_flag_timer timeout to occur
# "Reset steering flag for STA 02:00:00:00:00:00"
time.sleep(2.1)
ev = dev[0].wait_event(["Trying to authenticate"], timeout=5)
if ev is None:
raise Exception("No authentication attempt seen")
if hapd2.own_addr() not in ev:
raise Exception("Unexpected authentication target: " + ev)
# Wait for return back to the previous AP
dev[0].wait_connected()
def test_wnm_bss_tm_errors(dev, apdev):
"""WNM BSS Transition Management errors"""
hapd = start_wnm_ap(apdev[0])
id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
tests = ["BSS_TM_REQ q",
"BSS_TM_REQ 22:22:22:22:22:22",
"BSS_TM_REQ %s disassoc_timer=-1" % addr,
"BSS_TM_REQ %s disassoc_timer=65536" % addr,
"BSS_TM_REQ %s bss_term=foo" % addr,
"BSS_TM_REQ %s neighbor=q" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0,0,q" % addr,
"BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0,0,0q" % addr,
"BSS_TM_REQ " + addr + " url=" + 256*'a',
"BSS_TM_REQ %s url=foo mbo=1:2" % addr,
"BSS_TM_REQ %s url=foo mbo=100000:0:0" % addr,
"BSS_TM_REQ %s url=foo mbo=0:0:254" % addr,
"BSS_TM_REQ %s url=foo mbo=0:100000:0" % addr]
for t in tests:
if "FAIL" not in hapd.request(t):
raise Exception("Invalid command accepted: %s" % t)
with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_bss_tm_req"):
if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
raise Exception("BSS_TM_REQ accepted during OOM")
with alloc_fail(hapd, 1, "=wnm_send_bss_tm_req"):
if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
raise Exception("BSS_TM_REQ accepted during OOM")
with fail_test(hapd, 1, "wnm_send_bss_tm_req"):
if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
raise Exception("BSS_TM_REQ accepted during failure testing")
def test_wnm_bss_tm_termination(dev, apdev):
"""WNM BSS Transition Management and BSS termination"""
hapd = start_wnm_ap(apdev[0])
id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
if "OK" not in hapd.request("BSS_TM_REQ %s bss_term=0,1" % addr):
raise Exception("BSS_TM_REQ failed")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
if ev is None:
raise Exception("No BSS-TM-RESP event seen")
if "OK" not in hapd.request("BSS_TM_REQ %s url=http://example.com/" % addr):
raise Exception("BSS_TM_REQ failed")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
if ev is None:
raise Exception("No BSS-TM-RESP event seen")
def test_wnm_bss_tm_scan_not_needed(dev, apdev):
"""WNM BSS Transition Management and scan not needed"""
run_wnm_bss_tm_scan_not_needed(dev, apdev)
def test_wnm_bss_tm_nei_vht(dev, apdev):
"""WNM BSS Transition Management and VHT neighbor"""
run_wnm_bss_tm_scan_not_needed(dev, apdev, vht=True, nei_info="115,36,9")
def test_wnm_bss_tm_nei_11a(dev, apdev):
"""WNM BSS Transition Management and 11a neighbor"""
run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, nei_info="115,36,4")
def test_wnm_bss_tm_nei_11g(dev, apdev):
"""WNM BSS Transition Management and 11g neighbor"""
run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, hwmode='g',
channel='2', freq=2417, nei_info="81,2,6")
def test_wnm_bss_tm_nei_11b(dev, apdev):
"""WNM BSS Transition Management and 11g neighbor"""
run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, hwmode='b',
channel='3', freq=2422, nei_info="81,2,5")
def run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=True, vht=False, hwmode='a',
channel='36', freq=5180,
nei_info="115,36,7,0301ff"):
try:
hapd = None
hapd2 = None
hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
channel="1")
hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode=hwmode,
channel=channel, ht=ht, vht=vht)
dev[0].scan_for_bss(apdev[1]['bssid'], freq)
id = dev[0].connect("test-wnm", key_mgmt="NONE",
bssid=apdev[0]['bssid'], scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
dev[0].set_network(id, "bssid", "")
addr = dev[0].own_addr()
dev[0].dump_monitor()
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000," + nei_info):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
dev[0].wait_connected(timeout=15, error="No reassociation seen")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected reassociation target: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected scan started")
dev[0].dump_monitor()
finally:
clear_regdom_state(dev, hapd, hapd2)
def test_wnm_bss_tm_scan_needed(dev, apdev):
"""WNM BSS Transition Management and scan needed"""
try:
hapd = None
hapd2 = None
hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
channel="1")
hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
channel="36")
dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
id = dev[0].connect("test-wnm", key_mgmt="NONE",
bssid=apdev[0]['bssid'], scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
dev[0].set_network(id, "bssid", "")
addr = dev[0].own_addr()
dev[0].dump_monitor()
logger.info("Wait 11 seconds for the last scan result to be too old, but still present in BSS table")
time.sleep(11)
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
dev[0].wait_connected(timeout=15, error="No reassociation seen")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected reassociation target: " + ev)
ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected scan started")
dev[0].dump_monitor()
finally:
clear_regdom_state(dev, hapd, hapd2)
def test_wnm_bss_tm_scan_needed_e4(dev, apdev):
"""WNM BSS Transition Management and scan needed (Table E-4)"""
try:
hapd = None
hapd2 = None
hapd = start_wnm_ap(apdev[0], country_code="FI", country3="0x04",
hw_mode="g", channel="1")
hapd2 = start_wnm_ap(apdev[1], country_code="FI", country3="0x04",
hw_mode="a", channel="36")
id = dev[0].connect("test-wnm", key_mgmt="NONE",
bssid=apdev[0]['bssid'], scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
dev[0].set_network(id, "bssid", "")
addr = dev[0].own_addr()
dev[0].dump_monitor()
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=4)
if ev is None:
raise Exception("No BSS Transition Management Response seen quickly enough - did scan optimization fail?")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
dev[0].wait_connected(timeout=15, error="No reassociation seen")
# Wait for regdom change due to country IE to avoid issues with that
# processing happening only after the disconnection and cfg80211 ending
# up intersecting regdoms when we try to clear state back to world (00)
# regdom below.
while True:
ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
if not ev or "COUNTRY_IE" in ev:
break
dev[0].dump_monitor()
finally:
clear_regdom_state(dev, hapd, hapd2)
def start_wnm_tm(ap, country, dev, country3=None):
hapd = start_wnm_ap(ap, country_code=country, country3=country3)
id = dev.connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
wait_regdom_changes(dev)
dev.dump_monitor()
dev.set_network(id, "scan_freq", "")
return hapd, id
def stop_wnm_tm(hapd, dev):
if hapd:
hapd.request("DISABLE")
time.sleep(0.1)
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
wait_regdom_changes(dev[0])
country = dev[0].get_driver_status_field("country")
logger.info("Country code at the end: " + country)
if country != "00":
clear_country(dev)
dev[0].flush_scan_cache()
def wnm_bss_tm_check(hapd, dev, data):
addr = dev.p2p_interface_addr()
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " " + data):
raise Exception("BSS_TM_REQ command failed")
ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
if ev is None:
raise Exception("No scan started")
ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
if ev is None:
raise Exception("Scan did not complete")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=7" not in ev:
raise Exception("Unexpected response: " + ev)
def test_wnm_bss_tm_country_us(dev, apdev):
"""WNM BSS Transition Management (US)"""
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
logger.info("Preferred Candidate List (no matching neighbor, known channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,2,52,7,03010a neighbor=00:11:22:33:44:57,0x0000,4,100,7 neighbor=00:11:22:33:44:59,0x0000,3,149,7 neighbor=00:11:22:33:44:5b,0x0000,34,1,7 neighbor=00:11:22:33:44:5d,0x0000,5,149,7")
# Make the test take less time by limiting full scans
dev[0].set_network(id, "scan_freq", "2412")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,12,12,7 neighbor=00:11:22:33:44:55,0x0000,2,35,7,03010a neighbor=00:11:22:33:44:56,0x0000,2,65,7 neighbor=00:11:22:33:44:57,0x0000,4,99,7 neighbor=00:11:22:33:44:58,0x0000,4,145,7")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,162,7 neighbor=00:11:22:33:44:5b,0x0000,34,0,7 neighbor=00:11:22:33:44:5c,0x0000,34,4,7 neighbor=00:11:22:33:44:5d,0x0000,5,148,7 neighbor=00:11:22:33:44:5e,0x0000,5,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_country_fi(dev, apdev):
"""WNM BSS Transition Management (FI)"""
addr = dev[0].p2p_interface_addr()
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], "FI", dev[0])
logger.info("Preferred Candidate List (no matching neighbor, known channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,4,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,100,7 neighbor=00:11:22:33:44:59,0x0000,17,149,7 neighbor=00:11:22:33:44:5c,0x0000,18,1,7")
# Make the test take less time by limiting full scans
dev[0].set_network(id, "scan_freq", "2412")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,4,0,7 neighbor=00:11:22:33:44:01,0x0000,4,14,7 neighbor=00:11:22:33:44:02,0x0000,1,35,7 neighbor=00:11:22:33:44:03,0x0000,1,65,7 neighbor=00:11:22:33:44:04,0x0000,3,99,7 neighbor=00:11:22:33:44:05,0x0000,3,141,7 neighbor=00:11:22:33:44:06,0x0000,17,148,7 neighbor=00:11:22:33:44:07,0x0000,17,170,7 neighbor=00:11:22:33:44:08,0x0000,18,0,7 neighbor=00:11:22:33:44:09,0x0000,18,5,7")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,0,0,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_country_jp(dev, apdev):
"""WNM BSS Transition Management (JP)"""
addr = dev[0].p2p_interface_addr()
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], "JP", dev[0])
logger.info("Preferred Candidate List (no matching neighbor, known channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,31,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,1,36,7 neighbor=00:11:22:33:44:59,0x0000,34,100,7 neighbor=00:11:22:33:44:5c,0x0000,59,1,7")
# Make the test take less time by limiting full scans
dev[0].set_network(id, "scan_freq", "2412")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,30,14,7 neighbor=00:11:22:33:44:56,0x0000,31,13,7 neighbor=00:11:22:33:44:57,0x0000,1,33,7 neighbor=00:11:22:33:44:58,0x0000,1,65,7 neighbor=00:11:22:33:44:5a,0x0000,34,99,7 neighbor=00:11:22:33:44:5b,0x0000,34,141,7 neighbor=00:11:22:33:44:5d,0x0000,59,0,7 neighbor=00:11:22:33:44:5e,0x0000,59,4,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_country_cn(dev, apdev):
"""WNM BSS Transition Management (CN)"""
addr = dev[0].p2p_interface_addr()
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], "CN", dev[0])
logger.info("Preferred Candidate List (no matching neighbor, known channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,149,7 neighbor=00:11:22:33:44:59,0x0000,6,149,7")
# Make the test take less time by limiting full scans
dev[0].set_network(id, "scan_freq", "2412")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,7,14,7 neighbor=00:11:22:33:44:56,0x0000,1,35,7 neighbor=00:11:22:33:44:57,0x0000,1,65,7 neighbor=00:11:22:33:44:58,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_global(dev, apdev):
"""WNM BSS Transition Management (global)"""
run_wnm_bss_tm_global(dev, apdev, "XX", None)
def test_wnm_bss_tm_global4(dev, apdev):
"""WNM BSS Transition Management (global; indicate table E-4)"""
run_wnm_bss_tm_global(dev, apdev, "FI", "0x04")
def run_wnm_bss_tm_global(dev, apdev, country, country3):
addr = dev[0].p2p_interface_addr()
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], country, dev[0], country3=country3)
logger.info("Preferred Candidate List (no matching neighbor, known channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,82,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,83,1,7 neighbor=00:11:22:33:44:59,0x0000,115,36,7 neighbor=00:11:22:33:44:5a,0x0000,121,100,7 neighbor=00:11:22:33:44:5c,0x0000,124,149,7 neighbor=00:11:22:33:44:5d,0x0000,125,149,7 neighbor=00:11:22:33:44:5e,0x0000,128,42,7 neighbor=00:11:22:33:44:5f,0x0000,129,50,7 neighbor=00:11:22:33:44:60,0x0000,180,1,7")
# Make the test take less time by limiting full scans
dev[0].set_network(id, "scan_freq", "2412")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,81,0,7 neighbor=00:11:22:33:44:01,0x0000,81,14,7 neighbor=00:11:22:33:44:02,0x0000,82,13,7 neighbor=00:11:22:33:44:03,0x0000,83,0,7 neighbor=00:11:22:33:44:04,0x0000,83,14,7 neighbor=00:11:22:33:44:05,0x0000,115,35,7 neighbor=00:11:22:33:44:06,0x0000,115,65,7 neighbor=00:11:22:33:44:07,0x0000,121,99,7 neighbor=00:11:22:33:44:08,0x0000,121,141,7 neighbor=00:11:22:33:44:09,0x0000,124,148,7")
logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,124,162,7 neighbor=00:11:22:33:44:01,0x0000,125,148,7 neighbor=00:11:22:33:44:02,0x0000,125,170,7 neighbor=00:11:22:33:44:03,0x0000,128,35,7 neighbor=00:11:22:33:44:04,0x0000,128,162,7 neighbor=00:11:22:33:44:05,0x0000,129,49,7 neighbor=00:11:22:33:44:06,0x0000,129,115,7 neighbor=00:11:22:33:44:07,0x0000,180,0,7 neighbor=00:11:22:33:44:08,0x0000,180,5,7 neighbor=00:11:22:33:44:09,0x0000,0,0,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_op_class_0(dev, apdev):
"""WNM BSS Transition Management with invalid operating class"""
try:
hapd = None
hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
logger.info("Preferred Candidate List (no matching neighbor, invalid op class specified for channels)")
wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,0,149,7 neighbor=00:11:22:33:44:5b,0x0000,0,1,7")
finally:
stop_wnm_tm(hapd, dev)
def test_wnm_bss_tm_rsn(dev, apdev):
"""WNM BSS Transition Management with RSN"""
passphrase = "zxcvbnm,.-"
try:
hapd = None
hapd2 = None
hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
channel="1",
rsn=True, pmf=False, passphrase=passphrase)
hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
channel="36",
rsn=True, pmf=False, passphrase=passphrase)
dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
id = dev[0].connect("test-wnm-rsn", psk=passphrase,
bssid=apdev[0]['bssid'], scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
dev[0].set_network(id, "bssid", "")
addr = dev[0].own_addr()
dev[0].dump_monitor()
time.sleep(0.5)
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000," + "115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
dev[0].wait_connected(timeout=15, error="No reassociation seen")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected reassociation target: " + ev)
finally:
clear_regdom_state(dev, hapd, hapd2)
def test_wnm_action_proto(dev, apdev):
"""WNM Action protocol testing"""
hapd = start_wnm_ap(apdev[0], bss_transition=False, wnm_sleep_mode=True)
bssid = apdev[0]['bssid']
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].request("WNM_SLEEP enter")
time.sleep(0.1)
hapd.set("ext_mgmt_frame_handling", "1")
msg = {}
msg['fc'] = MGMT_SUBTYPE_ACTION << 4
msg['da'] = dev[0].own_addr()
msg['sa'] = bssid
msg['bssid'] = bssid
dialog_token = 1
logger.debug("Unexpected WNM-Notification Response")
# Note: This is actually not registered for user space processing in
# driver_nl80211.c nl80211_mgmt_subscribe_non_ap() and as such, won't make
# it to wpa_supplicant.
msg['payload'] = struct.pack("<BBBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_RESP,
dialog_token, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("Truncated WNM-Notification Request (no Type field)")
msg['payload'] = struct.pack("<BBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated IE (min)")
msg['payload'] = struct.pack("<BBBBBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0, 1)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated IE (max)")
msg['payload'] = struct.pack("<BBBBBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0, 255)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with too short IE")
msg['payload'] = struct.pack("<BBBBBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL")
msg['payload'] = struct.pack(">BBBBBBLB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
0x506f9a00, 1)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(2)")
msg['payload'] = struct.pack(">BBBBBBLBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 6,
0x506f9a00, 1, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(3)")
msg['payload'] = struct.pack(">BBBBBBLB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
0x506f9a00, 0xff)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(min)")
msg['payload'] = struct.pack(">BBBBBBLBHB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
0x506f9a01, 0, 0, 1)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(max)")
msg['payload'] = struct.pack(">BBBBBBLBHB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
0x506f9a01, 0, 0, 0xff)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WFA WNM-Notification Request with unsupported IE")
msg['payload'] = struct.pack("<BBBBBBL",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 4, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM-Notification Request with unknown WNM-Notification type 0")
msg['payload'] = struct.pack("<BBBB",
ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
dialog_token, WNM_NOTIF_TYPE_FW_UPGRADE)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("Truncated WNM Sleep Mode Response - no Dialog Token")
msg['payload'] = struct.pack("<BB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("Truncated WNM Sleep Mode Response - no Key Data Length")
msg['payload'] = struct.pack("<BBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (min)")
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
1)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (max)")
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0xffff)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - truncated IE header")
msg['payload'] = struct.pack("<BBBHB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - truncated IE")
msg['payload'] = struct.pack("<BBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, 0, 1)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Empty TFS Response")
msg['payload'] = struct.pack("<BBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - EID 0 not recognized")
msg['payload'] = struct.pack("<BBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, 0, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Empty WNM Sleep Mode element and TFS Response element")
msg['payload'] = struct.pack("<BBBHBBBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, WLAN_EID_WNMSLEEP, 0, WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element and empty TFS Response element")
msg['payload'] = struct.pack("<BBBHBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(exit, deny key) and empty TFS Response element")
msg['payload'] = struct.pack("<BBBHBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_DENIED_KEY, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(enter, deny key) and empty TFS Response element")
msg['payload'] = struct.pack("<BBBHBBBBHBB",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
WNM_STATUS_DENIED_KEY, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
@remote_compatible
def test_wnm_action_proto_pmf(dev, apdev):
"""WNM Action protocol testing (PMF enabled)"""
ssid = "test-wnm-pmf"
hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, ssid=ssid)
bssid = apdev[0]['bssid']
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256",
proto="WPA2", ieee80211w="2", scan_freq="2412")
dev[0].request("WNM_SLEEP enter")
time.sleep(0.1)
hapd.set("ext_mgmt_frame_handling", "1")
msg = {}
msg['fc'] = MGMT_SUBTYPE_ACTION << 4
msg['da'] = dev[0].own_addr()
msg['sa'] = bssid
msg['bssid'] = bssid
logger.debug("WNM Sleep Mode Response - Invalid Key Data element length")
keydata = struct.pack("<BB", 0, 1)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Too short GTK subelem")
keydata = struct.pack("<BB", WNM_SLEEP_SUBELEM_GTK, 0)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Invalid GTK subelem")
keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
0, 17, 0, 0, 0, 0, 0, 0)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Invalid GTK subelem (2)")
keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
0, 0, 0, 0, 0, 0, 0, 0)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - GTK subelem and too short IGTK subelem")
keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
keydata += struct.pack("<BB", WNM_SLEEP_SUBELEM_IGTK, 0)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
logger.debug("WNM Sleep Mode Response - Unknown subelem")
keydata = struct.pack("<BB", 255, 0)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
@remote_compatible
def test_wnm_action_proto_no_pmf(dev, apdev):
"""WNM Action protocol testing (PMF disabled)"""
ssid = "test-wnm-no-pmf"
hapd = start_wnm_ap(apdev[0], rsn=True, pmf=False, bss_transition=False,
wnm_sleep_mode=True, ssid=ssid)
bssid = apdev[0]['bssid']
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
proto="WPA2", ieee80211w="0", scan_freq="2412")
dev[0].request("WNM_SLEEP enter")
time.sleep(0.1)
hapd.set("ext_mgmt_frame_handling", "1")
hapd.dump_monitor()
dev[0].request("WNM_SLEEP exit")
ev = hapd.wait_event(['MGMT-RX'], timeout=5)
if ev is None:
raise Exception("WNM-Sleep Mode Request not seen")
msg = {}
msg['fc'] = MGMT_SUBTYPE_ACTION << 4
msg['da'] = dev[0].own_addr()
msg['sa'] = bssid
msg['bssid'] = bssid
logger.debug("WNM Sleep Mode Response - GTK subelem and IGTK subelem")
keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
keydata += struct.pack("<BBHLH4L", WNM_SLEEP_SUBELEM_IGTK, 2 + 6 + 16, 0,
0x10203040, 0x5060,
0xf1f2f3f4, 0xf5f6f7f8, 0xf9f0fafb, 0xfcfdfeff)
msg['payload'] = struct.pack("<BBBH",
ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
len(keydata))
msg['payload'] += keydata
msg['payload'] += struct.pack("<BBBBHBB",
WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
WNM_STATUS_SLEEP_ACCEPT, 0,
WLAN_EID_TFS_RESP, 0)
hapd.mgmt_tx(msg)
expect_ack(hapd)
ev = dev[0].wait_event(["WNM: Ignore Key Data"], timeout=5)
if ev is None:
raise Exception("Key Data not ignored")
def test_wnm_bss_tm_req_with_mbo_ie(dev, apdev):
"""WNM BSS transition request with MBO IE and reassociation delay attribute"""
ssid = "test-wnm-mbo"
hapd = start_wnm_ap(apdev[0], rsn=True, pmf=False, ssid=ssid)
bssid = apdev[0]['bssid']
if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
raise Exception("Failed to set STA as cellular data capable")
dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
proto="WPA2", ieee80211w="0", scan_freq="2412")
logger.debug("BTM request with MBO reassociation delay when disassoc imminent is not set")
if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=3:2:1"):
raise Exception("BSS transition management succeeded unexpectedly")
logger.debug("BTM request with invalid MBO transition reason code")
if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=10:2:1"):
raise Exception("BSS transition management succeeded unexpectedly")
logger.debug("BTM request with MBO reassociation retry delay of 5 seconds")
if 'OK' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " disassoc_imminent=1 disassoc_timer=3 mbo=3:5:1"):
raise Exception("BSS transition management command failed")
ev = dev[0].wait_event(['MBO-CELL-PREFERENCE'], 1)
if ev is None or "preference=1" not in ev:
raise Exception("Timeout waiting for MBO-CELL-PREFERENCE event")
ev = dev[0].wait_event(['MBO-TRANSITION-REASON'], 1)
if ev is None or "reason=3" not in ev:
raise Exception("Timeout waiting for MBO-TRANSITION-REASON event")
t0 = datetime.now()
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if dev[0].own_addr() not in ev:
raise Exception("Unexpected BSS Transition Management Response address")
ev = dev[0].wait_event(['CTRL-EVENT-DISCONNECTED'], 5)
if ev is None:
raise Exception("Station did not disconnect although disassoc imminent was set")
# Set the scan interval to make dev[0] look for connections
if 'OK' not in dev[0].request("SCAN_INTERVAL 1"):
raise Exception("Failed to set scan interval")
# Wait until connected
ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED'], 10)
if ev is None:
raise Exception("Station did not connect")
# Make sure no connection is made during the retry delay
time_diff = datetime.now() - t0
if time_diff.total_seconds() < 5:
raise Exception("Station connected before assoc retry delay was over")
if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
raise Exception("Failed to set STA as cellular data not-capable")
@remote_compatible
def test_wnm_bss_transition_mgmt_query(dev, apdev):
"""WNM BSS Transition Management query"""
hapd = start_wnm_ap(apdev[0])
params = {"ssid": "another"}
hapd2 = hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], 2412)
dev[0].scan_for_bss(apdev[0]['bssid'], 2412)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].request("WNM_BSS_QUERY 0 list")
ev = dev[0].wait_event(["WNM: BSS Transition Management Request"],
timeout=5)
if ev is None:
raise Exception("No BSS Transition Management Request frame seen")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
if ev is None:
raise Exception("No BSS Transition Management Response frame seen")
def test_wnm_bss_transition_mgmt_query_disabled_on_ap(dev, apdev):
"""WNM BSS Transition Management query - TM disabled on AP"""
hapd = start_wnm_ap(apdev[0], bss_transition=False)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
# Ignore BSS Transition Management Query from 02:00:00:00:00:00 since BSS Transition Management is disabled
dev[0].request("WNM_BSS_QUERY 0 list")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=0.1)
if ev is not None:
raise Exception("Unexpected BSS TM Response reported")
def test_wnm_bss_transition_mgmt_query_mbo(dev, apdev):
"""WNM BSS Transition Management query - TM only due to MBO on AP"""
hapd = start_wnm_ap(apdev[0], bss_transition=False, mbo=True)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].request("WNM_BSS_QUERY 0 list")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
if ev is None:
raise Exception("No BSS TM Response reported")
@remote_compatible
def test_wnm_bss_tm_security_mismatch(dev, apdev):
"""WNM BSS Transition Management and security mismatch"""
hapd = start_wnm_ap(apdev[0], hw_mode="g", channel="1", ssid="test-wnm",
rsn=True, pmf=False)
hapd2 = start_wnm_ap(apdev[1], hw_mode="g", channel="11")
dev[0].scan_for_bss(apdev[1]['bssid'], 2462)
id = dev[0].connect("test-wnm", psk="12345678",
bssid=apdev[0]['bssid'], scan_freq="2412")
dev[0].set_network(id, "scan_freq", "")
dev[0].set_network(id, "bssid", "")
addr = dev[0].own_addr()
dev[0].dump_monitor()
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=7" not in ev:
raise Exception("Unexpected BSS transition request response: " + ev)
def test_wnm_bss_tm_connect_cmd(dev, apdev):
"""WNM BSS Transition Management and cfg80211 connect command"""
hapd = start_wnm_ap(apdev[0], hw_mode="g", channel="1")
hapd2 = start_wnm_ap(apdev[1], hw_mode="g", channel="11")
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
wpas.scan_for_bss(apdev[1]['bssid'], 2462)
id = wpas.connect("test-wnm", key_mgmt="NONE",
bssid=apdev[0]['bssid'], scan_freq="2412")
wpas.set_network(id, "scan_freq", "")
wpas.set_network(id, "bssid", "")
addr = wpas.own_addr()
wpas.dump_monitor()
logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if "status_code=0" not in ev:
raise Exception("BSS transition request was not accepted: " + ev)
if "target_bssid=" + apdev[1]['bssid'] not in ev:
raise Exception("Unexpected target BSS: " + ev)
ev = wpas.wait_event(["CTRL-EVENT-CONNECTED",
"CTRL-EVENT-DISCONNECTED"], timeout=10)
if ev is None:
raise Exception("No reassociation seen")
if "CTRL-EVENT-DISCONNECTED" in ev:
raise Exception("Unexpected disconnection reported")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected reassociation target: " + ev)
def test_wnm_bss_tm_reject(dev, apdev):
"""WNM BSS Transition Management request getting rejected"""
try:
hapd = None
hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
channel="1")
id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
dev[0].dump_monitor()
if "OK" not in dev[0].request("SET reject_btm_req_reason 123"):
raise Exception("Failed to set reject_btm_req_reason")
if "OK" not in hapd.request("BSS_TM_REQ " + addr + " disassoc_timer=1"):
raise Exception("BSS_TM_REQ command failed")
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
if ev is None:
raise Exception("No BSS Transition Management Response")
if addr not in ev:
raise Exception("Unexpected BSS Transition Management Response address")
if "status_code=123" not in ev:
raise Exception("Unexpected BSS Transition Management Response status: " + ev)
dev[0].wait_disconnected()
dev[0].wait_connected()
finally:
if hapd:
hapd.request("DISABLE")
dev[0].disconnect_and_stop_scan()
subprocess.call(['iw', 'reg', 'set', '00'])
dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
dev[0].flush_scan_cache()
def test_wnm_bss_tm_ap_proto(dev, apdev):
"""WNM BSS TM - protocol testing for AP message parsing"""
hapd = start_wnm_ap(apdev[0])
bssid = hapd.own_addr()
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
tests = ["0a",
"0a06",
"0a0601",
"0a060100",
"0a080000",
"0a08000000",
"0a080000001122334455",
"0a08000000112233445566",
"0a08000000112233445566112233445566778899",
"0a08ffffff",
"0a08ffffff112233445566778899",
"0a1a",
"0a1a00",
"0a1a0000",
"0a0c016015007f0f000000000000000000000000000000000000",
"0a0700",
"0aff00",
"0aff"]
for t in tests:
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
hapd.set("ext_mgmt_frame_handling", "0")
def test_wnm_bss_transition_mgmt_query_with_unknown_candidates(dev, apdev):
"""WNM BSS Transition Management query with unknown candidates"""
hapd = start_wnm_ap(apdev[0])
dev[0].scan_for_bss(apdev[0]['bssid'], 2412)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
dev[0].request("WNM_BSS_QUERY 0 neighbor=00:11:22:33:44:55,0,81,1,4")
ev = dev[0].wait_event(["WNM: BSS Transition Management Request"],
timeout=5)
if ev is None:
raise Exception("No BSS Transition Management Request frame seen")
ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
if ev is None:
raise Exception("No BSS Transition Management Response frame seen")
def test_wnm_time_adv_without_time_zone(dev, apdev):
"""WNM Time Advertisement without time zone configuration"""
params = {"ssid": "test-wnm",
"time_advertisement": "2"}
hostapd.add_ap(apdev[0], params)
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
def test_wnm_coloc_intf_reporting(dev, apdev):
"""WNM Collocated Interference Reporting"""
hapd = start_wnm_ap(apdev[0], bss_transition=False,
coloc_intf_reporting=True)
no_intf = struct.pack("<BBBBBLLLLH", 96, 21, 0, 127, 0x0f, 0, 0, 0, 0, 0)
try:
dev[0].set("coloc_intf_reporting", "1")
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
if "OK" not in hapd.request("COLOC_INTF_REQ %s 1 5" % addr):
raise Exception("Could not send Collocated Interference Request")
ev = dev[0].wait_event(["COLOC-INTF-REQ"], timeout=2)
if ev is None:
raise Exception("No Collocated Interference Request frame seen")
vals = ev.split(' ')
if vals[2] != '1' or vals[3] != '5':
raise Exception("Unexpected request values: " + ev)
dev[0].set("coloc_intf_elems", binascii.hexlify(no_intf).decode())
ev = hapd.wait_event(["COLOC-INTF-REPORT"], timeout=1)
if ev is None:
raise Exception("No Collocated Interference Report frame seen")
if addr + " 1 " + binascii.hexlify(no_intf).decode() not in ev:
raise Exception("Unexpected report values: " + ev)
if "OK" not in hapd.request("COLOC_INTF_REQ %s 0 0" % addr):
raise Exception("Could not send Collocated Interference Request")
ev = dev[0].wait_event(["COLOC-INTF-REQ"], timeout=2)
if ev is None:
raise Exception("No Collocated Interference Request frame seen")
vals = ev.split(' ')
if vals[2] != '0' or vals[3] != '0':
raise Exception("Unexpected request values: " + ev)
res = dev[0].request("COLOC_INTF_REPORT " + binascii.hexlify(no_intf).decode())
if "OK" not in res:
raise Exception("Could not send unsolicited report")
ev = hapd.wait_event(["COLOC-INTF-REPORT"], timeout=1)
if ev is None:
raise Exception("No Collocated Interference Report frame seen")
if addr + " 0 " + binascii.hexlify(no_intf).decode() not in ev:
raise Exception("Unexpected report values: " + ev)
if "FAIL" not in hapd.request("COLOC_INTF_REQ foo 1 5"):
raise Exception("Invalid COLOC_INTF_REQ accepted")
if "FAIL" not in hapd.request("COLOC_INTF_REQ 02:ff:ff:ff:ff:ff 1 5"):
raise Exception("COLOC_INTF_REQ for unknown STA accepted")
if "FAIL" not in hapd.request("COLOC_INTF_REQ %s 1" % addr):
raise Exception("Invalid COLOC_INTF_REQ accepted")
if "FAIL" not in hapd.request("COLOC_INTF_REQ %s" % addr):
raise Exception("Invalid COLOC_INTF_REQ accepted")
finally:
dev[0].set("coloc_intf_reporting", "0")
dev[0].set("coloc_intf_elems", "")
def test_wnm_coloc_intf_reporting_errors(dev, apdev):
"""WNM Collocated Interference Reporting errors"""
hapd = start_wnm_ap(apdev[0], bss_transition=False,
coloc_intf_reporting=True)
bssid = hapd.own_addr()
dev[0].set("coloc_intf_reporting", "1")
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
if "FAIL" not in hapd.request("COLOC_INTF_REQ %s 4 5" % addr):
raise Exception("Invalid Collocated Interference Request accepted")
hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
hapd.set("ext_mgmt_frame_handling", "1")
tests = ["0a0c016015007f0f000000000000000000000000000000000000",
"0a0c"]
with alloc_fail(hapd, 1, "ieee802_11_rx_wnm_coloc_intf_report"):
for t in tests:
if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
raise Exception("MGMT_RX_PROCESS failed")
hapd.set("ext_mgmt_frame_handling", "0")
def test_wnm_bss_transition_mgmt_disabled(dev, apdev):
"""WNM BSS Transition Management disabled"""
hapd = start_wnm_ap(apdev[0])
try:
dev[0].set("disable_btm", "1")
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
addr = dev[0].own_addr()
hapd.request("BSS_TM_REQ " + addr)
ev = hapd.wait_event(['BSS-TM-RESP'], timeout=0.5)
if ev is not None:
raise Exception("Unexpected BSS Transition Management Response")
finally:
dev[0].set("disable_btm", "0")
def test_wnm_time_adv_restart(dev, apdev):
"""WNM time advertisement and interface restart"""
hapd = start_wnm_ap(apdev[0], time_adv=True)
hapd.disable()
hapd.enable()
dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
diff --git a/tests/hwsim/test_wpas_ctrl.py b/tests/hwsim/test_wpas_ctrl.py
index a99e2f49af83..210c11907ee7 100644
--- a/tests/hwsim/test_wpas_ctrl.py
+++ b/tests/hwsim/test_wpas_ctrl.py
@@ -1,2149 +1,2159 @@
# wpa_supplicant control interface
# Copyright (c) 2014, Qualcomm Atheros, Inc.
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
from remotehost import remote_compatible
import logging
logger = logging.getLogger()
import os
import socket
import subprocess
import time
import binascii
import hostapd
import hwsim_utils
from hwsim import HWSimRadio
from wpasupplicant import WpaSupplicant
from utils import *
from test_wpas_ap import wait_ap_ready
@remote_compatible
def test_wpas_ctrl_network(dev):
"""wpa_supplicant ctrl_iface network set/get"""
skip_without_tkip(dev[0])
id = dev[0].add_network()
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id)):
raise Exception("Unexpected success for invalid SET_NETWORK")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " name"):
raise Exception("Unexpected success for invalid SET_NETWORK")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id + 1) + " proto OPEN"):
raise Exception("Unexpected success for invalid network id")
if "FAIL" not in dev[0].request("GET_NETWORK " + str(id)):
raise Exception("Unexpected success for invalid GET_NETWORK")
if "FAIL" not in dev[0].request("GET_NETWORK " + str(id + 1) + " proto"):
raise Exception("Unexpected success for invalid network id")
if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " proto \t WPA2 "):
raise Exception("Unexpected failure for SET_NETWORK proto")
res = dev[0].request("GET_NETWORK " + str(id) + " proto")
if res != "RSN":
raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for proto: " + res)
if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " key_mgmt \t WPA-PSK "):
raise Exception("Unexpected success for SET_NETWORK key_mgmt")
res = dev[0].request("GET_NETWORK " + str(id) + " key_mgmt")
if res != "WPA-PSK":
raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for key_mgmt: " + res)
if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " auth_alg \t OPEN "):
raise Exception("Unexpected failure for SET_NETWORK auth_alg")
res = dev[0].request("GET_NETWORK " + str(id) + " auth_alg")
if res != "OPEN":
raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for auth_alg: " + res)
if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " eap \t TLS "):
raise Exception("Unexpected failure for SET_NETWORK eap")
res = dev[0].request("GET_NETWORK " + str(id) + " eap")
if res != "TLS":
raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for eap: " + res)
tests = ("bssid foo", "key_mgmt foo", "key_mgmt ", "group NONE")
for t in tests:
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " " + t):
raise Exception("Unexpected success for invalid SET_NETWORK: " + t)
tests = [("key_mgmt", "WPA-PSK WPA-EAP IEEE8021X NONE WPA-NONE FT-PSK FT-EAP WPA-PSK-SHA256 WPA-EAP-SHA256"),
("pairwise", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
("group", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
("auth_alg", "OPEN SHARED LEAP"),
("scan_freq", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"),
("freq_list", "2412 2417"),
("scan_ssid", "1"),
("bssid", "00:11:22:33:44:55"),
("proto", "WPA RSN OSEN"),
("eap", "TLS"),
("go_p2p_dev_addr", "22:33:44:55:66:aa"),
("p2p_client_list", "22:33:44:55:66:bb 02:11:22:33:44:55")]
if "SAE" not in dev[0].get_capability("auth_alg"):
tests.append(("key_mgmt", "WPS OSEN"))
else:
tests.append(("key_mgmt", "WPS SAE FT-SAE OSEN"))
dev[0].set_network_quoted(id, "ssid", "test")
for field, value in tests:
dev[0].set_network(id, field, value)
res = dev[0].get_network(id, field)
if res != value:
raise Exception("Unexpected response for '" + field + "': '" + res + "'")
try:
value = "WPA-EAP-SUITE-B WPA-EAP-SUITE-B-192"
dev[0].set_network(id, "key_mgmt", value)
res = dev[0].get_network(id, "key_mgmt")
if res != value:
raise Exception("Unexpected response for key_mgmt")
except Exception as e:
if str(e).startswith("Unexpected"):
raise
else:
pass
q_tests = (("identity", "hello"),
("anonymous_identity", "foo@nowhere.com"))
for field, value in q_tests:
dev[0].set_network_quoted(id, field, value)
res = dev[0].get_network(id, field)
if res != '"' + value + '"':
raise Exception("Unexpected quoted response for '" + field + "': '" + res + "'")
get_tests = (("foo", None), ("ssid", '"test"'))
for field, value in get_tests:
res = dev[0].get_network(id, field)
if res != value:
raise Exception("Unexpected response for '" + field + "': '" + res + "'")
if dev[0].get_network(id, "password"):
raise Exception("Unexpected response for 'password'")
dev[0].set_network_quoted(id, "password", "foo")
if dev[0].get_network(id, "password") != '*':
raise Exception("Unexpected response for 'password' (expected *)")
dev[0].set_network(id, "password", "hash:12345678901234567890123456789012")
if dev[0].get_network(id, "password") != '*':
raise Exception("Unexpected response for 'password' (expected *)")
dev[0].set_network(id, "password", "NULL")
if dev[0].get_network(id, "password"):
raise Exception("Unexpected response for 'password'")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:12"):
raise Exception("Unexpected success for invalid password hash")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:123456789012345678x0123456789012"):
raise Exception("Unexpected success for invalid password hash")
dev[0].set_network(id, "identity", "414243")
if dev[0].get_network(id, "identity") != '"ABC"':
raise Exception("Unexpected identity hex->text response")
dev[0].set_network(id, "identity", 'P"abc\ndef"')
if dev[0].get_network(id, "identity") != "6162630a646566":
raise Exception("Unexpected identity printf->hex response")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity P"foo'):
raise Exception("Unexpected success for invalid identity string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity 12x3'):
raise Exception("Unexpected success for invalid identity string")
if "WEP40" in dev[0].get_capability("group"):
for i in range(0, 4):
if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' wep_key' + str(i) + ' aabbccddee'):
raise Exception("Unexpected wep_key set failure")
if dev[0].get_network(id, "wep_key" + str(i)) != '*':
raise Exception("Unexpected wep_key get failure")
if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
raise Exception("Unexpected failure for psk_list string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list 00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
raise Exception("Unexpected success for invalid psk_list string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
raise Exception("Unexpected success for invalid psk_list string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55+0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
raise Exception("Unexpected success for invalid psk_list string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde'):
raise Exception("Unexpected success for invalid psk_list string")
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdex'):
raise Exception("Unexpected success for invalid psk_list string")
if dev[0].get_network(id, "psk_list"):
raise Exception("Unexpected psk_list get response")
if dev[0].list_networks()[0]['ssid'] != "test":
raise Exception("Unexpected ssid in LIST_NETWORKS")
dev[0].set_network(id, "ssid", "NULL")
if dev[0].list_networks()[0]['ssid'] != "":
raise Exception("Unexpected ssid in LIST_NETWORKS after clearing it")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' ssid "0123456789abcdef0123456789abcdef0"'):
raise Exception("Too long SSID accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid qwerty'):
raise Exception("Invalid integer accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid 2'):
raise Exception("Too large integer accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk 12345678'):
raise Exception("Invalid PSK accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567"'):
raise Exception("Too short PSK accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567890123456789012345678901234567890123456789012345678901234"'):
raise Exception("Too long PSK accepted")
dev[0].set_network_quoted(id, "psk", "123456768")
dev[0].set_network_quoted(id, "psk", "123456789012345678901234567890123456789012345678901234567890123")
if dev[0].get_network(id, "psk") != '*':
raise Exception("Unexpected psk read result")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' eap UNKNOWN'):
raise Exception("Unknown EAP method accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' password "foo'):
raise Exception("Invalid password accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "foo'):
raise Exception("Invalid WEP key accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "12345678901234567"'):
raise Exception("Too long WEP key accepted")
if "WEP40" in dev[0].get_capability("group"):
# too short WEP key is ignored
dev[0].set_network_quoted(id, "wep_key0", "1234")
dev[0].set_network_quoted(id, "wep_key1", "12345")
dev[0].set_network_quoted(id, "wep_key2", "1234567890123")
dev[0].set_network_quoted(id, "wep_key3", "1234567890123456")
dev[0].set_network(id, "go_p2p_dev_addr", "any")
if dev[0].get_network(id, "go_p2p_dev_addr") is not None:
raise Exception("Unexpected go_p2p_dev_addr value")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' go_p2p_dev_addr 00:11:22:33:44'):
raise Exception("Invalid go_p2p_dev_addr accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44'):
raise Exception("Invalid p2p_client_list accepted")
if "FAIL" in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44:55 00:1'):
raise Exception("p2p_client_list truncation workaround failed")
if dev[0].get_network(id, "p2p_client_list") != "00:11:22:33:44:55":
raise Exception("p2p_client_list truncation workaround did not work")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg '):
raise Exception("Empty auth_alg accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg FOO'):
raise Exception("Invalid auth_alg accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto '):
raise Exception("Empty proto accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto FOO'):
raise Exception("Invalid proto accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise '):
raise Exception("Empty pairwise accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise FOO'):
raise Exception("Invalid pairwise accepted")
if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise WEP40'):
raise Exception("Invalid pairwise accepted")
if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44:55'):
raise Exception("Unexpected BSSID failure")
if dev[0].request("GET_NETWORK 0 bssid") != '00:11:22:33:44:55':
raise Exception("BSSID command did not set network bssid")
if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:00:00:00:00:00'):
raise Exception("Unexpected BSSID failure")
if "FAIL" not in dev[0].request("GET_NETWORK 0 bssid"):
raise Exception("bssid claimed configured after clearing")
if "FAIL" not in dev[0].request('BSSID 123 00:11:22:33:44:55'):
raise Exception("Unexpected BSSID success")
if "FAIL" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44'):
raise Exception("Unexpected BSSID success")
if "FAIL" not in dev[0].request('BSSID ' + str(id)):
raise Exception("Unexpected BSSID success")
tests = ["02:11:22:33:44:55",
"02:11:22:33:44:55 02:ae:be:ce:53:77",
"02:11:22:33:44:55/ff:00:ff:00:ff:00",
"02:11:22:33:44:55/ff:00:ff:00:ff:00 f2:99:88:77:66:55",
"f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00",
"f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00 12:34:56:78:90:ab",
"02:11:22:33:44:55/ff:ff:ff:00:00:00 02:ae:be:ce:53:77/00:00:00:00:00:ff"]
for val in tests:
dev[0].set_network(id, "bssid_ignore", val)
res = dev[0].get_network(id, "bssid_ignore")
if res != val:
raise Exception("Unexpected bssid_ignore value: %s != %s" % (res, val))
dev[0].set_network(id, "bssid_accept", val)
res = dev[0].get_network(id, "bssid_accept")
if res != val:
raise Exception("Unexpected bssid_accept value: %s != %s" % (res, val))
tests = ["foo",
"00:11:22:33:44:5",
"00:11:22:33:44:55q",
"00:11:22:33:44:55/",
"00:11:22:33:44:55/66:77:88:99:aa:b"]
for val in tests:
if "FAIL" not in dev[0].request("SET_NETWORK %d bssid_ignore %s" % (id, val)):
raise Exception("Invalid bssid_ignore value accepted")
@remote_compatible
def test_wpas_ctrl_network_oom(dev):
"""wpa_supplicant ctrl_iface network OOM in string parsing"""
id = dev[0].add_network()
tests = [('"foo"', 1, 'dup_binstr;wpa_config_set'),
('P"foo"', 1, 'dup_binstr;wpa_config_set'),
('P"foo"', 2, 'wpa_config_set'),
('112233', 1, 'wpa_config_set')]
for val, count, func in tests:
with alloc_fail(dev[0], count, func):
if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' ssid ' + val):
raise Exception("Unexpected success for SET_NETWORK during OOM")
@remote_compatible
def test_wpas_ctrl_many_networks(dev, apdev):
"""wpa_supplicant ctrl_iface LIST_NETWORKS with huge number of networks"""
for i in range(1000):
id = dev[0].add_network()
res = dev[0].request("LIST_NETWORKS")
if str(id) in res:
raise Exception("Last added network was unexpectedly included")
res = dev[0].request("LIST_NETWORKS LAST_ID=%d" % (id - 2))
if str(id) not in res:
raise Exception("Last added network was not present when using LAST_ID")
# This command can take a very long time under valgrind testing on a low
# power CPU, so increase the command timeout significantly to avoid issues
# with the test case failing and following reset operation timing out.
dev[0].request("REMOVE_NETWORK all", timeout=60)
@remote_compatible
def test_wpas_ctrl_dup_network(dev, apdev):
"""wpa_supplicant ctrl_iface DUP_NETWORK"""
ssid = "target"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hostapd.add_ap(apdev[0], params)
src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
only_add_network=True)
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", ssid)
for f in ["key_mgmt", "psk", "scan_freq"]:
res = dev[0].request("DUP_NETWORK {} {} {}".format(src, id, f))
if "OK" not in res:
raise Exception("DUP_NETWORK failed")
dev[0].connect_network(id)
if "FAIL" not in dev[0].request("DUP_NETWORK "):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].request("DUP_NETWORK %d " % id):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].request("DUP_NETWORK %d %d" % (id, id)):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].request("DUP_NETWORK 123456 1234567 "):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].request("DUP_NETWORK %d 123456 " % id):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].request("DUP_NETWORK %d %d foo" % (id, id)):
raise Exception("Unexpected DUP_NETWORK success")
dev[0].request("DISCONNECT")
if "OK" not in dev[0].request("DUP_NETWORK %d %d ssid" % (id, id)):
raise Exception("Unexpected DUP_NETWORK failure")
@remote_compatible
def test_wpas_ctrl_dup_network_global(dev, apdev):
"""wpa_supplicant ctrl_iface DUP_NETWORK (global)"""
ssid = "target"
passphrase = 'qwertyuiop'
params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
hostapd.add_ap(apdev[0], params)
src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
only_add_network=True)
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", ssid)
for f in ["key_mgmt", "psk", "scan_freq"]:
res = dev[0].global_request("DUP_NETWORK {} {} {} {} {}".format(dev[0].ifname, dev[0].ifname, src, id, f))
if "OK" not in res:
raise Exception("DUP_NETWORK failed")
dev[0].connect_network(id)
if "FAIL" not in dev[0].global_request("DUP_NETWORK "):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].global_request("DUP_NETWORK %s" % dev[0].ifname):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s" % (dev[0].ifname, dev[0].ifname)):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d" % (dev[0].ifname, dev[0].ifname, id)):
raise Exception("Unexpected DUP_NETWORK success")
if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d %d" % (dev[0].ifname, dev[0].ifname, id, id)):
raise Exception("Unexpected DUP_NETWORK success")
dev[0].request("DISCONNECT")
if "OK" not in dev[0].global_request("DUP_NETWORK %s %s %d %d ssid" % (dev[0].ifname, dev[0].ifname, id, id)):
raise Exception("Unexpected DUP_NETWORK failure")
def add_cred(dev):
id = dev.add_cred()
ev = dev.wait_event(["CRED-ADDED"])
if ev is None:
raise Exception("Missing CRED-ADDED event")
if " " + str(id) not in ev:
raise Exception("CRED-ADDED event without matching id")
return id
def set_cred(dev, id, field, value):
dev.set_cred(id, field, value)
ev = dev.wait_event(["CRED-MODIFIED"])
if ev is None:
raise Exception("Missing CRED-MODIFIED event")
if " " + str(id) + " " not in ev:
raise Exception("CRED-MODIFIED event without matching id")
if field not in ev:
raise Exception("CRED-MODIFIED event without matching field")
def set_cred_quoted(dev, id, field, value):
dev.set_cred_quoted(id, field, value)
ev = dev.wait_event(["CRED-MODIFIED"])
if ev is None:
raise Exception("Missing CRED-MODIFIED event")
if " " + str(id) + " " not in ev:
raise Exception("CRED-MODIFIED event without matching id")
if field not in ev:
raise Exception("CRED-MODIFIED event without matching field")
def remove_cred(dev, id):
dev.remove_cred(id)
ev = dev.wait_event(["CRED-REMOVED"])
if ev is None:
raise Exception("Missing CRED-REMOVED event")
if " " + str(id) not in ev:
raise Exception("CRED-REMOVED event without matching id")
@remote_compatible
def test_wpas_ctrl_cred(dev):
"""wpa_supplicant ctrl_iface cred set"""
id1 = add_cred(dev[0])
if "FAIL" not in dev[0].request("SET_CRED " + str(id1 + 1) + " temporary 1"):
raise Exception("SET_CRED succeeded unexpectedly on unknown cred id")
if "FAIL" not in dev[0].request("SET_CRED " + str(id1)):
raise Exception("Invalid SET_CRED succeeded unexpectedly")
if "FAIL" not in dev[0].request("SET_CRED " + str(id1) + " temporary"):
raise Exception("Invalid SET_CRED succeeded unexpectedly")
if "FAIL" not in dev[0].request("GET_CRED " + str(id1 + 1) + " temporary"):
raise Exception("GET_CRED succeeded unexpectedly on unknown cred id")
if "FAIL" not in dev[0].request("GET_CRED " + str(id1)):
raise Exception("Invalid GET_CRED succeeded unexpectedly")
if "FAIL" not in dev[0].request("GET_CRED " + str(id1) + " foo"):
raise Exception("Invalid GET_CRED succeeded unexpectedly")
id = add_cred(dev[0])
id2 = add_cred(dev[0])
set_cred(dev[0], id, "temporary", "1")
set_cred(dev[0], id, "priority", "1")
set_cred(dev[0], id, "pcsc", "1")
set_cred(dev[0], id, "sim_num", "0")
set_cred_quoted(dev[0], id, "private_key_passwd", "test")
set_cred_quoted(dev[0], id, "domain_suffix_match", "test")
set_cred_quoted(dev[0], id, "phase1", "test")
set_cred_quoted(dev[0], id, "phase2", "test")
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " eap FOO"):
raise Exception("Unexpected success on unknown EAP method")
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " username 12xa"):
raise Exception("Unexpected success on invalid string")
for i in ("11", "1122", "112233445566778899aabbccddeeff00"):
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " roaming_consortium " + i):
raise Exception("Unexpected success on invalid roaming_consortium")
dev[0].set_cred(id, "excluded_ssid", "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff")
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " excluded_ssid 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"):
raise Exception("Unexpected success on invalid excluded_ssid")
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " foo 4142"):
raise Exception("Unexpected success on unknown field")
tests = ["sp_priority 256",
'roaming_partner "example.org"',
'roaming_partner "' + 200*'a' + '.example.org,"',
'roaming_partner "example.org,1"',
'roaming_partner "example.org,1,2"',
'roaming_partner "example.org,1,2,ABC"']
for t in tests:
if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " " + t):
raise Exception("Unexpected success on invalid SET_CRED value: " + t)
id3 = add_cred(dev[0])
id4 = add_cred(dev[0])
if len(dev[0].request("LIST_CREDS").splitlines()) != 6:
raise Exception("Unexpected LIST_CREDS result(1)")
remove_cred(dev[0], id1)
remove_cred(dev[0], id3)
remove_cred(dev[0], id4)
remove_cred(dev[0], id2)
remove_cred(dev[0], id)
if "FAIL" not in dev[0].request("REMOVE_CRED 1"):
raise Exception("Unexpected success on invalid remove cred")
if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
raise Exception("Unexpected LIST_CREDS result(2)")
id = add_cred(dev[0])
values = [("temporary", "1", False),
("temporary", "0", False),
("pcsc", "1", False),
("realm", "example.com", True),
("username", "user@example.com", True),
("password", "foo", True, "*"),
("ca_cert", "ca.pem", True),
("client_cert", "user.pem", True),
("private_key", "key.pem", True),
("private_key_passwd", "foo", True, "*"),
("imsi", "310026-000000000", True),
("milenage", "foo", True, "*"),
("domain_suffix_match", "example.com", True),
("domain", "example.com", True),
("domain", "example.org", True, "example.com\nexample.org"),
("roaming_consortium", "0123456789", False),
("required_roaming_consortium", "456789", False),
("eap", "TTLS", False),
("phase1", "foo=bar1", True),
("phase2", "foo=bar2", True),
("excluded_ssid", "test", True),
("excluded_ssid", "foo", True, "test\nfoo"),
("roaming_partner", "example.com,0,4,*", True),
("roaming_partner", "example.org,1,2,US", True,
"example.com,0,4,*\nexample.org,1,2,US"),
("update_identifier", "4", False),
("provisioning_sp", "sp.example.com", True),
("sp_priority", "7", False),
("min_dl_bandwidth_home", "100", False),
("min_ul_bandwidth_home", "101", False),
("min_dl_bandwidth_roaming", "102", False),
("min_ul_bandwidth_roaming", "103", False),
("max_bss_load", "57", False),
("req_conn_capab", "6:22,80,443", False),
("req_conn_capab", "17:500", False, "6:22,80,443\n17:500"),
("req_conn_capab", "50", False, "6:22,80,443\n17:500\n50"),
("ocsp", "1", False)]
for v in values:
if v[2]:
set_cred_quoted(dev[0], id, v[0], v[1])
else:
set_cred(dev[0], id, v[0], v[1])
val = dev[0].get_cred(id, v[0])
if len(v) == 4:
expect = v[3]
else:
expect = v[1]
if val != expect:
raise Exception("Unexpected GET_CRED value for {}: {} != {}".format(v[0], val, expect))
creds = dev[0].request("LIST_CREDS").splitlines()
if len(creds) != 2:
raise Exception("Unexpected LIST_CREDS result(3)")
if creds[1] != "0\texample.com\tuser@example.com\texample.com\t310026-000000000":
raise Exception("Unexpected LIST_CREDS value")
remove_cred(dev[0], id)
if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
raise Exception("Unexpected LIST_CREDS result(4)")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "foo.example.com")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "bar.example.com")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "foo.example.com")
if "OK" not in dev[0].request("REMOVE_CRED sp_fqdn=foo.example.com"):
raise Exception("REMOVE_CRED failed")
creds = dev[0].request("LIST_CREDS")
if "foo.example.com" in creds:
raise Exception("REMOVE_CRED sp_fqdn did not remove cred")
if "bar.example.com" not in creds:
raise Exception("REMOVE_CRED sp_fqdn removed incorrect cred")
dev[0].request("REMOVE_CRED all")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "foo.example.com")
set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "bar.example.com")
set_cred_quoted(dev[0], id, "provisioning_sp", "sp.bar.example.com")
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "domain", "foo.example.com")
set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
if "OK" not in dev[0].request("REMOVE_CRED provisioning_sp=sp.foo.example.com"):
raise Exception("REMOVE_CRED failed")
creds = dev[0].request("LIST_CREDS")
if "foo.example.com" in creds:
raise Exception("REMOVE_CRED provisioning_sp did not remove cred")
if "bar.example.com" not in creds:
raise Exception("REMOVE_CRED provisioning_sp removed incorrect cred")
dev[0].request("REMOVE_CRED all")
# Test large number of creds and LIST_CREDS truncation
dev[0].dump_monitor()
for i in range(0, 100):
id = add_cred(dev[0])
set_cred_quoted(dev[0], id, "realm", "relatively.long.realm.test%d.example.com" % i)
dev[0].dump_monitor()
creds = dev[0].request("LIST_CREDS")
for i in range(0, 100):
dev[0].remove_cred(i)
dev[0].dump_monitor()
if len(creds) < 3900 or len(creds) > 4100:
raise Exception("Unexpected LIST_CREDS length: %d" % len(creds))
if "test10.example.com" not in creds:
raise Exception("Missing credential")
if len(creds.splitlines()) > 95:
raise Exception("Too many LIST_CREDS entries in the buffer")
def test_wpas_ctrl_pno(dev):
"""wpa_supplicant ctrl_iface pno"""
if "FAIL" not in dev[0].request("SET pno 1"):
raise Exception("Unexpected success in enabling PNO without enabled network blocks")
id = dev[0].add_network()
dev[0].set_network_quoted(id, "ssid", "test")
dev[0].set_network(id, "key_mgmt", "NONE")
dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect")
#mac80211_hwsim does not yet support PNO, so this fails
if "FAIL" not in dev[0].request("SET pno 1"):
raise Exception("Unexpected success in enabling PNO")
if "FAIL" not in dev[0].request("SET pno 1 freq=2000-3000,5180"):
raise Exception("Unexpected success in enabling PNO")
if "FAIL" not in dev[0].request("SET pno 1 freq=0-6000"):
raise Exception("Unexpected success in enabling PNO")
if "FAIL" in dev[0].request("SET pno 0"):
raise Exception("Unexpected failure in disabling PNO")
@remote_compatible
def test_wpas_ctrl_get(dev):
"""wpa_supplicant ctrl_iface get"""
if "FAIL" in dev[0].request("GET version"):
raise Exception("Unexpected get failure for version")
if "FAIL" in dev[0].request("GET wifi_display"):
raise Exception("Unexpected get failure for wifi_display")
if "FAIL" not in dev[0].request("GET foo"):
raise Exception("Unexpected success on get command")
dev[0].set("wifi_display", "0")
if dev[0].request("GET wifi_display") != '0':
raise Exception("Unexpected wifi_display value")
dev[0].set("wifi_display", "1")
if dev[0].request("GET wifi_display") != '1':
raise Exception("Unexpected wifi_display value")
dev[0].request("P2P_SET disabled 1")
if dev[0].request("GET wifi_display") != '0':
raise Exception("Unexpected wifi_display value (P2P disabled)")
dev[0].request("P2P_SET disabled 0")
if dev[0].request("GET wifi_display") != '1':
raise Exception("Unexpected wifi_display value (P2P re-enabled)")
dev[0].set("wifi_display", "0")
if dev[0].request("GET wifi_display") != '0':
raise Exception("Unexpected wifi_display value")
@remote_compatible
def test_wpas_ctrl_preauth(dev):
"""wpa_supplicant ctrl_iface preauth"""
if "FAIL" not in dev[0].request("PREAUTH "):
raise Exception("Unexpected success on invalid PREAUTH")
if "FAIL" in dev[0].request("PREAUTH 00:11:22:33:44:55"):
raise Exception("Unexpected failure on PREAUTH")
@remote_compatible
def test_wpas_ctrl_tdls_discover(dev):
"""wpa_supplicant ctrl_iface tdls_discover"""
if "FAIL" not in dev[0].request("TDLS_DISCOVER "):
raise Exception("Unexpected success on invalid TDLS_DISCOVER")
if "FAIL" not in dev[0].request("TDLS_DISCOVER 00:11:22:33:44:55"):
raise Exception("Unexpected success on TDLS_DISCOVER")
@remote_compatible
def test_wpas_ctrl_tdls_chan_switch(dev):
"""wpa_supplicant ctrl_iface tdls_chan_switch error cases"""
for args in ['', '00:11:22:33:44:55']:
if "FAIL" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + args):
raise Exception("Unexpected success on invalid TDLS_CANCEL_CHAN_SWITCH: " + args)
for args in ['', 'foo ', '00:11:22:33:44:55 ', '00:11:22:33:44:55 q',
'00:11:22:33:44:55 81', '00:11:22:33:44:55 81 1234',
'00:11:22:33:44:55 81 1234 center_freq1=234 center_freq2=345 bandwidth=456 sec_channel_offset=567 ht vht']:
if "FAIL" not in dev[0].request("TDLS_CHAN_SWITCH " + args):
raise Exception("Unexpected success on invalid TDLS_CHAN_SWITCH: " + args)
@remote_compatible
def test_wpas_ctrl_addr(dev):
"""wpa_supplicant ctrl_iface invalid address"""
if "FAIL" not in dev[0].request("TDLS_SETUP "):
raise Exception("Unexpected success on invalid TDLS_SETUP")
if "FAIL" not in dev[0].request("TDLS_TEARDOWN "):
raise Exception("Unexpected success on invalid TDLS_TEARDOWN")
if "FAIL" not in dev[0].request("FT_DS "):
raise Exception("Unexpected success on invalid FT_DS")
if "FAIL" not in dev[0].request("WPS_PBC 00:11:22:33:44"):
raise Exception("Unexpected success on invalid WPS_PBC")
if "FAIL" not in dev[0].request("WPS_PIN 00:11:22:33:44"):
raise Exception("Unexpected success on invalid WPS_PIN")
if "FAIL" not in dev[0].request("WPS_NFC 00:11:22:33:44"):
raise Exception("Unexpected success on invalid WPS_NFC")
if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44 12345670"):
raise Exception("Unexpected success on invalid WPS_REG")
if "FAIL" not in dev[0].request("IBSS_RSN 00:11:22:33:44"):
raise Exception("Unexpected success on invalid IBSS_RSN")
if "FAIL" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44"):
raise Exception("Unexpected success on invalid BSSID_IGNORE")
@remote_compatible
def test_wpas_ctrl_wps_errors(dev):
"""wpa_supplicant ctrl_iface WPS error cases"""
if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_REG")
if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233"):
raise Exception("Unexpected success on invalid WPS_REG")
if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN"):
raise Exception("Unexpected success on invalid WPS_REG")
if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
raise Exception("Unexpected success on invalid WPS_REG")
if "FAIL" not in dev[0].request("WPS_AP_PIN random"):
raise Exception("Unexpected success on WPS_AP_PIN in non-AP mode")
if "FAIL" not in dev[0].request("WPS_ER_PIN any"):
raise Exception("Unexpected success on invalid WPS_ER_PIN")
if "FAIL" not in dev[0].request("WPS_ER_LEARN 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_ER_LEARN")
if "FAIL" not in dev[0].request("WPS_ER_SET_CONFIG 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_ER_SET_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670"):
raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233"):
raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN"):
raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN WPS"):
raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN FOO 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN NDEF 00:11:22:33:44:55"):
raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN FOO"):
raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN WPS FOO"):
raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
if "FAIL" not in dev[0].request("WPS_NFC_TOKEN FOO"):
raise Exception("Unexpected success on invalid WPS_NFC_TOKEN")
@remote_compatible
def test_wpas_ctrl_config_parser(dev):
"""wpa_supplicant ctrl_iface SET config parser"""
if "FAIL" not in dev[0].request("SET pbc_in_m1 qwerty"):
raise Exception("Non-number accepted as integer")
if "FAIL" not in dev[0].request("SET eapol_version 0"):
raise Exception("Out-of-range value accepted")
if "FAIL" not in dev[0].request("SET eapol_version 10"):
raise Exception("Out-of-range value accepted")
if "FAIL" not in dev[0].request("SET serial_number 0123456789abcdef0123456789abcdef0"):
raise Exception("Too long string accepted")
@remote_compatible
def test_wpas_ctrl_mib(dev):
"""wpa_supplicant ctrl_iface MIB"""
mib = dev[0].get_mib()
if "dot11RSNAOptionImplemented" not in mib:
raise Exception("Missing MIB entry")
if mib["dot11RSNAOptionImplemented"] != "TRUE":
raise Exception("Unexpected dot11RSNAOptionImplemented value")
def test_wpas_ctrl_set_wps_params(dev):
"""wpa_supplicant ctrl_iface SET config_methods"""
try:
_test_wpas_ctrl_set_wps_params(dev)
finally:
dev[2].request("SET config_methods ")
def _test_wpas_ctrl_set_wps_params(dev):
ts = ["config_methods label virtual_display virtual_push_button keypad",
"device_type 1-0050F204-1",
"os_version 01020300",
"uuid 12345678-9abc-def0-1234-56789abcdef0"]
for t in ts:
if "OK" not in dev[2].request("SET " + t):
raise Exception("SET failed for: " + t)
ts = ["uuid 12345678+9abc-def0-1234-56789abcdef0",
"uuid 12345678-qabc-def0-1234-56789abcdef0",
"uuid 12345678-9abc+def0-1234-56789abcdef0",
"uuid 12345678-9abc-qef0-1234-56789abcdef0",
"uuid 12345678-9abc-def0+1234-56789abcdef0",
"uuid 12345678-9abc-def0-q234-56789abcdef0",
"uuid 12345678-9abc-def0-1234+56789abcdef0",
"uuid 12345678-9abc-def0-1234-q6789abcdef0",
"uuid qwerty"]
for t in ts:
if "FAIL" not in dev[2].request("SET " + t):
raise Exception("SET succeeded for: " + t)
def test_wpas_ctrl_level(dev):
"""wpa_supplicant ctrl_iface LEVEL"""
try:
if "FAIL" not in dev[2].request("LEVEL 3"):
raise Exception("Unexpected LEVEL success")
if "OK" not in dev[2].mon.request("LEVEL 2"):
raise Exception("Unexpected LEVEL failure")
dev[2].request("SCAN freq=2412")
ev = dev[2].wait_event(["State:"], timeout=5)
if ev is None:
raise Exception("No debug message received")
dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
finally:
dev[2].mon.request("LEVEL 3")
@remote_compatible
def test_wpas_ctrl_bssid_filter(dev, apdev):
"""wpa_supplicant bssid_filter"""
try:
if "OK" not in dev[2].request("SET bssid_filter " + apdev[0]['bssid']):
raise Exception("Failed to set bssid_filter")
params = {"ssid": "test"}
hostapd.add_ap(apdev[0], params)
hostapd.add_ap(apdev[1], params)
dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
dev[2].scan(freq="2412")
bss = dev[2].get_bss(apdev[0]['bssid'])
if bss is None or len(bss) == 0:
raise Exception("Missing BSS data")
bss = dev[2].get_bss(apdev[1]['bssid'])
if bss and len(bss) != 0:
raise Exception("Unexpected BSS data")
dev[2].request("SET bssid_filter " + apdev[0]['bssid'] + " " + \
apdev[1]['bssid'])
dev[2].scan(freq="2412")
bss = dev[2].get_bss(apdev[0]['bssid'])
if bss is None or len(bss) == 0:
raise Exception("Missing BSS data")
bss = dev[2].get_bss(apdev[1]['bssid'])
if bss is None or len(bss) == 0:
raise Exception("Missing BSS data(2)")
res = dev[2].request("SCAN_RESULTS").splitlines()
if "test" not in res[1] or "test" not in res[2]:
raise Exception("SSID missing from SCAN_RESULTS")
if apdev[0]['bssid'] not in res[1] and apdev[1]['bssid'] not in res[1]:
raise Exception("BSS1 missing from SCAN_RESULTS")
if apdev[0]['bssid'] not in res[2] and apdev[1]['bssid'] not in res[2]:
raise Exception("BSS1 missing from SCAN_RESULTS")
if "FAIL" not in dev[2].request("SET bssid_filter 00:11:22:33:44:55 00:11:22:33:44"):
raise Exception("Unexpected success for invalid SET bssid_filter")
finally:
dev[2].request("SET bssid_filter ")
@remote_compatible
def test_wpas_ctrl_disallow_aps(dev, apdev):
"""wpa_supplicant ctrl_iface disallow_aps"""
params = {"ssid": "test"}
hostapd.add_ap(apdev[0], params)
if "FAIL" not in dev[0].request("SET disallow_aps bssid "):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44"):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps ssid 0"):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps ssid 4q"):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 ssid 112233 ssid 123"):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps ssid 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f00"):
raise Exception("Unexpected success on invalid disallow_aps")
if "FAIL" not in dev[0].request("SET disallow_aps foo 112233445566"):
raise Exception("Unexpected success on invalid disallow_aps")
dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
hostapd.add_ap(apdev[1], params)
dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
dev[0].dump_monitor()
if "OK" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 bssid 00:22:33:44:55:66"):
raise Exception("Failed to set disallow_aps")
if "OK" not in dev[0].request("SET disallow_aps bssid " + apdev[0]['bssid']):
raise Exception("Failed to set disallow_aps")
ev = dev[0].wait_connected(timeout=30, error="Reassociation timed out")
if apdev[1]['bssid'] not in ev:
raise Exception("Unexpected BSSID")
dev[0].dump_monitor()
if "OK" not in dev[0].request("SET disallow_aps ssid " + binascii.hexlify(b"test").decode()):
raise Exception("Failed to set disallow_aps")
dev[0].wait_disconnected(timeout=5, error="Disconnection not seen")
ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
if ev is not None:
raise Exception("Unexpected reassociation")
dev[0].request("DISCONNECT")
dev[0].p2p_start_go(freq=2412)
if "OK" not in dev[0].request("SET disallow_aps "):
raise Exception("Failed to set disallow_aps")
@remote_compatible
def test_wpas_ctrl_blob(dev):
"""wpa_supplicant ctrl_iface SET blob"""
if "FAIL" not in dev[0].request("SET blob foo"):
raise Exception("Unexpected SET success")
if "FAIL" not in dev[0].request("SET blob foo 0"):
raise Exception("Unexpected SET success")
if "FAIL" not in dev[0].request("SET blob foo 0q"):
raise Exception("Unexpected SET success")
if "OK" not in dev[0].request("SET blob foo 00"):
raise Exception("Unexpected SET failure")
if "OK" not in dev[0].request("SET blob foo 0011"):
raise Exception("Unexpected SET failure")
@remote_compatible
def test_wpas_ctrl_set_uapsd(dev):
"""wpa_supplicant ctrl_iface SET uapsd"""
if "FAIL" not in dev[0].request("SET uapsd foo"):
raise Exception("Unexpected SET success")
if "FAIL" not in dev[0].request("SET uapsd 0,0,0"):
raise Exception("Unexpected SET success")
if "FAIL" not in dev[0].request("SET uapsd 0,0"):
raise Exception("Unexpected SET success")
if "FAIL" not in dev[0].request("SET uapsd 0"):
raise Exception("Unexpected SET success")
if "OK" not in dev[0].request("SET uapsd 1,1,1,1;1"):
raise Exception("Unexpected SET failure")
if "OK" not in dev[0].request("SET uapsd 0,0,0,0;0"):
raise Exception("Unexpected SET failure")
if "OK" not in dev[0].request("SET uapsd disable"):
raise Exception("Unexpected SET failure")
def test_wpas_ctrl_set(dev):
"""wpa_supplicant ctrl_iface SET"""
vals = ["foo",
"ampdu 0",
"radio_disable 0",
"ps 10",
"dot11RSNAConfigPMKLifetime 0",
"dot11RSNAConfigPMKReauthThreshold 101",
"dot11RSNAConfigSATimeout 0",
"wps_version_number -1",
"wps_version_number 256",
"fst_group_id ",
"fst_llt 0"]
for val in vals:
if "FAIL" not in dev[0].request("SET " + val):
raise Exception("Unexpected SET success for " + val)
vals = ["ps 1"]
for val in vals:
dev[0].request("SET " + val)
vals = ["EAPOL::heldPeriod 60",
"EAPOL::authPeriod 30",
"EAPOL::startPeriod 30",
"EAPOL::maxStart 3",
"dot11RSNAConfigSATimeout 60",
"ps -1",
"ps 0",
"no_keep_alive 0",
"tdls_disabled 1",
"tdls_disabled 0"]
for val in vals:
if "OK" not in dev[0].request("SET " + val):
raise Exception("Unexpected SET failure for " + val)
# This fails if wpa_supplicant is built with loadable EAP peer method
# support due to missing file and succeeds if no support for loadable
# methods is included, so don't check the return value for now.
dev[0].request("SET load_dynamic_eap /tmp/hwsim-eap-not-found.so")
@remote_compatible
def test_wpas_ctrl_get_capability(dev):
"""wpa_supplicant ctrl_iface GET_CAPABILITY"""
if "FAIL" not in dev[0].request("GET_CAPABILITY 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"):
raise Exception("Unexpected success on invalid GET_CAPABILITY")
if "FAIL" not in dev[0].request("GET_CAPABILITY eap foo"):
raise Exception("Unexpected success on invalid GET_CAPABILITY")
if "AP" not in dev[0].request("GET_CAPABILITY modes strict"):
raise Exception("Unexpected GET_CAPABILITY response")
res = dev[0].get_capability("eap")
if "TTLS" not in res:
raise Exception("Unexpected GET_CAPABILITY eap response: " + str(res))
res = dev[0].get_capability("pairwise")
if "CCMP" not in res:
raise Exception("Unexpected GET_CAPABILITY pairwise response: " + str(res))
res = dev[0].get_capability("group")
if "CCMP" not in res:
raise Exception("Unexpected GET_CAPABILITY group response: " + str(res))
res = dev[0].get_capability("key_mgmt")
if "WPA-PSK" not in res or "WPA-EAP" not in res:
raise Exception("Unexpected GET_CAPABILITY key_mgmt response: " + str(res))
res = dev[0].get_capability("key_mgmt iftype=STATION")
if "WPA-PSK" not in res or "WPA-EAP" not in res:
raise Exception("Unexpected GET_CAPABILITY key_mgmt iftype=STATION response: " + str(res))
iftypes = [ "STATION", "AP_VLAN", "AP", "P2P_GO", "P2P_CLIENT",
"P2P_DEVICE", "MESH", "IBSS", "NAN", "UNKNOWN" ]
for i in iftypes:
res = dev[0].get_capability("key_mgmt iftype=" + i)
logger.info("GET_CAPABILITY key_mgmt iftype=%s: %s" % (i, res))
res = dev[0].get_capability("proto")
if "WPA" not in res or "RSN" not in res:
raise Exception("Unexpected GET_CAPABILITY proto response: " + str(res))
res = dev[0].get_capability("auth_alg")
if "OPEN" not in res or "SHARED" not in res:
raise Exception("Unexpected GET_CAPABILITY auth_alg response: " + str(res))
res = dev[0].get_capability("modes")
if "IBSS" not in res or "AP" not in res:
raise Exception("Unexpected GET_CAPABILITY modes response: " + str(res))
res = dev[0].get_capability("channels")
if "8" not in res or "36" not in res:
raise Exception("Unexpected GET_CAPABILITY channels response: " + str(res))
res = dev[0].get_capability("freq")
if "2457" not in res or "5180" not in res:
raise Exception("Unexpected GET_CAPABILITY freq response: " + str(res))
res = dev[0].get_capability("tdls")
if "EXTERNAL" not in res[0]:
raise Exception("Unexpected GET_CAPABILITY tdls response: " + str(res))
res = dev[0].get_capability("erp")
if res is None or "ERP" not in res[0]:
raise Exception("Unexpected GET_CAPABILITY erp response: " + str(res))
if dev[0].get_capability("foo") is not None:
raise Exception("Unexpected GET_CAPABILITY foo response: " + str(res))
@remote_compatible
def test_wpas_ctrl_nfc_report_handover(dev):
"""wpa_supplicant ctrl_iface NFC_REPORT_HANDOVER"""
vals = ["FOO",
"ROLE freq=12345",
"ROLE TYPE",
"ROLE TYPE REQ",
"ROLE TYPE REQ SEL",
"ROLE TYPE 0Q SEL",
"ROLE TYPE 00 SEL",
"ROLE TYPE 00 0Q",
"ROLE TYPE 00 00"]
for v in vals:
if "FAIL" not in dev[0].request("NFC_REPORT_HANDOVER " + v):
raise Exception("Unexpected NFC_REPORT_HANDOVER success for " + v)
@remote_compatible
def test_wpas_ctrl_nfc_tag_read(dev):
"""wpa_supplicant ctrl_iface WPS_NFC_TAG_READ"""
vals = ["FOO", "0Q", "00", "000000", "10000001", "10000000", "00000000",
"100e0000", "100e0001ff", "100e000411110000", "100e0004100e0001"]
for v in vals:
if "FAIL" not in dev[0].request("WPS_NFC_TAG_READ " + v):
raise Exception("Unexpected WPS_NFC_TAG_READ success for " + v)
@remote_compatible
def test_wpas_ctrl_nfc_get_handover(dev):
"""wpa_supplicant ctrl_iface NFC_GET_HANDOVER"""
vals = ["FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P"]
for v in vals:
if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
raise Exception("Unexpected NFC_GET_HANDOVER_REQ success for " + v)
vals = ["NDEF WPS", "NDEF P2P-CR", "WPS P2P-CR"]
for v in vals:
if "FAIL" in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
raise Exception("Unexpected NFC_GET_HANDOVER_REQ failure for " + v)
vals = ["FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P",
"NDEF WPS", "NDEF WPS uuid"]
for v in vals:
if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
raise Exception("Unexpected NFC_GET_HANDOVER_SEL success for " + v)
vals = ["NDEF P2P-CR", "WPS P2P-CR", "NDEF P2P-CR-TAG",
"WPS P2P-CR-TAG"]
for v in vals:
if "FAIL" in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
raise Exception("Unexpected NFC_GET_HANDOVER_SEL failure for " + v)
def get_bssid_ignore_list(dev):
return dev.request("BSSID_IGNORE").splitlines()
@remote_compatible
def test_wpas_ctrl_bssid_ignore(dev):
"""wpa_supplicant ctrl_iface BSSID_IGNORE"""
if "OK" not in dev[0].request("BSSID_IGNORE clear"):
raise Exception("BSSID_IGNORE clear failed")
b = get_bssid_ignore_list(dev[0])
if len(b) != 0:
raise Exception("Unexpected BSSID ignore list contents: " + str(b))
if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:55"):
raise Exception("BSSID_IGNORE add failed")
b = get_bssid_ignore_list(dev[0])
if "00:11:22:33:44:55" not in b:
raise Exception("Unexpected BSSID ignore list contents: " + str(b))
if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"):
raise Exception("BSSID_IGNORE add failed")
b = get_bssid_ignore_list(dev[0])
if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b:
raise Exception("Unexpected BSSID ignore list contents: " + str(b))
if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"):
raise Exception("BSSID_IGNORE add failed")
b = get_bssid_ignore_list(dev[0])
if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b or len(b) != 2:
raise Exception("Unexpected BSSID ignore list contents: " + str(b))
if "OK" not in dev[0].request("BSSID_IGNORE clear"):
raise Exception("BSSID_IGNORE clear failed")
if dev[0].request("BSSID_IGNORE") != "":
raise Exception("Unexpected BSSID ignore list contents")
@remote_compatible
def test_wpas_ctrl_bssid_ignore_oom(dev):
"""wpa_supplicant ctrl_iface BSSID_IGNORE and out-of-memory"""
with alloc_fail(dev[0], 1, "wpa_bssid_ignore_add"):
if "FAIL" not in dev[0].request("BSSID_IGNORE aa:bb:cc:dd:ee:ff"):
raise Exception("Unexpected success with allocation failure")
def test_wpas_ctrl_log_level(dev):
"""wpa_supplicant ctrl_iface LOG_LEVEL"""
level = dev[2].request("LOG_LEVEL")
if "Current level: MSGDUMP" not in level:
raise Exception("Unexpected debug level(1): " + level)
if "Timestamp: 1" not in level:
raise Exception("Unexpected timestamp(1): " + level)
if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 0"):
raise Exception("LOG_LEVEL failed")
level = dev[2].request("LOG_LEVEL")
if "Current level: MSGDUMP" not in level:
raise Exception("Unexpected debug level(2): " + level)
if "Timestamp: 0" not in level:
raise Exception("Unexpected timestamp(2): " + level)
if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 1"):
raise Exception("LOG_LEVEL failed")
level = dev[2].request("LOG_LEVEL")
if "Current level: MSGDUMP" not in level:
raise Exception("Unexpected debug level(3): " + level)
if "Timestamp: 1" not in level:
raise Exception("Unexpected timestamp(3): " + level)
if "FAIL" not in dev[2].request("LOG_LEVEL FOO"):
raise Exception("Invalid LOG_LEVEL accepted")
for lev in ["EXCESSIVE", "MSGDUMP", "DEBUG", "INFO", "WARNING", "ERROR"]:
if "OK" not in dev[2].request("LOG_LEVEL " + lev):
raise Exception("LOG_LEVEL failed for " + lev)
level = dev[2].request("LOG_LEVEL")
if "Current level: " + lev not in level:
raise Exception("Unexpected debug level: " + level)
if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 1"):
raise Exception("LOG_LEVEL failed")
level = dev[2].request("LOG_LEVEL")
if "Current level: MSGDUMP" not in level:
raise Exception("Unexpected debug level(3): " + level)
if "Timestamp: 1" not in level:
raise Exception("Unexpected timestamp(3): " + level)
@remote_compatible
def test_wpas_ctrl_enable_disable_network(dev, apdev):
"""wpa_supplicant ctrl_iface ENABLE/DISABLE_NETWORK"""
params = {"ssid": "test"}
hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412",
only_add_network=True)
if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
raise Exception("Failed to disable network")
if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect"):
raise Exception("Failed to enable network")
if "OK" not in dev[0].request("DISABLE_NETWORK all"):
raise Exception("Failed to disable networks")
if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id)):
raise Exception("Failed to enable network")
dev[0].wait_connected(timeout=10)
if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
raise Exception("Failed to disable network")
dev[0].wait_disconnected(timeout=10)
time.sleep(0.1)
if "OK" not in dev[0].request("ENABLE_NETWORK all"):
raise Exception("Failed to enable network")
dev[0].wait_connected(timeout=10)
if "OK" not in dev[0].request("DISABLE_NETWORK all"):
raise Exception("Failed to disable network")
dev[0].wait_disconnected(timeout=10)
def test_wpas_ctrl_country(dev, apdev):
"""wpa_supplicant SET/GET country code"""
try:
# work around issues with possible pending regdom event from the end of
# the previous test case
time.sleep(0.2)
dev[0].dump_monitor()
if "OK" not in dev[0].request("SET country FI"):
raise Exception("Failed to set country code")
if dev[0].request("GET country") != "FI":
raise Exception("Country code set failed")
ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
if ev is None:
raise Exception("regdom change event not seen")
if "init=USER type=COUNTRY alpha2=FI" not in ev:
raise Exception("Unexpected event contents: " + ev)
dev[0].request("SET country 00")
if dev[0].request("GET country") != "00":
raise Exception("Country code set failed")
ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
if ev is None:
raise Exception("regdom change event not seen")
# init=CORE was previously used due to invalid db.txt data for 00. For
# now, allow both it and the new init=USER after fixed db.txt.
if "init=CORE type=WORLD" not in ev and "init=USER type=WORLD" not in ev:
raise Exception("Unexpected event contents: " + ev)
finally:
subprocess.call(['iw', 'reg', 'set', '00'])
def test_wpas_ctrl_suspend_resume(dev):
"""wpa_supplicant SUSPEND/RESUME"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
if "OK" not in wpas.global_request("SUSPEND"):
raise Exception("SUSPEND failed")
time.sleep(1)
if "OK" not in wpas.global_request("RESUME"):
raise Exception("RESUME failed")
if "OK" not in wpas.request("SUSPEND"):
raise Exception("Per-interface SUSPEND failed")
if "OK" not in wpas.request("RESUME"):
raise Exception("Per-interface RESUME failed")
ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
if ev is None:
raise Exception("Scan not completed")
def test_wpas_ctrl_global(dev):
"""wpa_supplicant global control interface"""
wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas.interface_add("wlan5")
if "PONG" not in wpas.global_request("PING"):
raise Exception("PING failed")
if "wlan5" not in wpas.global_request("INTERFACES"):
raise Exception("Interface not found")
if "UNKNOWN COMMAND" not in wpas.global_request("FOO"):
raise Exception("Unexpected response to unknown command")
if "PONG" not in wpas.global_request("IFNAME=wlan5 PING"):
raise Exception("Per-interface PING failed")
if "FAIL-NO-IFNAME-MATCH" not in wpas.global_request("IFNAME=notfound PING"):
raise Exception("Unknown interface not reported correctly")
if "FAIL" not in wpas.global_request("SAVE_CONFIG"):
raise Exception("SAVE_CONFIG succeeded unexpectedly")
if "OK" not in wpas.global_request("SET wifi_display 0"):
raise Exception("SET failed")
if "wifi_display=0" not in wpas.global_request("STATUS"):
raise Exception("wifi_display not disabled")
if "OK" not in wpas.global_request("SET wifi_display 1"):
raise Exception("SET failed")
if "wifi_display=1" not in wpas.global_request("STATUS"):
raise Exception("wifi_display not enabled")
if "FAIL" not in wpas.global_request("SET foo 1"):
raise Exception("SET succeeded unexpectedly")
if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
raise Exception("P2P was disabled")
wpas.global_request("P2P_SET disabled 1")
if "p2p_state=DISABLED" not in wpas.global_request("STATUS"):
raise Exception("P2P was not disabled")
wpas.global_request("P2P_SET disabled 0")
if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
raise Exception("P2P was not enabled")
# driver_nl80211.c does not support interface list, so do not fail because
# of that
logger.debug(wpas.global_request("INTERFACE_LIST"))
if "FAIL" not in wpas.global_request("INTERFACE_ADD "):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge foo"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO "):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge create abcd"):
raise Exception("INTERFACE_ADD succeeded unexpectedly")
@remote_compatible
def test_wpas_ctrl_roam(dev, apdev):
"""wpa_supplicant ctrl_iface ROAM error cases"""
if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44"):
raise Exception("Unexpected success")
if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
raise Exception("Unexpected success")
params = {"ssid": "test"}
hostapd.add_ap(apdev[0], params)
id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
raise Exception("Unexpected success")
@remote_compatible
def test_wpas_ctrl_ipaddr(dev, apdev):
"""wpa_supplicant IP address in STATUS"""
try:
dev[0].cmd_execute(['ip', 'addr', 'add', '10.174.65.207/32', 'dev',
dev[0].ifname])
ipaddr = dev[0].get_status_field('ip_address')
if ipaddr != '10.174.65.207':
raise Exception("IP address not in STATUS output")
finally:
dev[0].cmd_execute(['ip', 'addr', 'del', '10.174.65.207/32', 'dev',
dev[0].ifname])
@remote_compatible
def test_wpas_ctrl_rsp(dev, apdev):
"""wpa_supplicant ctrl_iface CTRL-RSP-"""
if "FAIL" not in dev[0].request("CTRL-RSP-"):
raise Exception("Request succeeded unexpectedly")
if "FAIL" not in dev[0].request("CTRL-RSP-foo-"):
raise Exception("Request succeeded unexpectedly")
if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567"):
raise Exception("Request succeeded unexpectedly")
if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567:"):
raise Exception("Request succeeded unexpectedly")
id = dev[0].add_network()
if "FAIL" not in dev[0].request("CTRL-RSP-foo-%d:" % id):
raise Exception("Request succeeded unexpectedly")
for req in ["IDENTITY", "PASSWORD", "NEW_PASSWORD", "PIN", "OTP",
"PASSPHRASE", "SIM"]:
if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
raise Exception("Request failed unexpectedly")
if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
raise Exception("Request failed unexpectedly")
def test_wpas_ctrl_vendor_test(dev, apdev):
"""wpas_supplicant and VENDOR test command"""
OUI_QCA = 0x001374
QCA_NL80211_VENDOR_SUBCMD_TEST = 1
QCA_WLAN_VENDOR_ATTR_TEST = 8
attr = struct.pack("@HHI", 4 + 4, QCA_WLAN_VENDOR_ATTR_TEST, 123)
cmd = "VENDOR %x %d %s" % (OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_TEST, binascii.hexlify(attr).decode())
res = dev[0].request(cmd)
if "FAIL" in res:
raise Exception("VENDOR command failed")
val, = struct.unpack("@I", binascii.unhexlify(res))
if val != 125:
raise Exception("Incorrect response value")
res = dev[0].request(cmd + " nested=1")
if "FAIL" in res:
raise Exception("VENDOR command failed")
val, = struct.unpack("@I", binascii.unhexlify(res))
if val != 125:
raise Exception("Incorrect response value")
res = dev[0].request(cmd + " nested=0")
if "FAIL" not in res:
raise Exception("VENDOR command with invalid (not nested) data accepted")
@remote_compatible
def test_wpas_ctrl_vendor(dev, apdev):
"""wpa_supplicant ctrl_iface VENDOR"""
cmds = ["foo",
"1",
"1 foo",
"1 2foo",
"1 2 qq"]
for cmd in cmds:
if "FAIL" not in dev[0].request("VENDOR " + cmd):
raise Exception("Invalid VENDOR command accepted: " + cmd)
@remote_compatible
def test_wpas_ctrl_mgmt_tx(dev, apdev):
"""wpa_supplicant ctrl_iface MGMT_TX"""
cmds = ["foo",
"00:11:22:33:44:55 foo",
"00:11:22:33:44:55 11:22:33:44:55:66",
"00:11:22:33:44:55 11:22:33:44:55:66 freq=0 no_cck=0 wait_time=0 action=123",
"00:11:22:33:44:55 11:22:33:44:55:66 action=12qq"]
for cmd in cmds:
if "FAIL" not in dev[0].request("MGMT_TX " + cmd):
raise Exception("Invalid MGMT_TX command accepted: " + cmd)
if "OK" not in dev[0].request("MGMT_TX_DONE"):
raise Exception("MGMT_TX_DONE failed")
@remote_compatible
def test_wpas_ctrl_driver_event(dev, apdev):
"""wpa_supplicant ctrl_iface DRIVER_EVENT"""
if "FAIL" not in dev[0].request("DRIVER_EVENT foo"):
raise Exception("Invalid DRIVER_EVENT accepted")
+ if "OK" not in dev[0].request("DRIVER_EVENT ASSOC reassoc=1 req_ies=0000 resp_ies=0000 resp_frame=0000 beacon_ies=0000 freq=2412 wmm::info_bitmap=0 wmm::uapsd_queues=0 addr=02:02:02:02:02:02 authorized=0 key_replay_ctr=00 ptk_kck=00 ptk_kek=00 subnet_status=0 fils_erp_next_seq_num=0 fils_pmk=00 fils_pmkid=" + 16*"00"):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
@remote_compatible
def test_wpas_ctrl_eapol_rx(dev, apdev):
"""wpa_supplicant ctrl_iface EAPOL_RX"""
cmds = ["foo",
"00:11:22:33:44:55 123",
"00:11:22:33:44:55 12qq"]
for cmd in cmds:
if "FAIL" not in dev[0].request("EAPOL_RX " + cmd):
raise Exception("Invalid EAPOL_RX command accepted: " + cmd)
@remote_compatible
def test_wpas_ctrl_data_test(dev, apdev):
"""wpa_supplicant ctrl_iface DATA_TEST"""
dev[0].request("DATA_TEST_CONFIG 0")
if "FAIL" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
raise Exception("DATA_TEST_TX accepted when not in test mode")
try:
if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
raise Exception("DATA_TEST_CONFIG failed")
if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
raise Exception("DATA_TEST_CONFIG failed")
cmds = ["foo",
"00:11:22:33:44:55 foo",
"00:11:22:33:44:55 00:11:22:33:44:55 -1",
"00:11:22:33:44:55 00:11:22:33:44:55 256"]
for cmd in cmds:
if "FAIL" not in dev[0].request("DATA_TEST_TX " + cmd):
raise Exception("Invalid DATA_TEST_TX command accepted: " + cmd)
if "OK" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
raise Exception("DATA_TEST_TX failed")
finally:
dev[0].request("DATA_TEST_CONFIG 0")
cmds = ["",
"00",
"00112233445566778899aabbccdde",
"00112233445566778899aabbccdq"]
for cmd in cmds:
if "FAIL" not in dev[0].request("DATA_TEST_FRAME " + cmd):
raise Exception("Invalid DATA_TEST_FRAME command accepted: " + cmd)
if "OK" not in dev[0].request("DATA_TEST_FRAME 00112233445566778899aabbccddee"):
raise Exception("DATA_TEST_FRAME failed")
@remote_compatible
def test_wpas_ctrl_vendor_elem(dev, apdev):
"""wpa_supplicant ctrl_iface VENDOR_ELEM"""
if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 "):
raise Exception("VENDOR_ELEM_ADD failed")
cmds = ["-1 ",
"255 ",
"1",
"1 123",
"1 12qq34"]
for cmd in cmds:
if "FAIL" not in dev[0].request("VENDOR_ELEM_ADD " + cmd):
raise Exception("Invalid VENDOR_ELEM_ADD command accepted: " + cmd)
cmds = ["-1 ",
"255 "]
for cmd in cmds:
if "FAIL" not in dev[0].request("VENDOR_ELEM_GET " + cmd):
raise Exception("Invalid VENDOR_ELEM_GET command accepted: " + cmd)
dev[0].request("VENDOR_ELEM_REMOVE 1 *")
cmds = ["-1 ",
"255 ",
"1",
"1",
"1 123",
"1 12qq34",
"1 12",
"1 0000"]
for cmd in cmds:
if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
dev[0].request("VENDOR_ELEM_ADD 1 000100")
if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 "):
raise Exception("VENDOR_ELEM_REMOVE failed")
cmds = ["-1 ",
"255 ",
"1",
"1 123",
"1 12qq34",
"1 12",
"1 0000"]
for cmd in cmds:
if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 000100"):
raise Exception("VENDOR_ELEM_REMOVE failed")
def test_wpas_ctrl_misc(dev, apdev):
"""wpa_supplicant ctrl_iface and miscellaneous commands"""
if "OK" not in dev[0].request("RELOG"):
raise Exception("RELOG failed")
if dev[0].request("IFNAME") != dev[0].ifname:
raise Exception("IFNAME returned unexpected response")
if "FAIL" not in dev[0].request("REATTACH"):
raise Exception("REATTACH accepted while disabled")
if "OK" not in dev[2].request("RECONFIGURE"):
raise Exception("RECONFIGURE failed")
if "FAIL" in dev[0].request("INTERFACE_LIST"):
raise Exception("INTERFACE_LIST failed")
if "UNKNOWN COMMAND" not in dev[0].request("FOO"):
raise Exception("Unknown command accepted")
if "FAIL" not in dev[0].global_request("INTERFACE_REMOVE foo"):
raise Exception("Invalid INTERFACE_REMOVE accepted")
if "FAIL" not in dev[0].global_request("SET foo"):
raise Exception("Invalid global SET accepted")
@remote_compatible
def test_wpas_ctrl_dump(dev, apdev):
"""wpa_supplicant ctrl_iface and DUMP/GET global parameters"""
vals = dev[0].get_config()
logger.info("Config values from DUMP: " + str(vals))
for field in vals:
res = dev[0].request("GET " + field)
if res == 'FAIL\n':
res = "null"
if res != vals[field]:
print("'{}' != '{}'".format(res, vals[field]))
raise Exception("Mismatch in config field " + field)
if "beacon_int" not in vals:
raise Exception("Missing config field")
def test_wpas_ctrl_interface_add(dev, apdev):
"""wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
ifname = "test-" + dev[0].ifname
dev[0].interface_add(ifname, create=True)
wpas = WpaSupplicant(ifname=ifname)
wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(wpas, hapd)
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].global_request("INTERFACE_REMOVE " + ifname)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_wpas_ctrl_interface_add_sta(dev, apdev):
"""wpa_supplicant INTERFACE_ADD/REMOVE with STA vif creation/removal"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
ifname = "test-" + dev[0].ifname
dev[0].interface_add(ifname, create=True, if_type='sta')
wpas = WpaSupplicant(ifname=ifname)
wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
wpas.request("DISCONNECT")
wpas.wait_disconnected()
dev[0].global_request("INTERFACE_REMOVE " + ifname)
def test_wpas_ctrl_interface_add_ap(dev, apdev):
"""wpa_supplicant INTERFACE_ADD/REMOVE AP interface"""
with HWSimRadio() as (radio, iface):
wpas0 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
wpas0.interface_add(iface)
ifname = "test-wpas-ap"
wpas0.interface_add(ifname, create=True, if_type='ap')
wpas = WpaSupplicant(ifname=ifname)
id = wpas.add_network()
wpas.set_network(id, "mode", "2")
wpas.set_network_quoted(id, "ssid", "wpas-ap-open")
wpas.set_network(id, "key_mgmt", "NONE")
wpas.set_network(id, "frequency", "2412")
wpas.set_network(id, "scan_freq", "2412")
wpas.select_network(id)
wait_ap_ready(wpas)
dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
dev[2].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(wpas, dev[1])
hwsim_utils.test_connectivity(dev[1], dev[2])
dev[1].request("DISCONNECT")
dev[2].request("DISCONNECT")
dev[1].wait_disconnected()
dev[2].wait_disconnected()
wpas0.global_request("INTERFACE_REMOVE " + ifname)
def test_wpas_ctrl_interface_add_many(dev, apdev):
"""wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal (many)"""
try:
_test_wpas_ctrl_interface_add_many(dev, apdev)
finally:
for i in range(10):
ifname = "test%d-" % i + dev[0].ifname
dev[0].global_request("INTERFACE_REMOVE " + ifname)
def _test_wpas_ctrl_interface_add_many(dev, apdev):
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
dev[0].dump_monitor()
l = []
for i in range(10):
ifname = "test%d-" % i + dev[0].ifname
dev[0].interface_add(ifname, create=True)
wpas = WpaSupplicant(ifname=ifname)
wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
wpas.dump_monitor()
l.append(wpas)
dev[0].dump_monitor()
for wpas in l:
wpas.dump_monitor()
hwsim_utils.test_connectivity(wpas, hapd)
wpas.dump_monitor()
dev[0].dump_monitor()
def test_wpas_ctrl_interface_add2(dev, apdev):
"""wpa_supplicant INTERFACE_ADD/REMOVE with vif without creation/removal"""
ifname = "test-ext-" + dev[0].ifname
try:
_test_wpas_ctrl_interface_add2(dev, apdev, ifname)
finally:
subprocess.call(['iw', 'dev', ifname, 'del'])
def _test_wpas_ctrl_interface_add2(dev, apdev, ifname):
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(dev[0], hapd)
subprocess.call(['iw', 'dev', dev[0].ifname, 'interface', 'add', ifname,
'type', 'station'])
subprocess.call(['ip', 'link', 'set', 'dev', ifname, 'address',
'02:01:00:00:02:01'])
dev[0].interface_add(ifname, set_ifname=False, all_params=True)
wpas = WpaSupplicant(ifname=ifname)
wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
hwsim_utils.test_connectivity(wpas, hapd)
hwsim_utils.test_connectivity(dev[0], hapd)
del wpas
dev[0].global_request("INTERFACE_REMOVE " + ifname)
hwsim_utils.test_connectivity(dev[0], hapd)
def test_wpas_ctrl_wait(dev, apdev, test_params):
"""wpa_supplicant control interface wait for client"""
logfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.log-wpas')
pidfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.pid-wpas')
conffile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.conf')
with open(conffile, 'w') as f:
f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
prg = os.path.join(test_params['logdir'],
'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
if not os.path.exists(prg):
prg = '../../wpa_supplicant/wpa_supplicant'
arg = [prg]
cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
out = cmd.communicate()[0].decode()
cmd.wait()
tracing = "Linux tracing" in out
with HWSimRadio() as (radio, iface):
arg = [prg, '-BdddW', '-P', pidfile, '-f', logfile,
'-Dnl80211', '-c', conffile, '-i', iface]
if tracing:
arg += ['-T']
logger.info("Start wpa_supplicant: " + str(arg))
subprocess.call(arg)
wpas = WpaSupplicant(ifname=iface)
if "PONG" not in wpas.request("PING"):
raise Exception("Could not PING wpa_supplicant")
if not os.path.exists(pidfile):
raise Exception("PID file not created")
if "OK" not in wpas.request("TERMINATE"):
raise Exception("Could not TERMINATE")
ev = wpas.wait_event(["CTRL-EVENT-TERMINATING"], timeout=2)
if ev is None:
raise Exception("No termination event received")
for i in range(20):
if not os.path.exists(pidfile):
break
time.sleep(0.1)
if os.path.exists(pidfile):
raise Exception("PID file not removed")
@remote_compatible
def test_wpas_ctrl_oom(dev):
"""Various wpa_supplicant ctrl_iface OOM cases"""
try:
_test_wpas_ctrl_oom(dev)
finally:
dev[0].request("VENDOR_ELEM_REMOVE 1 *")
dev[0].request("VENDOR_ELEM_REMOVE 2 *")
dev[0].request("SET bssid_filter ")
def _test_wpas_ctrl_oom(dev):
dev[0].request('VENDOR_ELEM_ADD 2 000100')
tests = [('DRIVER_EVENT AVOID_FREQUENCIES 2412', 'FAIL',
1, 'freq_range_list_parse'),
('P2P_SET disallow_freq 2412', 'FAIL',
1, 'freq_range_list_parse'),
('SCAN freq=2412', 'FAIL',
1, 'freq_range_list_parse'),
('INTERWORKING_SELECT freq=2412', 'FAIL',
1, 'freq_range_list_parse'),
('SCAN ssid 112233', 'FAIL',
1, 'wpas_ctrl_scan'),
('MGMT_TX 00:00:00:00:00:00 00:00:00:00:00:00 action=00', 'FAIL',
1, 'wpas_ctrl_iface_mgmt_tx'),
('EAPOL_RX 00:00:00:00:00:00 00', 'FAIL',
1, 'wpas_ctrl_iface_eapol_rx'),
('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
1, 'wpas_ctrl_iface_data_test_frame'),
('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
1, 'l2_packet_init;wpas_ctrl_iface_data_test_frame'),
('VENDOR_ELEM_ADD 1 000100', 'FAIL',
1, 'wpas_ctrl_vendor_elem_add'),
('VENDOR_ELEM_ADD 2 000100', 'FAIL',
2, 'wpas_ctrl_vendor_elem_add'),
('VENDOR_ELEM_REMOVE 2 000100', 'FAIL',
1, 'wpas_ctrl_vendor_elem_remove'),
('SET bssid_filter 00:11:22:33:44:55', 'FAIL',
1, 'set_bssid_filter'),
('SET disallow_aps bssid 00:11:22:33:44:55', 'FAIL',
1, 'set_disallow_aps'),
('SET disallow_aps ssid 11', 'FAIL',
1, 'set_disallow_aps'),
('SET blob foo 0011', 'FAIL',
1, 'wpas_ctrl_set_blob'),
('SET blob foo 0011', 'FAIL',
2, 'wpas_ctrl_set_blob'),
('SET blob foo 0011', 'FAIL',
3, 'wpas_ctrl_set_blob'),
('WPS_NFC_TAG_READ 00', 'FAIL',
1, 'wpa_supplicant_ctrl_iface_wps_nfc_tag_read'),
('WPS_NFC_TOKEN NDEF', 'FAIL',
1, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('WPS_NFC_TOKEN NDEF', 'FAIL',
2, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('WPS_NFC_TOKEN NDEF', 'FAIL',
3, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('WPS_NFC_TOKEN NDEF', 'FAIL',
4, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
1, 'wpas_ctrl_nfc_report_handover'),
('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
2, 'wpas_ctrl_nfc_report_handover'),
('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
1, 'wps_build_nfc_handover_req'),
('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
1, 'ndef_build_record'),
('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
1, 'wpas_p2p_nfc_handover'),
('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
1, 'wps_build_nfc_handover_req_p2p'),
('NFC_GET_HANDOVER_REQ NDEF P2P-CR', 'FAIL',
1, 'ndef_build_record'),
('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', None,
1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
('NFC_GET_HANDOVER_SEL NDEF P2P-CR', None,
1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
('P2P_ASP_PROVISION_RESP 00:11:22:33:44:55 id=1', 'FAIL',
1, 'p2p_parse_asp_provision_cmd'),
('P2P_SERV_DISC_REQ 00:11:22:33:44:55 02000001', 'FAIL',
1, 'p2p_ctrl_serv_disc_req'),
('P2P_SERV_DISC_RESP 2412 00:11:22:33:44:55 1 00', 'FAIL',
1, 'p2p_ctrl_serv_disc_resp'),
('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
'FAIL',
1, 'p2p_ctrl_service_add_bonjour'),
('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
'FAIL',
2, 'p2p_ctrl_service_add_bonjour'),
('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
'FAIL',
3, 'p2p_ctrl_service_add_bonjour'),
('P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01',
'FAIL',
1, 'p2p_ctrl_service_del_bonjour'),
('GAS_REQUEST 00:11:22:33:44:55 00', 'FAIL',
1, 'gas_request'),
('GAS_REQUEST 00:11:22:33:44:55 00 11', 'FAIL',
2, 'gas_request'),
('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 realm=example.com',
'FAIL',
1, 'hs20_nai_home_realm_list'),
('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 00',
'FAIL',
1, 'hs20_get_nai_home_realm_list'),
('WNM_SLEEP enter tfs_req=11', 'FAIL',
1, 'wpas_ctrl_iface_wnm_sleep'),
('WNM_SLEEP enter tfs_req=11', 'FAIL',
2, 'wpas_ctrl_iface_wnm_sleep'),
('WNM_SLEEP enter tfs_req=11', 'FAIL',
3, 'wpas_ctrl_iface_wnm_sleep'),
('WNM_SLEEP enter tfs_req=11', 'FAIL',
4, 'wpas_ctrl_iface_wnm_sleep'),
('WNM_SLEEP enter tfs_req=11', 'FAIL',
5, 'wpas_ctrl_iface_wnm_sleep'),
('WNM_SLEEP enter', 'FAIL',
3, 'wpas_ctrl_iface_wnm_sleep'),
('VENDOR 1 1 00', 'FAIL',
1, 'wpa_supplicant_vendor_cmd'),
('VENDOR 1 1 00', 'FAIL',
2, 'wpa_supplicant_vendor_cmd'),
('RADIO_WORK add test', 'FAIL',
1, 'wpas_ctrl_radio_work_add'),
('RADIO_WORK add test', 'FAIL',
2, 'wpas_ctrl_radio_work_add'),
('AUTOSCAN periodic:1', 'FAIL',
1, 'wpa_supplicant_ctrl_iface_autoscan'),
('PING', None,
1, 'wpa_supplicant_ctrl_iface_process')]
tls = dev[0].request("GET tls_library")
if not tls.startswith("internal"):
tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
for cmd, exp, count, func in tests:
with alloc_fail(dev[0], count, func):
res = dev[0].request(cmd)
if exp and exp not in res:
raise Exception("Unexpected success for '%s' during OOM (%d:%s)" % (cmd, count, func))
tests = [('FOO', None,
1, 'wpa_supplicant_global_ctrl_iface_process'),
('IFNAME=notfound PING', 'FAIL\n',
1, 'wpas_global_ctrl_iface_ifname')]
for cmd, exp, count, func in tests:
with alloc_fail(dev[0], count, func):
res = dev[0].global_request(cmd)
if exp and exp not in res:
raise Exception("Unexpected success for '%s' during OOM" % cmd)
@remote_compatible
def test_wpas_ctrl_error(dev):
"""Various wpa_supplicant ctrl_iface error cases"""
tests = [('WPS_NFC_TOKEN NDEF', 'FAIL',
1, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('WPS_NFC_TOKEN NDEF', 'FAIL',
2, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
1, 'wpas_p2p_nfc_handover'),
('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
1, 'wps_build_nfc_handover_req_p2p')]
for cmd, exp, count, func in tests:
with fail_test(dev[0], count, func):
res = dev[0].request(cmd)
if exp and exp not in res:
raise Exception("Unexpected success for '%s' during failure testing (%d:%s)" % (cmd, count, func))
def test_wpas_ctrl_socket_full(dev, apdev, test_params):
"""wpa_supplicant control socket and full send buffer"""
if not dev[0].ping():
raise Exception("Could not ping wpa_supplicant at the beginning of the test")
dev[0].get_status()
counter = 0
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
counter += 1
s.bind(local)
s.connect("/var/run/wpa_supplicant/wlan0")
for i in range(20):
logger.debug("Command %d" % i)
try:
s.send(b"MIB")
except Exception as e:
logger.info("Could not send command %d: %s" % (i, str(e)))
break
# Close without receiving response
time.sleep(0.01)
if not dev[0].ping():
raise Exception("Could not ping wpa_supplicant in the middle of the test")
dev[0].get_status()
s2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
local2 = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
counter += 1
s2.bind(local2)
s2.connect("/var/run/wpa_supplicant/wlan0")
for i in range(10):
logger.debug("Command %d [2]" % i)
try:
s2.send(b"MIB")
except Exception as e:
logger.info("Could not send command %d [2]: %s" % (i, str(e)))
break
# Close without receiving response
time.sleep(0.01)
s.close()
os.unlink(local)
for i in range(10):
logger.debug("Command %d [3]" % i)
try:
s2.send(b"MIB")
except Exception as e:
logger.info("Could not send command %d [3]: %s" % (i, str(e)))
break
# Close without receiving response
time.sleep(0.01)
s2.close()
os.unlink(local2)
if not dev[0].ping():
raise Exception("Could not ping wpa_supplicant in the middle of the test [2]")
dev[0].get_status()
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
counter += 1
s.bind(local)
s.connect("/var/run/wpa_supplicant/wlan0")
s.send(b"ATTACH")
res = s.recv(100).decode()
if "OK" not in res:
raise Exception("Could not attach a test socket")
for i in range(5):
dev[0].scan(freq=2412)
s.close()
os.unlink(local)
for i in range(5):
dev[0].scan(freq=2412)
if not dev[0].ping():
raise Exception("Could not ping wpa_supplicant at the end of the test")
dev[0].get_status()
def test_wpas_ctrl_event_burst(dev, apdev):
"""wpa_supplicant control socket and event burst"""
if "OK" not in dev[0].request("EVENT_TEST 1000"):
raise Exception("Could not request event messages")
total_i = 0
total_g = 0
for i in range(100):
(i, g) = dev[0].dump_monitor()
total_i += i
total_g += g
logger.info("Received i=%d g=%d" % (i, g))
if total_i >= 1000 and total_g >= 1000:
break
time.sleep(0.05)
if total_i < 1000:
raise Exception("Some per-interface events not seen: %d" % total_i)
if total_g < 1000:
raise Exception("Some global events not seen: %d" % total_g)
if not dev[0].ping():
raise Exception("Could not ping wpa_supplicant at the end of the test")
@remote_compatible
def test_wpas_ctrl_sched_scan_plans(dev, apdev):
"""wpa_supplicant sched_scan_plans parsing"""
dev[0].request("SET sched_scan_plans foo")
dev[0].request("SET sched_scan_plans 10:100 20:200 30")
dev[0].request("SET sched_scan_plans 4294967295:0")
dev[0].request("SET sched_scan_plans 1 1")
dev[0].request("SET sched_scan_plans ")
try:
with alloc_fail(dev[0], 1, "wpas_sched_scan_plans_set"):
dev[0].request("SET sched_scan_plans 10:100")
finally:
dev[0].request("SET sched_scan_plans ")
def test_wpas_ctrl_signal_monitor(dev, apdev):
"""wpa_supplicant SIGNAL_MONITOR command"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
bgscan="simple:1:-45:2")
dev[2].connect("open", key_mgmt="NONE", scan_freq="2412")
tests = [" THRESHOLD=-45", " THRESHOLD=-44 HYSTERESIS=5", ""]
try:
if "FAIL" in dev[2].request("SIGNAL_MONITOR THRESHOLD=-1 HYSTERESIS=5"):
raise Exception("SIGNAL_MONITOR command failed")
for t in tests:
if "OK" not in dev[0].request("SIGNAL_MONITOR" + t):
raise Exception("SIGNAL_MONITOR command failed: " + t)
if "FAIL" not in dev[1].request("SIGNAL_MONITOR THRESHOLD=-44 HYSTERESIS=5"):
raise Exception("SIGNAL_MONITOR command accepted while using bgscan")
ev = dev[2].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
if ev is None:
raise Exception("No signal change event seen")
if "above=0" not in ev:
raise Exception("Unexpected signal change event contents: " + ev)
finally:
dev[0].request("SIGNAL_MONITOR")
dev[1].request("SIGNAL_MONITOR")
dev[2].request("SIGNAL_MONITOR")
dev[0].request("REMOVE_NETWORK all")
dev[1].request("REMOVE_NETWORK all")
dev[1].wait_disconnected()
def test_wpas_ctrl_p2p_listen_offload(dev, apdev):
"""wpa_supplicant P2P_LO_START and P2P_LO_STOP commands"""
dev[0].request("P2P_LO_STOP")
dev[0].request("P2P_LO_START ")
dev[0].request("P2P_LO_START 2412")
dev[0].request("P2P_LO_START 2412 100 200 3")
dev[0].request("P2P_LO_STOP")
def test_wpas_ctrl_driver_flags(dev, apdev):
"""DRIVER_FLAGS command"""
params = hostapd.wpa2_params(ssid="test", passphrase="12345678")
hapd = hostapd.add_ap(apdev[0], params)
hapd_flags = hapd.request("DRIVER_FLAGS")
wpas_flags = dev[0].request("DRIVER_FLAGS")
if "FAIL" in hapd_flags:
raise Exception("DRIVER_FLAGS failed")
if hapd_flags != wpas_flags:
raise Exception("Unexpected difference in hostapd vs. wpa_supplicant DRIVER_FLAGS output")
logger.info("DRIVER_FLAGS: " + hapd_flags)
flags = hapd_flags.split('\n')
if 'AP' not in flags:
raise Exception("AP flag missing from DRIVER_FLAGS")
def test_wpas_ctrl_bss_current(dev, apdev):
"""wpa_supplicant BSS CURRENT command"""
hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
bssid = hapd.own_addr()
res = dev[0].request("BSS CURRENT")
if res != '':
raise Exception("Unexpected BSS CURRENT response in disconnected state")
dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
res = dev[0].request("BSS CURRENT")
if bssid not in res:
raise Exception("Unexpected BSS CURRENT response in connected state")
def test_wpas_ctrl_set_lci_errors(dev):
"""wpa_supplicant SET lci error cases"""
if "FAIL" not in dev[0].request("SET lci q"):
raise Exception("Invalid LCI value accepted")
with fail_test(dev[0], 1, "os_get_reltime;wpas_ctrl_iface_set_lci"):
if "FAIL" not in dev[0].request("SET lci 00"):
raise Exception("SET lci accepted with failing os_get_reltime")
def test_wpas_ctrl_set_radio_disabled(dev):
"""wpa_supplicant SET radio_disabled"""
# This is not currently supported with nl80211, but execute the commands
# without checking the result for some additional code coverage.
dev[0].request("SET radio_disabled 1")
dev[0].request("SET radio_disabled 0")
def test_wpas_ctrl_set_tdls_trigger_control(dev):
"""wpa_supplicant SET tdls_trigger_control"""
# This is not supported with upstream nl80211, but execute the commands
# without checking the result for some additional code coverage.
dev[0].request("SET tdls_trigger_control 1")
dev[0].request("SET tdls_trigger_control 0")
def test_wpas_ctrl_set_sched_scan_relative_rssi(dev):
"""wpa_supplicant SET relative RSSI"""
tests = ["relative_rssi -1",
"relative_rssi 101",
"relative_band_adjust 2G",
"relative_band_adjust 2G:-101",
"relative_band_adjust 2G:101",
"relative_band_adjust 3G:1"]
for t in tests:
if "FAIL" not in dev[0].request("SET " + t):
raise Exception("No failure reported for SET " + t)
tests = ["relative_rssi 0",
"relative_rssi 10",
"relative_rssi disable",
"relative_band_adjust 2G:-1",
"relative_band_adjust 2G:0",
"relative_band_adjust 2G:1",
"relative_band_adjust 5G:-1",
"relative_band_adjust 5G:1",
"relative_band_adjust 5G:0"]
for t in tests:
if "OK" not in dev[0].request("SET " + t):
raise Exception("Failed to SET " + t)
def test_wpas_ctrl_get_pref_freq_list_override(dev):
"""wpa_supplicant get_pref_freq_list_override"""
if dev[0].request("GET_PREF_FREQ_LIST ").strip() != "FAIL":
raise Exception("Invalid GET_PREF_FREQ_LIST accepted")
dev[0].set("get_pref_freq_list_override", "foo")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "FAIL":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "1234:1,2,3 0")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "FAIL":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "1234:1,2,3 0:")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "0":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "0:1,2")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "1,2":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "1:3,4 0:1,2 2:5,6")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "1,2":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "1:3,4 0:1 2:5,6")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "1":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "0:1000,1001 2:1002,1003 3:1004,1005 4:1006,1007 8:1010,1011 9:1008,1009")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
if res != "1000,1001":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
res = dev[0].request("GET_PREF_FREQ_LIST AP").strip()
if res != "1002,1003":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
res = dev[0].request("GET_PREF_FREQ_LIST P2P_GO").strip()
if res != "1004,1005":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
res = dev[0].request("GET_PREF_FREQ_LIST P2P_CLIENT").strip()
if res != "1006,1007":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
res = dev[0].request("GET_PREF_FREQ_LIST IBSS").strip()
if res != "1008,1009":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
res = dev[0].request("GET_PREF_FREQ_LIST TDLS").strip()
if res != "1010,1011":
raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
dev[0].set("get_pref_freq_list_override", "")
res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
logger.info("STATION (without override): " + res)
+
+def test_wpas_ctrl_interface_add_driver_init_failure(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD and driver init failing"""
+ for i in range(1000):
+ res = dev[0].global_request("INTERFACE_ADD FOO")
+ if "FAIL" not in res:
+ raise Exception("Unexpected result: " + res)
+ dev[0].dump_monitor()
diff --git a/tests/hwsim/vm/inside.sh b/tests/hwsim/vm/inside.sh
index 6ccad4bec487..9d4a933fe729 100755
--- a/tests/hwsim/vm/inside.sh
+++ b/tests/hwsim/vm/inside.sh
@@ -1,164 +1,169 @@
#!/bin/sh
# keep old /etc
mount tmpfs -t tmpfs /tmp
mkdir /tmp/etc
mount --bind /etc /tmp/etc
# mount all kinds of things
mount tmpfs -t tmpfs /etc
# we need our own /dev/rfkill, and don't want device access
mount tmpfs -t tmpfs /dev
# some sockets go into /var/run, and / is read-only
mount tmpfs -t tmpfs /var/run
mount proc -t proc /proc
mount sysfs -t sysfs /sys
# needed for tracing
mount debugfs -t debugfs /sys/kernel/debug
+mkdir /tmp/wireshark-share
+mount --bind /usr/share/wireshark /tmp/wireshark-share
+mount tmpfs -t tmpfs /usr/share/wireshark
+
# for inside telnet
mkdir /dev/pts
mount devpts -t devpts /dev/pts
export PATH=/usr/sbin:$PATH
+export HOME=/tmp
# reboot on any sort of crash
sysctl kernel.panic_on_oops=1
sysctl kernel.panic=1
# get extra command line variables from /proc/cmdline
TESTDIR=$(sed 's/.*testdir=\([^ ]*\) .*/\1/' /proc/cmdline)
TIMEWARP=$(sed 's/.*timewarp=\([^ ]*\) .*/\1/' /proc/cmdline)
EPATH=$(sed 's/.*EPATH=\([^ ]*\) .*/\1/' /proc/cmdline)
TELNET=$(sed 's/.*TELNET=\([^ ]*\) .*/\1/' /proc/cmdline)
ARGS=$(sed 's/.*ARGS=\([^ ]*\)\( \|$\).*/\1/' /proc/cmdline)
LOGDIR=$(sed 's/.*LOGDIR=\([^ ]*\)\( \|$\).*/\1/' /proc/cmdline)
# create /dev entries we need
mknod -m 660 /dev/ttyS0 c 4 64
mknod -m 666 /dev/ptmx c 5 2
mknod -m 660 /dev/random c 1 8
mknod -m 660 /dev/urandom c 1 9
mknod -m 666 /dev/null c 1 3
mknod -m 666 /dev/kmsg c 1 11
test -f /sys/class/misc/rfkill/dev && \
mknod -m 660 /dev/rfkill c $(cat /sys/class/misc/rfkill/dev | tr ':' ' ')
ln -s /proc/self/fd/0 /dev/stdin
ln -s /proc/self/fd/1 /dev/stdout
ln -s /proc/self/fd/2 /dev/stderr
echo "VM has started up" > /dev/ttyS0
# create dummy sudo - everything runs as uid 0
mkdir /tmp/bin
cat > /tmp/bin/sudo << EOF
#!/bin/bash
exec "\$@"
EOF
chmod +x /tmp/bin/sudo
# and put it into $PATH, as well as our extra-$PATH
export PATH=/tmp/bin:$EPATH:$PATH
# some tests assume adm/admin group(s) exist(s)
cat > /etc/group <<EOF
adm:x:0:
admin:x:0:
messagebus:x:106:
EOF
# root should exist
cat > /etc/passwd <<EOF
root:x:0:0:root:/tmp:/bin/bash
messagebus:x:102:106::/var/run/dbus:/bin/false
EOF
cat > /etc/ethertypes <<EOF
IPv4 0800 ip ip4
ARP 0806 ether-arp
IPv6 86DD ip6
EOF
cat > /etc/protocols <<EOF
ip 0 IP
icmp 1 ICMP
tcp 6 TCP
udp 17 UDP
ipv6-icmp 58 IPv6-ICMP
EOF
# we may need /etc/alternatives, at least on Debian-based systems
ln -s /tmp/etc/alternatives /etc/
# local network is needed for some tests
ip link set lo up
# create logs mountpoint and mount the logshare
mkdir /tmp/logs
if grep -q rootfstype=hostfs /proc/cmdline; then
mount -t hostfs none /tmp/logs -o $LOGDIR
else
mount -t 9p -o trans=virtio,rw logshare /tmp/logs
fi
# allow access to any outside directory (e.g. /tmp) we also have
mkdir /tmp/host
mount --bind / /tmp/host
if [ "$TIMEWARP" = "1" ] ; then
(
while sleep 1 ; do
date --set "@$(($(date +%s) + 19))"
done
) &
fi
echo hwsimvm > /proc/sys/kernel/hostname
echo 8 8 8 8 > /proc/sys/kernel/printk
cat > /tmp/bin/login <<EOF
#!/bin/sh
export PS1='\h:\w\$ '
exec bash
EOF
chmod +x /tmp/bin/login
if [ "$TELNET" = "1" ] ; then
ip link set eth0 up
ip addr add 172.16.0.15/24 dev eth0
which in.telnetd >/dev/null && (
while true ; do
in.telnetd -debug 23 -L /tmp/bin/login
done
) &
fi
# check if we're rebooting due to a kernel panic ...
if grep -q 'Kernel panic' /tmp/logs/console ; then
echo "KERNEL CRASHED!" >/dev/ttyS0
else
# finally run the tests
export USER=0
export LOGDIR=/tmp/logs
export DBFILE=$LOGDIR/results.db
export PREFILL_DB=y
# some tests need CRDA, install a simple uevent helper
# and preload the 00 domain it will have asked for already
echo $TESTDIR/vm/uevent.sh > /sys/kernel/uevent_helper
COUNTRY=00 crda
mkdir -p /var/run/dbus
touch /var/run/dbus/hwsim-test
chown messagebus.messagebus /var/run/dbus
dbus-daemon --config-file=$TESTDIR/vm/dbus.conf --fork
cd $TESTDIR
./run-all.sh --vm $(cat /tmp/host$ARGS) </dev/ttyS0 >/dev/ttyS0 2>&1
if test -d /sys/kernel/debug/gcov ; then
cp -ar /sys/kernel/debug/gcov /tmp/logs/
# these are broken as they're updated while being read ...
find /tmp/logs/gcov/ -wholename '*kernel/gcov/*' -print0 | xargs -0 rm
fi
#bash </dev/ttyS0 >/dev/ttyS0 2>&1
fi
# and shut down the machine again
halt -f -p
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b54d7d4968d8..bf83e41686a0 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,12319 +1,12473 @@
/*
* 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 "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_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);
#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 {
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 (rsnxe && rsnxe[1] >= 1) {
- if (rsnxe[2] & BIT(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 (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
- ret = os_snprintf(pos, end - pos, "[SAE-PK]");
- if (os_snprintf_error(end - pos, ret))
- return -1;
- pos += ret;
- }
+ 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 (rsnxe && rsnxe[1] >= 1) {
- if (rsnxe[2] & BIT(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 (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
- ret = os_snprintf(pos, end - pos, "[SAE-PK]");
- if (os_snprintf_error(end - pos, ret))
- return -1;
- pos += ret;
- }
+ 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;
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, "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);
}
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) */
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;
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;
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;
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);
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;
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;
return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
max_oper_chwidth, pref_freq, he, edmg);
}
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;
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;
}
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);
}
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)
{
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);
}
static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
{
int freq = 0, persistent = 0, group_id = -1;
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 {
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 (group_id >= 0)
return p2p_ctrl_group_add_persistent(wpa_s, group_id,
freq, freq2, ht40, vht,
max_oper_chwidth, he,
edmg);
return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
max_oper_chwidth, he, edmg);
}
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_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->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 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;
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;
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;
- int id = 0;
+ 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))
- return -1;
+ 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);
- return -1;
+ goto out;
}
}
if (!got_bssid || akmp == -1 || cipher == -1 || group == 0xFFFF) {
wpa_printf(MSG_DEBUG,"CTRL: PASN missing parameter");
- return -1;
+ goto out;
}
- return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id);
+ 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 */
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;
#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, "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 {
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/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index 510668d49353..dfbd25a03b1b 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -1,163 +1,167 @@
/*
* WPA Supplicant / UNIX domain socket -based control interface
* 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.
*/
#ifndef CTRL_IFACE_H
#define CTRL_IFACE_H
#ifdef CONFIG_CTRL_IFACE
#ifndef CTRL_IFACE_MAX_LEN
#define CTRL_IFACE_MAX_LEN 8192
#endif /* CTRL_IFACE_MAX_LEN */
/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
/**
* wpa_supplicant_ctrl_iface_process - Process ctrl_iface command
* @wpa_s: Pointer to wpa_supplicant data
* @buf: Received command buffer (nul terminated string)
* @resp_len: Variable to be set to the response length
* Returns: Response (*resp_len bytes) or %NULL on failure
*
* Control interface backends call this function when receiving a message that
* they do not process internally, i.e., anything else than ATTACH, DETACH,
* and LEVEL. The return response value is then sent to the external program
* that sent the command. Caller is responsible for freeing the buffer after
* this. If %NULL is returned, *resp_len can be set to two special values:
* 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any
* other value, no response is sent.
*/
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len);
/**
* wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
* @global: Pointer to global data from wpa_supplicant_init()
* @buf: Received command buffer (nul terminated string)
* @resp_len: Variable to be set to the response length
* Returns: Response (*resp_len bytes) or %NULL on failure
*
* Control interface backends call this function when receiving a message from
* the global ctrl_iface connection. The return response value is then sent to
* the external program that sent the command. Caller is responsible for
* freeing the buffer after this. If %NULL is returned, *resp_len can be set to
* two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If
* *resp_len has any other value, no response is sent.
*/
char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
char *buf, size_t *resp_len);
/* Functions that each ctrl_iface backend must implement */
/**
* wpa_supplicant_ctrl_iface_init - Initialize control interface
* @wpa_s: Pointer to wpa_supplicant data
* Returns: Pointer to private data on success, %NULL on failure
*
* Initialize the control interface and start receiving commands from external
* programs.
*
* Required to be implemented in each control interface backend.
*/
struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s);
/**
* wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
* @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
*
* Deinitialize the control interface that was initialized with
- * wpa_supplicant_ctrl_iface_init().
+ * wpa_supplicant_ctrl_iface_init() and any data related to the wpa_s instance.
+ * @priv may be %NULL if the control interface has not yet been initialized.
*
* Required to be implemented in each control interface backend.
*/
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv);
/**
* wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
* @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
*
* Wait until the first message from an external program using the control
* interface is received. This function can be used to delay normal startup
* processing to allow control interface programs to attach with
* %wpa_supplicant before normal operations are started.
*
* Required to be implemented in each control interface backend.
*/
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
/**
* wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
* @global: Pointer to global data from wpa_supplicant_init()
* Returns: Pointer to private data on success, %NULL on failure
*
* Initialize the global control interface and start receiving commands from
* external programs.
*
* Required to be implemented in each control interface backend.
*/
struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
/**
* wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface
* @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init()
*
* Deinitialize the global control interface that was initialized with
* wpa_supplicant_global_ctrl_iface_init().
*
* Required to be implemented in each control interface backend.
*/
void wpa_supplicant_global_ctrl_iface_deinit(
struct ctrl_iface_global_priv *priv);
void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
#else /* CONFIG_CTRL_IFACE */
static inline struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
return (void *) -1;
}
static inline void
-wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
{
}
static inline void
wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level,
char *buf, size_t len)
{
}
static inline void
wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
{
}
static inline struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
return (void *) 1;
}
static inline void
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
{
}
static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
{
}
#endif /* CONFIG_CTRL_IFACE */
#endif /* CTRL_IFACE_H */
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 79ff7871db8d..bddc0414245e 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -1,828 +1,831 @@
/*
* WPA Supplicant / Windows Named Pipe -based control interface
* Copyright (c) 2004-2006, 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 "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
#include "common/wpa_ctrl.h"
#ifdef __MINGW32_VERSION
/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
*/
#define SDDL_REVISION_1 1
BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
#ifdef UNICODE
#define ConvertStringSecurityDescriptorToSecurityDescriptor \
ConvertStringSecurityDescriptorToSecurityDescriptorW
#else
#define ConvertStringSecurityDescriptorToSecurityDescriptor \
ConvertStringSecurityDescriptorToSecurityDescriptorA
#endif
#else /* __MINGW32_VERSION */
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
#include <sddl.h>
#endif /* __MINGW32_VERSION */
#ifndef WPA_SUPPLICANT_NAMED_PIPE
#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
#endif
#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
/* Per-interface ctrl_iface */
#define REQUEST_BUFSIZE CTRL_IFACE_MAX_LEN
#define REPLY_BUFSIZE 4096
struct ctrl_iface_priv;
/**
* struct wpa_ctrl_dst - Internal data structure of control interface clients
*
* This structure is used to store information about registered control
* interface monitors into struct wpa_supplicant. This data is private to
* ctrl_iface_named_pipe.c and should not be touched directly from other files.
*/
struct wpa_ctrl_dst {
/* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
OVERLAPPED overlap;
struct wpa_ctrl_dst *next, *prev;
struct ctrl_iface_priv *priv;
HANDLE pipe;
int attached;
int debug_level;
int errors;
char req_buf[REQUEST_BUFSIZE];
char *rsp_buf;
int used;
};
struct ctrl_iface_priv {
struct wpa_supplicant *wpa_s;
struct wpa_ctrl_dst *ctrl_dst;
SECURITY_ATTRIBUTES attr;
int sec_attr_set;
};
static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
int level, const char *buf,
size_t len);
static void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
static void wpa_supplicant_ctrl_iface_receive(void *, void *);
static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap);
struct wpa_global_dst;
static void global_close_pipe(struct wpa_global_dst *dst);
static void wpa_supplicant_global_iface_receive(void *eloop_data,
void *user_ctx);
static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap);
static int ctrl_broken_pipe(HANDLE pipe, int used)
{
DWORD err;
if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
return 0;
err = GetLastError();
if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used))
return 1;
return 0;
}
static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
{
struct wpa_ctrl_dst *dst, *next;
dst = priv->ctrl_dst;
while (dst) {
next = dst->next;
if (ctrl_broken_pipe(dst->pipe, dst->used)) {
wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
dst);
ctrl_close_pipe(dst);
}
dst = next;
}
}
static int ctrl_open_pipe(struct ctrl_iface_priv *priv)
{
struct wpa_ctrl_dst *dst;
DWORD err;
TCHAR name[256];
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
dst->priv = priv;
dst->debug_level = MSG_INFO;
dst->pipe = INVALID_HANDLE_VALUE;
dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (dst->overlap.hEvent == NULL) {
wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
(int) GetLastError());
goto fail;
}
eloop_register_event(dst->overlap.hEvent,
sizeof(dst->overlap.hEvent),
wpa_supplicant_ctrl_iface_receive, dst, NULL);
#ifdef UNICODE
_snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
priv->wpa_s->ifname);
#else /* UNICODE */
os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
priv->wpa_s->ifname);
#endif /* UNICODE */
/* TODO: add support for configuring access list for the pipe */
dst->pipe = CreateNamedPipe(name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
1000,
priv->sec_attr_set ? &priv->attr : NULL);
if (dst->pipe == INVALID_HANDLE_VALUE) {
wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
(int) GetLastError());
goto fail;
}
if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
(int) GetLastError());
CloseHandle(dst->pipe);
os_free(dst);
return -1;
}
err = GetLastError();
switch (err) {
case ERROR_IO_PENDING:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
"progress");
break;
case ERROR_PIPE_CONNECTED:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
"connected");
if (SetEvent(dst->overlap.hEvent))
break;
/* fall through */
default:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
(int) err);
CloseHandle(dst->pipe);
os_free(dst);
return -1;
}
dst->next = priv->ctrl_dst;
if (dst->next)
dst->next->prev = dst;
priv->ctrl_dst = dst;
return 0;
fail:
ctrl_close_pipe(dst);
return -1;
}
static void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
{
wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
if (dst->overlap.hEvent) {
eloop_unregister_event(dst->overlap.hEvent,
sizeof(dst->overlap.hEvent));
CloseHandle(dst->overlap.hEvent);
}
if (dst->pipe != INVALID_HANDLE_VALUE) {
/*
* Could use FlushFileBuffers() here to guarantee that all data
* gets delivered to the client, but that can block, so let's
* not do this for now.
* FlushFileBuffers(dst->pipe);
*/
CloseHandle(dst->pipe);
}
if (dst->prev)
dst->prev->next = dst->next;
else
dst->priv->ctrl_dst = dst->next;
if (dst->next)
dst->next->prev = dst->prev;
os_free(dst->rsp_buf);
os_free(dst);
}
static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap)
{
struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
"err=%d bytes=%d", dst, (int) err, (int) bytes);
if (err) {
ctrl_close_pipe(dst);
return;
}
os_free(dst->rsp_buf);
dst->rsp_buf = NULL;
if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
&dst->overlap, ctrl_iface_read_completed)) {
wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
(int) GetLastError());
ctrl_close_pipe(dst);
return;
}
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
}
static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
{
struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
char *reply = NULL, *send_buf;
size_t reply_len = 0, send_len;
int new_attached = 0;
char *buf = dst->req_buf;
dst->used = 1;
if (len >= REQUEST_BUFSIZE)
len = REQUEST_BUFSIZE - 1;
buf[len] = '\0';
if (os_strcmp(buf, "ATTACH") == 0) {
dst->attached = 1;
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
new_attached = 1;
reply_len = 2;
} else if (os_strcmp(buf, "DETACH") == 0) {
dst->attached = 0;
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
reply_len = 2;
} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
dst->debug_level = atoi(buf + 6);
reply_len = 2;
} else {
reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
&reply_len);
}
if (reply) {
send_buf = reply;
send_len = reply_len;
} else if (reply_len == 2) {
send_buf = "OK\n";
send_len = 3;
} else {
send_buf = "FAIL\n";
send_len = 5;
}
os_free(dst->rsp_buf);
dst->rsp_buf = os_memdup(send_buf, send_len);
if (dst->rsp_buf == NULL) {
ctrl_close_pipe(dst);
os_free(reply);
return;
}
os_free(reply);
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
ctrl_iface_write_completed)) {
wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
(int) GetLastError());
ctrl_close_pipe(dst);
} else {
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
dst);
}
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap)
{
struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
"bytes=%d", dst, (int) err, (int) bytes);
if (err == 0 && bytes > 0)
wpa_supplicant_ctrl_iface_rx(dst, bytes);
}
static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
{
struct wpa_ctrl_dst *dst = eloop_data;
struct ctrl_iface_priv *priv = dst->priv;
DWORD bytes;
wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
ResetEvent(dst->overlap.hEvent);
if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
(int) GetLastError());
return;
}
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
"connected");
/* Open a new named pipe for the next client. */
ctrl_open_pipe(priv);
/* Use write completion function to start reading a command */
ctrl_iface_write_completed(0, 0, &dst->overlap);
ctrl_flush_broken_pipes(priv);
}
static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
{
const char *sddl = NULL;
TCHAR *t_sddl;
if (os_strncmp(params, "SDDL=", 5) == 0)
sddl = params + 5;
if (!sddl) {
sddl = os_strstr(params, " SDDL=");
if (sddl)
sddl += 6;
}
if (!sddl)
return 0;
wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
os_memset(&priv->attr, 0, sizeof(priv->attr));
priv->attr.nLength = sizeof(priv->attr);
priv->attr.bInheritHandle = FALSE;
t_sddl = wpa_strdup_tchar(sddl);
if (t_sddl == NULL)
return -1;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
t_sddl, SDDL_REVISION_1,
(PSECURITY_DESCRIPTOR *) (void *)
&priv->attr.lpSecurityDescriptor,
NULL)) {
os_free(t_sddl);
wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
"security descriptor: %d",
sddl, (int) GetLastError());
return -1;
}
os_free(t_sddl);
priv->sec_attr_set = 1;
return 0;
}
static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
return;
wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
}
struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
priv->wpa_s = wpa_s;
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
os_free(priv);
return NULL;
}
if (ctrl_open_pipe(priv) < 0) {
os_free(priv);
return NULL;
}
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
}
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
{
+ if (!priv)
+ return;
while (priv->ctrl_dst)
ctrl_close_pipe(priv->ctrl_dst);
if (priv->sec_attr_set)
LocalFree(priv->attr.lpSecurityDescriptor);
os_free(priv);
}
static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
int level, const char *buf,
size_t len)
{
struct wpa_ctrl_dst *dst, *next;
char levelstr[10];
int idx;
char *sbuf;
int llen;
DWORD written;
dst = priv->ctrl_dst;
if (dst == NULL)
return;
os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
llen = os_strlen(levelstr);
sbuf = os_malloc(llen + len);
if (sbuf == NULL)
return;
os_memcpy(sbuf, levelstr, llen);
os_memcpy(sbuf + llen, buf, len);
idx = 0;
while (dst) {
next = dst->next;
if (dst->attached && level >= dst->debug_level) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
dst);
if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
NULL)) {
wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
"%p failed: %d",
dst, (int) GetLastError());
dst->errors++;
if (dst->errors > 10)
ctrl_close_pipe(dst);
} else
dst->errors = 0;
}
idx++;
dst = next;
}
os_free(sbuf);
}
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
{
wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
priv->wpa_s->ifname);
if (priv->ctrl_dst == NULL)
return;
WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
}
/* Global ctrl_iface */
struct ctrl_iface_global_priv;
struct wpa_global_dst {
/* Note: OVERLAPPED must be the first member of struct wpa_global_dst
*/
OVERLAPPED overlap;
struct wpa_global_dst *next, *prev;
struct ctrl_iface_global_priv *priv;
HANDLE pipe;
char req_buf[REQUEST_BUFSIZE];
char *rsp_buf;
int used;
};
struct ctrl_iface_global_priv {
struct wpa_global *global;
struct wpa_global_dst *ctrl_dst;
};
static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
{
struct wpa_global_dst *dst, *next;
dst = priv->ctrl_dst;
while (dst) {
next = dst->next;
if (ctrl_broken_pipe(dst->pipe, dst->used)) {
wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
dst);
global_close_pipe(dst);
}
dst = next;
}
}
static int global_open_pipe(struct ctrl_iface_global_priv *priv)
{
struct wpa_global_dst *dst;
DWORD err;
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
dst->priv = priv;
dst->pipe = INVALID_HANDLE_VALUE;
dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (dst->overlap.hEvent == NULL) {
wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
(int) GetLastError());
goto fail;
}
eloop_register_event(dst->overlap.hEvent,
sizeof(dst->overlap.hEvent),
wpa_supplicant_global_iface_receive, dst, NULL);
/* TODO: add support for configuring access list for the pipe */
dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
1000, NULL);
if (dst->pipe == INVALID_HANDLE_VALUE) {
wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
(int) GetLastError());
goto fail;
}
if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
(int) GetLastError());
CloseHandle(dst->pipe);
os_free(dst);
return -1;
}
err = GetLastError();
switch (err) {
case ERROR_IO_PENDING:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
"progress");
break;
case ERROR_PIPE_CONNECTED:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
"connected");
if (SetEvent(dst->overlap.hEvent))
break;
/* fall through */
default:
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
(int) err);
CloseHandle(dst->pipe);
os_free(dst);
return -1;
}
dst->next = priv->ctrl_dst;
if (dst->next)
dst->next->prev = dst;
priv->ctrl_dst = dst;
return 0;
fail:
global_close_pipe(dst);
return -1;
}
static void global_close_pipe(struct wpa_global_dst *dst)
{
wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
if (dst->overlap.hEvent) {
eloop_unregister_event(dst->overlap.hEvent,
sizeof(dst->overlap.hEvent));
CloseHandle(dst->overlap.hEvent);
}
if (dst->pipe != INVALID_HANDLE_VALUE) {
/*
* Could use FlushFileBuffers() here to guarantee that all data
* gets delivered to the client, but that can block, so let's
* not do this for now.
* FlushFileBuffers(dst->pipe);
*/
CloseHandle(dst->pipe);
}
if (dst->prev)
dst->prev->next = dst->next;
else
dst->priv->ctrl_dst = dst->next;
if (dst->next)
dst->next->prev = dst->prev;
os_free(dst->rsp_buf);
os_free(dst);
}
static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap)
{
struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
"err=%d bytes=%d", dst, (int) err, (int) bytes);
if (err) {
global_close_pipe(dst);
return;
}
os_free(dst->rsp_buf);
dst->rsp_buf = NULL;
if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
&dst->overlap, global_iface_read_completed)) {
wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
(int) GetLastError());
global_close_pipe(dst);
/* FIX: if this was the pipe waiting for new global
* connections, at this point there are no open global pipes..
* Should try to open a new pipe.. */
return;
}
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
}
static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
size_t len)
{
struct wpa_global *global = dst->priv->global;
char *reply = NULL, *send_buf;
size_t reply_len = 0, send_len;
char *buf = dst->req_buf;
dst->used = 1;
if (len >= REQUEST_BUFSIZE)
len = REQUEST_BUFSIZE - 1;
buf[len] = '\0';
reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
&reply_len);
if (reply) {
send_buf = reply;
send_len = reply_len;
} else if (reply_len) {
send_buf = "FAIL\n";
send_len = 5;
} else {
os_free(dst->rsp_buf);
dst->rsp_buf = NULL;
return;
}
os_free(dst->rsp_buf);
dst->rsp_buf = os_memdup(send_buf, send_len);
if (dst->rsp_buf == NULL) {
global_close_pipe(dst);
os_free(reply);
return;
}
os_free(reply);
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
global_iface_write_completed)) {
wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
(int) GetLastError());
global_close_pipe(dst);
} else {
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
dst);
}
}
static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
LPOVERLAPPED overlap)
{
struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
"bytes=%d", dst, (int) err, (int) bytes);
if (err == 0 && bytes > 0)
wpa_supplicant_global_iface_rx(dst, bytes);
}
static void wpa_supplicant_global_iface_receive(void *eloop_data,
void *user_ctx)
{
struct wpa_global_dst *dst = eloop_data;
struct ctrl_iface_global_priv *priv = dst->priv;
DWORD bytes;
wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
ResetEvent(dst->overlap.hEvent);
if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
(int) GetLastError());
return;
}
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
"connected");
/* Open a new named pipe for the next client. */
if (global_open_pipe(priv) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed");
return;
}
/* Use write completion function to start reading a command */
global_iface_write_completed(0, 0, &dst->overlap);
global_flush_broken_pipes(priv);
}
struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
priv->global = global;
if (global_open_pipe(priv) < 0) {
os_free(priv);
return NULL;
}
return priv;
}
void
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
{
while (priv->ctrl_dst)
global_close_pipe(priv->ctrl_dst);
os_free(priv);
}
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1512080d6b3b..1cbf7fa28d3f 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -1,827 +1,831 @@
/*
* WPA Supplicant / UDP socket -based control interface
* 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 "includes.h"
#include "common.h"
#include "eloop.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
#include "common/wpa_ctrl.h"
#define COOKIE_LEN 8
/* Per-interface ctrl_iface */
/**
* struct wpa_ctrl_dst - Internal data structure of control interface monitors
*
* This structure is used to store information about registered control
* interface monitors into struct wpa_supplicant. This data is private to
* ctrl_iface_udp.c and should not be touched directly from other files.
*/
struct wpa_ctrl_dst {
struct wpa_ctrl_dst *next;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 addr;
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in addr;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t addrlen;
int debug_level;
int errors;
};
struct ctrl_iface_priv {
struct wpa_supplicant *wpa_s;
int sock;
struct wpa_ctrl_dst *ctrl_dst;
u8 cookie[COOKIE_LEN];
};
struct ctrl_iface_global_priv {
int sock;
struct wpa_ctrl_dst *ctrl_dst;
u8 cookie[COOKIE_LEN];
};
static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
const char *ifname, int sock,
struct wpa_ctrl_dst **head,
int level, const char *buf,
size_t len);
static void wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst *dst)
{
struct wpa_ctrl_dst *prev;
while (dst) {
prev = dst;
dst = dst->next;
os_free(prev);
}
}
static int wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst **head,
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 *from,
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_UDP_IPV6 */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
os_memcpy(&dst->addr, from, sizeof(*from));
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
dst->next = *head;
*head = dst;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
ntohs(from->sin6_port));
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
inet_ntoa(from->sin_addr), ntohs(from->sin_port));
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
return 0;
}
static int wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst **head,
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 *from,
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst, *prev = NULL;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = *head;
while (dst) {
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
if (from->sin6_port == dst->addr.sin6_port &&
!os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
sizeof(from->sin6_addr))) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
inet_ntop(AF_INET6, &from->sin6_addr, addr,
sizeof(*from)),
ntohs(from->sin6_port));
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
"%s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (prev == NULL)
*head = dst->next;
else
prev->next = dst->next;
os_free(dst);
return 0;
}
prev = dst;
dst = dst->next;
}
return -1;
}
static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 *from,
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen,
char *level)
{
struct wpa_ctrl_dst *dst;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
dst = priv->ctrl_dst;
while (dst) {
#if CONFIG_CTRL_IFACE_UDP_IPV6
if (from->sin6_port == dst->addr.sin6_port &&
!os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
sizeof(from->sin6_addr))) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
inet_ntop(AF_INET6, &from->sin6_addr, addr,
sizeof(*from)),
ntohs(from->sin6_port));
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
"level %s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst->debug_level = atoi(level);
return 0;
}
dst = dst->next;
}
return -1;
}
static char *
wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
size_t *reply_len)
{
char *reply;
reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
if (reply == NULL) {
*reply_len = 1;
return NULL;
}
os_memcpy(reply, "COOKIE=", 7);
wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
priv->cookie, COOKIE_LEN);
*reply_len = 7 + 2 * COOKIE_LEN;
return reply;
}
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
char *buf, *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in from;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen = sizeof(from);
char *reply = NULL;
size_t reply_len = 0;
int new_attached = 0;
u8 cookie[COOKIE_LEN];
buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
if (!buf)
return;
res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
os_free(buf);
return;
}
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
if (os_strcmp(addr, "::1")) {
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
addr);
os_free(buf);
return;
}
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
* frames since the socket is bound to only localhost address.
* Just in case, drop the frame if it is coming from any other
* address.
*/
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
"source %s", inet_ntoa(from.sin_addr));
os_free(buf);
return;
}
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
if ((size_t) res > CTRL_IFACE_MAX_LEN) {
wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
os_free(buf);
return;
}
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
goto done;
}
/*
* Require that the client includes a prefix with the 'cookie' value
* fetched with GET_COOKIE command. This is used to verify that the
* client has access to a bidirectional link over UDP in order to
* avoid attacks using forged localhost IP address even if the OS does
* not block such frames from remote destinations.
*/
if (os_strncmp(buf, "COOKIE=", 7) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
"drop request");
os_free(buf);
return;
}
if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
"request - drop request");
os_free(buf);
return;
}
if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
"drop request");
os_free(buf);
return;
}
pos = buf + 7 + 2 * COOKIE_LEN;
while (*pos == ' ')
pos++;
if (os_strcmp(pos, "ATTACH") == 0) {
if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
&from, fromlen))
reply_len = 1;
else {
new_attached = 1;
reply_len = 2;
}
} else if (os_strcmp(pos, "DETACH") == 0) {
if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
&from, fromlen))
reply_len = 1;
else
reply_len = 2;
} else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
pos + 6))
reply_len = 1;
else
reply_len = 2;
} else {
reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
&reply_len);
}
done:
if (reply) {
sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen);
os_free(reply);
} else if (reply_len == 1) {
sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
fromlen);
} else if (reply_len == 2) {
sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
fromlen);
}
os_free(buf);
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
if (!wpa_s)
return;
if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
if (priv->ctrl_dst) {
wpa_supplicant_ctrl_iface_send(
wpa_s,
type != WPA_MSG_PER_INTERFACE ?
NULL : wpa_s->ifname,
priv->sock, &priv->ctrl_dst, level, txt, len);
}
}
if (type == WPA_MSG_ONLY_GLOBAL || !wpa_s->ctrl_iface)
return;
wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
&wpa_s->ctrl_iface->ctrl_dst,
level, txt, len);
}
struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
char port_str[40];
int port = WPA_CTRL_IFACE_PORT;
char *pos;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 addr;
int domain = PF_INET6;
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in addr;
int domain = PF_INET;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
priv->wpa_s = wpa_s;
priv->sock = -1;
os_get_random(priv->cookie, COOKIE_LEN);
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
pos = os_strstr(wpa_s->conf->ctrl_interface, "udp:");
if (pos) {
pos += 4;
port = atoi(pos);
if (port <= 0) {
wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port: %s",
wpa_s->conf->ctrl_interface);
goto fail;
}
}
priv->sock = socket(domain, SOCK_DGRAM, 0);
if (priv->sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
addr.sin6_family = AF_INET6;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
addr.sin6_addr = in6addr_any;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
inet_pton(AF_INET6, "::1", &addr.sin6_addr);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_family = AF_INET;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
addr.sin_addr.s_addr = INADDR_ANY;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
try_again:
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
addr.sin6_port = htons(port);
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_port = htons(port);
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
port--;
if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
goto try_again;
wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
goto fail;
}
/* Update the ctrl_interface value to match the selected port */
os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
os_free(wpa_s->conf->ctrl_interface);
wpa_s->conf->ctrl_interface = os_strdup(port_str);
if (!wpa_s->conf->ctrl_interface) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface");
goto fail;
}
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
fail:
if (priv->sock >= 0)
close(priv->sock);
os_free(priv);
return NULL;
}
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
{
+ if (!priv)
+ return;
+
if (priv->sock > -1) {
eloop_unregister_read_sock(priv->sock);
if (priv->ctrl_dst) {
/*
* Wait before closing the control socket if
* there are any attached monitors in order to allow
* them to receive any pending messages.
*/
wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
"monitors to receive messages");
os_sleep(0, 100000);
}
close(priv->sock);
priv->sock = -1;
}
wpas_ctrl_iface_free_dst(priv->ctrl_dst);
os_free(priv);
}
static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
const char *ifname, int sock,
struct wpa_ctrl_dst **head,
int level, const char *buf,
size_t len)
{
struct wpa_ctrl_dst *dst, *next;
char levelstr[64];
int idx;
char *sbuf;
int llen;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = *head;
if (sock < 0 || dst == NULL)
return;
if (ifname)
os_snprintf(levelstr, sizeof(levelstr), "IFNAME=%s <%d>",
ifname, level);
else
os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
llen = os_strlen(levelstr);
sbuf = os_malloc(llen + len);
if (sbuf == NULL)
return;
os_memcpy(sbuf, levelstr, llen);
os_memcpy(sbuf + llen, buf, len);
idx = 0;
while (dst) {
next = dst->next;
if (level >= dst->debug_level) {
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
inet_ntop(AF_INET6, &dst->addr.sin6_addr,
addr, sizeof(dst->addr)),
ntohs(dst->addr.sin6_port));
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
inet_ntoa(dst->addr.sin_addr),
ntohs(dst->addr.sin_port));
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (sendto(sock, sbuf, llen + len, 0,
(struct sockaddr *) &dst->addr,
sizeof(dst->addr)) < 0) {
wpa_printf(MSG_ERROR,
"sendto(CTRL_IFACE monitor): %s",
strerror(errno));
dst->errors++;
if (dst->errors > 10) {
wpa_supplicant_ctrl_iface_detach(
head, &dst->addr,
dst->addrlen);
}
} else
dst->errors = 0;
}
idx++;
dst = next;
}
os_free(sbuf);
}
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
{
wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
priv->wpa_s->ifname);
eloop_wait_for_read_sock(priv->sock);
}
/* Global ctrl_iface */
static char *
wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
size_t *reply_len)
{
char *reply;
reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
if (reply == NULL) {
*reply_len = 1;
return NULL;
}
os_memcpy(reply, "COOKIE=", 7);
wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
priv->cookie, COOKIE_LEN);
*reply_len = 7 + 2 * COOKIE_LEN;
return reply;
}
static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
char *buf, *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
char addr[INET6_ADDRSTRLEN];
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in from;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen = sizeof(from);
char *reply = NULL;
size_t reply_len;
u8 cookie[COOKIE_LEN];
buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
if (!buf)
return;
res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
os_free(buf);
return;
}
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
if (os_strcmp(addr, "::1")) {
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
addr);
os_free(buf);
return;
}
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
* frames since the socket is bound to only localhost address.
* Just in case, drop the frame if it is coming from any other
* address.
*/
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
"source %s", inet_ntoa(from.sin_addr));
os_free(buf);
return;
}
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
if ((size_t) res > CTRL_IFACE_MAX_LEN) {
wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
os_free(buf);
return;
}
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
goto done;
}
if (os_strncmp(buf, "COOKIE=", 7) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
"drop request");
os_free(buf);
return;
}
if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
"request - drop request");
os_free(buf);
return;
}
if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
"drop request");
os_free(buf);
return;
}
pos = buf + 7 + 2 * COOKIE_LEN;
while (*pos == ' ')
pos++;
if (os_strcmp(pos, "ATTACH") == 0) {
if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
&from, fromlen))
reply_len = 1;
else
reply_len = 2;
} else if (os_strcmp(pos, "DETACH") == 0) {
if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
&from, fromlen))
reply_len = 1;
else
reply_len = 2;
} else {
reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
&reply_len);
}
done:
if (reply) {
sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen);
os_free(reply);
} else if (reply_len == 1) {
sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
fromlen);
} else if (reply_len == 2) {
sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
fromlen);
}
os_free(buf);
}
struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv;
struct sockaddr_in addr;
char *pos;
int port = WPA_GLOBAL_CTRL_IFACE_PORT;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
priv->sock = -1;
os_get_random(priv->cookie, COOKIE_LEN);
if (global->params.ctrl_interface == NULL)
return priv;
wpa_printf(MSG_DEBUG, "Global control interface '%s'",
global->params.ctrl_interface);
pos = os_strstr(global->params.ctrl_interface, "udp:");
if (pos) {
pos += 4;
port = atoi(pos);
if (port <= 0) {
wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port %s",
global->params.ctrl_interface);
goto fail;
}
}
priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
if (priv->sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
addr.sin_addr.s_addr = INADDR_ANY;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
try_again:
addr.sin_port = htons(port);
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
port++;
if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
goto try_again;
wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
goto fail;
}
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
eloop_register_read_sock(priv->sock,
wpa_supplicant_global_ctrl_iface_receive,
global, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
fail:
if (priv->sock >= 0)
close(priv->sock);
os_free(priv);
return NULL;
}
void
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
{
if (priv->sock >= 0) {
eloop_unregister_read_sock(priv->sock);
close(priv->sock);
}
wpas_ctrl_iface_free_dst(priv->ctrl_dst);
os_free(priv);
}
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 953fd2ccfe50..639573dae75e 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -1,1390 +1,1431 @@
/*
* WPA Supplicant / UNIX domain socket -based control interface
* 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 "includes.h"
#include <sys/un.h>
#include <sys/stat.h>
#include <grp.h>
#include <stddef.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef __linux__
#include <sys/ioctl.h>
#endif /* __linux__ */
#ifdef ANDROID
#include <cutils/sockets.h>
#endif /* ANDROID */
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/list.h"
#include "common/ctrl_iface_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
/* Per-interface ctrl_iface */
struct ctrl_iface_priv {
struct wpa_supplicant *wpa_s;
int sock;
struct dl_list ctrl_dst;
int android_control_socket;
struct dl_list msg_queue;
unsigned int throttle_count;
};
struct ctrl_iface_global_priv {
struct wpa_global *global;
int sock;
struct dl_list ctrl_dst;
int android_control_socket;
struct dl_list msg_queue;
unsigned int throttle_count;
};
struct ctrl_iface_msg {
struct dl_list list;
struct wpa_supplicant *wpa_s;
int level;
enum wpa_msg_type type;
const char *txt;
size_t len;
};
static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
const char *ifname, int sock,
struct dl_list *ctrl_dst,
int level, const char *buf,
size_t len,
struct ctrl_iface_priv *priv,
struct ctrl_iface_global_priv *gp);
static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
struct ctrl_iface_priv *priv);
static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
struct ctrl_iface_global_priv *priv);
static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf,
size_t len)
{
#ifdef __linux__
socklen_t optlen;
int sndbuf, outq;
int level = MSG_MSGDUMP;
if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
level = MSG_EXCESSIVE;
optlen = sizeof(sndbuf);
sndbuf = 0;
if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
sndbuf = -1;
if (ioctl(sock, TIOCOUTQ, &outq) < 0)
outq = -1;
wpa_printf(level,
"CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d",
title, sock, sndbuf, outq, (int) len);
#endif /* __linux__ */
}
static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
struct sockaddr_storage *from,
socklen_t fromlen, int global)
{
return ctrl_iface_attach(ctrl_dst, from, fromlen, NULL);
}
static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
struct sockaddr_storage *from,
socklen_t fromlen)
{
return ctrl_iface_detach(ctrl_dst, from, fromlen);
}
static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
struct sockaddr_storage *from,
socklen_t fromlen,
char *level)
{
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
return ctrl_iface_level(&priv->ctrl_dst, from, fromlen, level);
}
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
char *buf;
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply = NULL, *reply_buf = NULL;
size_t reply_len = 0;
int new_attached = 0;
buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
if (!buf)
return;
res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
os_free(buf);
return;
}
if ((size_t) res > CTRL_IFACE_MAX_LEN) {
wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
os_free(buf);
return;
}
buf[res] = '\0';
if (os_strcmp(buf, "ATTACH") == 0) {
if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
fromlen, 0))
reply_len = 1;
else {
new_attached = 1;
reply_len = 2;
}
} else if (os_strcmp(buf, "DETACH") == 0) {
if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
fromlen))
reply_len = 1;
else
reply_len = 2;
} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
buf + 6))
reply_len = 1;
else
reply_len = 2;
} else {
reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
&reply_len);
reply = reply_buf;
/*
* There could be some password/key material in the command, so
* clear the buffer explicitly now that it is not needed
* anymore.
*/
os_memset(buf, 0, res);
}
if (!reply && reply_len == 1) {
reply = "FAIL\n";
reply_len = 5;
} else if (!reply && reply_len == 2) {
reply = "OK\n";
reply_len = 3;
}
if (reply) {
wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply,
reply_len);
if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen) < 0) {
int _errno = errno;
wpa_dbg(wpa_s, MSG_DEBUG,
"ctrl_iface sendto failed: %d - %s",
_errno, strerror(_errno));
if (_errno == ENOBUFS || _errno == EAGAIN) {
/*
* The socket send buffer could be full. This
* may happen if client programs are not
* receiving their pending messages. Close and
* reopen the socket as a workaround to avoid
* getting stuck being unable to send any new
* responses.
*/
sock = wpas_ctrl_iface_reinit(wpa_s, priv);
if (sock < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
}
}
if (new_attached) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
new_attached = 0;
wpa_supplicant_ctrl_iface_detach(
&priv->ctrl_dst, &from, fromlen);
}
}
}
os_free(reply_buf);
os_free(buf);
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
{
char *buf;
size_t len;
char *pbuf, *dir = NULL;
int res;
if (wpa_s->conf->ctrl_interface == NULL)
return NULL;
pbuf = os_strdup(wpa_s->conf->ctrl_interface);
if (pbuf == NULL)
return NULL;
if (os_strncmp(pbuf, "DIR=", 4) == 0) {
char *gid_str;
dir = pbuf + 4;
gid_str = os_strstr(dir, " GROUP=");
if (gid_str)
*gid_str = '\0';
} else
dir = pbuf;
len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2;
buf = os_malloc(len);
if (buf == NULL) {
os_free(pbuf);
return NULL;
}
res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
if (os_snprintf_error(len, res)) {
os_free(pbuf);
os_free(buf);
return NULL;
}
#ifdef __CYGWIN__
{
/* Windows/WinPcap uses interface names that are not suitable
* as a file name - convert invalid chars to underscores */
char *pos = buf;
while (*pos) {
if (*pos == '\\')
*pos = '_';
pos++;
}
}
#endif /* __CYGWIN__ */
os_free(pbuf);
return buf;
}
static int wpas_ctrl_iface_throttle(int sock)
{
#ifdef __linux__
socklen_t optlen;
int sndbuf, outq;
optlen = sizeof(sndbuf);
sndbuf = 0;
if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 ||
ioctl(sock, TIOCOUTQ, &outq) < 0 ||
sndbuf <= 0 || outq < 0)
return 0;
return outq > sndbuf / 2;
#else /* __linux__ */
return 0;
#endif /* __linux__ */
}
static void wpas_ctrl_msg_send_pending_global(struct wpa_global *global)
{
struct ctrl_iface_global_priv *gpriv;
struct ctrl_iface_msg *msg;
gpriv = global->ctrl_iface;
while (gpriv && !dl_list_empty(&gpriv->msg_queue) &&
!wpas_ctrl_iface_throttle(gpriv->sock)) {
msg = dl_list_first(&gpriv->msg_queue, struct ctrl_iface_msg,
list);
if (!msg)
break;
dl_list_del(&msg->list);
wpa_supplicant_ctrl_iface_send(
msg->wpa_s,
msg->type != WPA_MSG_PER_INTERFACE ?
NULL : msg->wpa_s->ifname,
gpriv->sock, &gpriv->ctrl_dst, msg->level,
msg->txt, msg->len, NULL, gpriv);
os_free(msg);
}
}
static void wpas_ctrl_msg_send_pending_iface(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
struct ctrl_iface_msg *msg;
priv = wpa_s->ctrl_iface;
while (priv && !dl_list_empty(&priv->msg_queue) &&
!wpas_ctrl_iface_throttle(priv->sock)) {
msg = dl_list_first(&priv->msg_queue, struct ctrl_iface_msg,
list);
if (!msg)
break;
dl_list_del(&msg->list);
wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock,
&priv->ctrl_dst, msg->level,
msg->txt, msg->len, priv, NULL);
os_free(msg);
}
}
static void wpas_ctrl_msg_queue_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv;
struct ctrl_iface_global_priv *gpriv;
int sock = -1, gsock = -1;
wpas_ctrl_msg_send_pending_global(wpa_s->global);
wpas_ctrl_msg_send_pending_iface(wpa_s);
priv = wpa_s->ctrl_iface;
if (priv && !dl_list_empty(&priv->msg_queue))
sock = priv->sock;
gpriv = wpa_s->global->ctrl_iface;
if (gpriv && !dl_list_empty(&gpriv->msg_queue))
gsock = gpriv->sock;
if (sock > -1 || gsock > -1) {
/* Continue pending message transmission from a timeout */
wpa_printf(MSG_MSGDUMP,
"CTRL: Had to throttle pending event message transmission for (sock %d gsock %d)",
sock, gsock);
eloop_register_timeout(0, 20000, wpas_ctrl_msg_queue_timeout,
wpa_s, NULL);
}
}
static void wpas_ctrl_msg_queue(struct dl_list *queue,
struct wpa_supplicant *wpa_s, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
struct ctrl_iface_msg *msg;
msg = os_zalloc(sizeof(*msg) + len);
if (!msg)
return;
msg->wpa_s = wpa_s;
msg->level = level;
msg->type = type;
os_memcpy(msg + 1, txt, len);
msg->txt = (const char *) (msg + 1);
msg->len = len;
dl_list_add_tail(queue, &msg->list);
eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
eloop_register_timeout(0, 0, wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
}
static void wpas_ctrl_msg_queue_limit(unsigned int throttle_count,
struct dl_list *queue)
{
struct ctrl_iface_msg *msg;
if (throttle_count < 2000)
return;
msg = dl_list_first(queue, struct ctrl_iface_msg, list);
if (msg) {
wpa_printf(MSG_DEBUG, "CTRL: Dropped oldest pending message");
dl_list_del(&msg->list);
os_free(msg);
}
}
static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
struct ctrl_iface_priv *priv;
struct ctrl_iface_global_priv *gpriv;
if (wpa_s == NULL)
return;
gpriv = wpa_s->global->ctrl_iface;
if (type != WPA_MSG_NO_GLOBAL && gpriv &&
!dl_list_empty(&gpriv->ctrl_dst)) {
if (!dl_list_empty(&gpriv->msg_queue) ||
wpas_ctrl_iface_throttle(gpriv->sock)) {
if (gpriv->throttle_count == 0) {
wpa_printf(MSG_MSGDUMP,
"CTRL: Had to throttle global event message for sock %d",
gpriv->sock);
}
gpriv->throttle_count++;
wpas_ctrl_msg_queue_limit(gpriv->throttle_count,
&gpriv->msg_queue);
wpas_ctrl_msg_queue(&gpriv->msg_queue, wpa_s, level,
type, txt, len);
} else {
if (gpriv->throttle_count) {
wpa_printf(MSG_MSGDUMP,
"CTRL: Had to throttle %u global event message(s) for sock %d",
gpriv->throttle_count, gpriv->sock);
}
gpriv->throttle_count = 0;
wpa_supplicant_ctrl_iface_send(
wpa_s,
type != WPA_MSG_PER_INTERFACE ?
NULL : wpa_s->ifname,
gpriv->sock, &gpriv->ctrl_dst, level,
txt, len, NULL, gpriv);
}
}
priv = wpa_s->ctrl_iface;
if (type != WPA_MSG_ONLY_GLOBAL && priv) {
if (!dl_list_empty(&priv->msg_queue) ||
wpas_ctrl_iface_throttle(priv->sock)) {
if (priv->throttle_count == 0) {
wpa_printf(MSG_MSGDUMP,
"CTRL: Had to throttle event message for sock %d",
priv->sock);
}
priv->throttle_count++;
wpas_ctrl_msg_queue_limit(priv->throttle_count,
&priv->msg_queue);
wpas_ctrl_msg_queue(&priv->msg_queue, wpa_s, level,
type, txt, len);
} else {
if (priv->throttle_count) {
wpa_printf(MSG_MSGDUMP,
"CTRL: Had to throttle %u event message(s) for sock %d",
priv->throttle_count, priv->sock);
}
priv->throttle_count = 0;
wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock,
&priv->ctrl_dst, level,
txt, len, priv, NULL);
}
}
}
static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
struct ctrl_iface_priv *priv)
{
struct sockaddr_un addr;
char *fname = NULL;
gid_t gid = 0;
int gid_set = 0;
char *buf, *dir = NULL, *gid_str = NULL;
struct group *grp;
char *endp;
int flags;
buf = os_strdup(wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto fail;
#ifdef ANDROID
os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
wpa_s->conf->ctrl_interface);
priv->sock = android_get_control_socket(addr.sun_path);
if (priv->sock >= 0) {
priv->android_control_socket = 1;
goto havesock;
}
#endif /* ANDROID */
if (os_strncmp(buf, "DIR=", 4) == 0) {
dir = buf + 4;
gid_str = os_strstr(dir, " GROUP=");
if (gid_str) {
*gid_str = '\0';
gid_str += 7;
}
} else {
dir = buf;
gid_str = wpa_s->conf->ctrl_interface_group;
}
if (mkdir(dir, 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]: %s",
dir, strerror(errno));
goto fail;
}
}
#ifdef ANDROID
/*
* wpa_supplicant is started from /init.*.rc on Android and that seems
* to be 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(dir, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
strerror(errno));
/* Try to continue anyway */
}
#endif /* ANDROID */
if (gid_str) {
grp = getgrnam(gid_str);
if (grp) {
gid = grp->gr_gid;
gid_set = 1;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
" (from group name '%s')",
(int) gid, gid_str);
} else {
/* Group name not found - try to parse this as gid */
gid = strtol(gid_str, &endp, 10);
if (*gid_str == '\0' || *endp != '\0') {
wpa_printf(MSG_ERROR, "CTRL: Invalid group "
"'%s'", gid_str);
goto fail;
}
gid_set = 1;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
(int) gid);
}
}
if (gid_set && lchown(dir, -1, gid) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface=%s,gid=%d]: %s",
dir, (int) gid, strerror(errno));
goto fail;
}
/* Make sure the group can enter and read the directory */
if (gid_set &&
chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) {
wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s",
strerror(errno));
goto fail;
}
if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >=
sizeof(addr.sun_path)) {
wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded");
goto fail;
}
priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (priv->sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
addr.sun_len = sizeof(addr);
#endif /* __FreeBSD__ */
addr.sun_family = AF_UNIX;
fname = wpa_supplicant_ctrl_iface_path(wpa_s);
if (fname == NULL)
goto fail;
os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
strerror(errno));
if (connect(priv->sock, (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(priv->sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: 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 (gid_set && lchown(fname, -1, gid) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface=%s,gid=%d]: %s",
fname, (int) gid, strerror(errno));
goto fail;
}
if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s",
fname, strerror(errno));
goto fail;
}
os_free(fname);
#ifdef ANDROID
havesock:
#endif /* ANDROID */
/*
* Make socket non-blocking so that we don't hang forever if
* target dies unexpectedly.
*/
flags = fcntl(priv->sock, F_GETFL);
if (flags >= 0) {
flags |= O_NONBLOCK;
if (fcntl(priv->sock, F_SETFL, flags) < 0) {
wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
strerror(errno));
/* Not fatal, continue on.*/
}
}
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
os_free(buf);
return 0;
fail:
if (priv->sock >= 0) {
close(priv->sock);
priv->sock = -1;
}
if (fname) {
unlink(fname);
os_free(fname);
}
os_free(buf);
return -1;
}
struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
dl_list_init(&priv->ctrl_dst);
dl_list_init(&priv->msg_queue);
priv->wpa_s = wpa_s;
priv->sock = -1;
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
#ifdef ANDROID
if (wpa_s->global->params.ctrl_interface) {
int same = 0;
if (wpa_s->global->params.ctrl_interface[0] == '/') {
if (os_strcmp(wpa_s->global->params.ctrl_interface,
wpa_s->conf->ctrl_interface) == 0)
same = 1;
} else if (os_strncmp(wpa_s->global->params.ctrl_interface,
"@android:", 9) == 0 ||
os_strncmp(wpa_s->global->params.ctrl_interface,
"@abstract:", 10) == 0) {
char *pos;
/*
* Currently, Android uses @android:wpa_* as the naming
* convention for the global ctrl interface. This logic
* needs to be revisited if the above naming convention
* is modified.
*/
pos = os_strchr(wpa_s->global->params.ctrl_interface,
'_');
if (pos &&
os_strcmp(pos + 1,
wpa_s->conf->ctrl_interface) == 0)
same = 1;
}
if (same) {
/*
* The invalid configuration combination might be
* possible to hit in an Android OTA upgrade case, so
* instead of refusing to start the wpa_supplicant
* process, do not open the per-interface ctrl_iface
* and continue with the global control interface that
* was set from the command line since the Wi-Fi
* framework will use it for operations.
*/
wpa_printf(MSG_ERROR,
"global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
wpa_s->global->params.ctrl_interface,
wpa_s->conf->ctrl_interface);
return priv;
}
}
#endif /* ANDROID */
if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
os_free(priv);
return NULL;
}
return priv;
}
static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
struct ctrl_iface_priv *priv)
{
int res;
if (priv->sock <= 0)
return -1;
/*
* On Android, the control socket being used may be the socket
* that is created when wpa_supplicant is started as a /init.*.rc
* service. Such a socket is maintained as a key-value pair in
* Android's environment. Closing this control socket would leave us
* in a bad state with an invalid socket descriptor.
*/
if (priv->android_control_socket)
return priv->sock;
eloop_unregister_read_sock(priv->sock);
close(priv->sock);
priv->sock = -1;
res = wpas_ctrl_iface_open_sock(wpa_s, priv);
if (res < 0)
return -1;
return priv->sock;
}
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+static void
+wpas_global_ctrl_iface_flush_queued_msg(struct wpa_global *global,
+ struct wpa_supplicant *wpa_s)
+{
+ struct ctrl_iface_global_priv *gpriv;
+ struct ctrl_iface_msg *msg, *prev_msg;
+ unsigned int count = 0;
+
+ if (!global || !global->ctrl_iface)
+ return;
+
+ gpriv = global->ctrl_iface;
+ dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue,
+ struct ctrl_iface_msg, list) {
+ if (msg->wpa_s == wpa_s) {
+ count++;
+ dl_list_del(&msg->list);
+ os_free(msg);
+ }
+ }
+
+ if (count) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL: Dropped %u pending message(s) for interface that is being removed",
+ count);
+ }
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+ struct ctrl_iface_priv *priv)
{
struct wpa_ctrl_dst *dst, *prev;
struct ctrl_iface_msg *msg, *prev_msg;
struct ctrl_iface_global_priv *gpriv;
+ if (!priv) {
+ /* Control interface has not yet been initialized, so there is
+ * nothing to deinitialize here. However, there might be a
+ * pending message for this interface, so get rid of any such
+ * entry before completing interface removal. */
+ wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
+ eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
+ return;
+ }
+
if (priv->sock > -1) {
char *fname;
char *buf, *dir = NULL;
eloop_unregister_read_sock(priv->sock);
if (!dl_list_empty(&priv->ctrl_dst)) {
/*
* Wait before closing the control socket if
* there are any attached monitors in order to allow
* them to receive any pending messages.
*/
wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
"monitors to receive messages");
os_sleep(0, 100000);
}
close(priv->sock);
priv->sock = -1;
fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s);
if (fname) {
unlink(fname);
os_free(fname);
}
if (priv->wpa_s->conf->ctrl_interface == NULL)
goto free_dst;
buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto free_dst;
if (os_strncmp(buf, "DIR=", 4) == 0) {
char *gid_str;
dir = buf + 4;
gid_str = os_strstr(dir, " GROUP=");
if (gid_str)
*gid_str = '\0';
} else
dir = buf;
if (rmdir(dir) < 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",
dir, strerror(errno));
}
}
os_free(buf);
}
free_dst:
dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
list) {
dl_list_del(&dst->list);
os_free(dst);
}
dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
struct ctrl_iface_msg, list) {
dl_list_del(&msg->list);
os_free(msg);
}
gpriv = priv->wpa_s->global->ctrl_iface;
if (gpriv) {
dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue,
struct ctrl_iface_msg, list) {
if (msg->wpa_s == priv->wpa_s) {
dl_list_del(&msg->list);
os_free(msg);
}
}
}
+ wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL);
os_free(priv);
}
/**
* wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors
* @ifname: Interface name for global control socket or %NULL
* @sock: Local socket fd
* @ctrl_dst: List of attached listeners
* @level: Priority level of the message
* @buf: Message data
* @len: Message length
*
* Send a packet to all monitor programs attached to the control interface.
*/
static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
const char *ifname, int sock,
struct dl_list *ctrl_dst,
int level, const char *buf,
size_t len,
struct ctrl_iface_priv *priv,
struct ctrl_iface_global_priv *gp)
{
struct wpa_ctrl_dst *dst, *next;
char levelstr[10];
int idx, res;
struct msghdr msg;
struct iovec io[5];
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;
dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
int _errno;
char txt[200];
if (level < dst->debug_level)
continue;
msg.msg_name = (void *) &dst->addr;
msg.msg_namelen = dst->addrlen;
wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
sockaddr_print(MSG_MSGDUMP,
"CTRL_IFACE monitor sent successfully to",
&dst->addr, dst->addrlen);
dst->errors = 0;
continue;
}
_errno = errno;
os_snprintf(txt, sizeof(txt), "CTRL_IFACE monitor: %d (%s) for",
_errno, strerror(_errno));
sockaddr_print(MSG_DEBUG, txt, &dst->addr, dst->addrlen);
dst->errors++;
if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) {
sockaddr_print(MSG_INFO, "CTRL_IFACE: Detach monitor that cannot receive messages:",
&dst->addr, dst->addrlen);
wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
dst->addrlen);
}
if (_errno == ENOBUFS || _errno == EAGAIN) {
/*
* The socket send buffer could be full. This may happen
* if client programs are not receiving their pending
* messages. Close and reopen the socket as a workaround
* to avoid getting stuck being unable to send any new
* responses.
*/
if (priv)
sock = wpas_ctrl_iface_reinit(wpa_s, priv);
else if (gp)
sock = wpas_ctrl_iface_global_reinit(
wpa_s->global, gp);
else
break;
if (sock < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Failed to reinitialize ctrl_iface socket");
break;
}
}
}
}
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
{
char buf[256];
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
if (priv->sock == -1)
return;
for (;;) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
"attach", priv->wpa_s->ifname);
eloop_wait_for_read_sock(priv->sock);
res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
continue;
}
buf[res] = '\0';
if (os_strcmp(buf, "ATTACH") == 0) {
/* handle ATTACH signal of first monitor interface */
if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
&from, fromlen,
0)) {
if (sendto(priv->sock, "OK\n", 3, 0,
(struct sockaddr *) &from, fromlen) <
0) {
wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
strerror(errno));
}
/* OK to continue */
return;
} else {
if (sendto(priv->sock, "FAIL\n", 5, 0,
(struct sockaddr *) &from, fromlen) <
0) {
wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
strerror(errno));
}
}
} else {
/* return FAIL for all other signals */
if (sendto(priv->sock, "FAIL\n", 5, 0,
(struct sockaddr *) &from, fromlen) < 0) {
wpa_printf(MSG_DEBUG,
"ctrl_iface sendto failed: %s",
strerror(errno));
}
}
}
}
/* Global ctrl_iface */
static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
char *buf;
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply = NULL, *reply_buf = NULL;
size_t reply_len;
buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
if (!buf)
return;
res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
os_free(buf);
return;
}
if ((size_t) res > CTRL_IFACE_MAX_LEN) {
wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
os_free(buf);
return;
}
buf[res] = '\0';
if (os_strcmp(buf, "ATTACH") == 0) {
if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
fromlen, 1))
reply_len = 1;
else
reply_len = 2;
} else if (os_strcmp(buf, "DETACH") == 0) {
if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
fromlen))
reply_len = 1;
else
reply_len = 2;
} else {
reply_buf = wpa_supplicant_global_ctrl_iface_process(
global, buf, &reply_len);
reply = reply_buf;
/*
* There could be some password/key material in the command, so
* clear the buffer explicitly now that it is not needed
* anymore.
*/
os_memset(buf, 0, res);
}
if (!reply && reply_len == 1) {
reply = "FAIL\n";
reply_len = 5;
} else if (!reply && reply_len == 2) {
reply = "OK\n";
reply_len = 3;
}
if (reply) {
wpas_ctrl_sock_debug("global_ctrl_sock-sendto",
sock, reply, reply_len);
if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
strerror(errno));
}
}
os_free(reply_buf);
os_free(buf);
}
static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
struct ctrl_iface_global_priv *priv)
{
struct sockaddr_un addr;
const char *ctrl = global->params.ctrl_interface;
int flags;
wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
#ifdef ANDROID
if (os_strncmp(ctrl, "@android:", 9) == 0) {
priv->sock = android_get_control_socket(ctrl + 9);
if (priv->sock < 0) {
wpa_printf(MSG_ERROR, "Failed to open Android control "
"socket '%s'", ctrl + 9);
goto fail;
}
wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
ctrl + 9);
priv->android_control_socket = 1;
goto havesock;
}
if (os_strncmp(ctrl, "@abstract:", 10) != 0) {
/*
* Backwards compatibility - try to open an Android control
* socket and if that fails, assume this was a UNIX domain
* socket instead.
*/
priv->sock = android_get_control_socket(ctrl);
if (priv->sock >= 0) {
wpa_printf(MSG_DEBUG,
"Using Android control socket '%s'",
ctrl);
priv->android_control_socket = 1;
goto havesock;
}
}
#endif /* ANDROID */
priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (priv->sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
addr.sun_len = sizeof(addr);
#endif /* __FreeBSD__ */
addr.sun_family = AF_UNIX;
if (os_strncmp(ctrl, "@abstract:", 10) == 0) {
addr.sun_path[0] = '\0';
os_strlcpy(addr.sun_path + 1, ctrl + 10,
sizeof(addr.sun_path) - 1);
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) <
0) {
wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: "
"bind(PF_UNIX;%s) failed: %s",
ctrl, strerror(errno));
goto fail;
}
wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'",
ctrl + 10);
goto havesock;
}
os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path));
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s",
ctrl, strerror(errno));
if (connect(priv->sock, (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(ctrl) < 0) {
wpa_printf(MSG_ERROR,
"Could not unlink existing ctrl_iface socket '%s': %s",
ctrl, strerror(errno));
goto fail;
}
if (bind(priv->sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s",
ctrl, strerror(errno));
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
"ctrl_iface socket '%s'",
ctrl);
} 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",
ctrl);
goto fail;
}
}
wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl);
if (global->params.ctrl_interface_group) {
char *gid_str = global->params.ctrl_interface_group;
gid_t gid = 0;
struct group *grp;
char *endp;
grp = getgrnam(gid_str);
if (grp) {
gid = grp->gr_gid;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
" (from group name '%s')",
(int) gid, gid_str);
} else {
/* Group name not found - try to parse this as gid */
gid = strtol(gid_str, &endp, 10);
if (*gid_str == '\0' || *endp != '\0') {
wpa_printf(MSG_ERROR, "CTRL: Invalid group "
"'%s'", gid_str);
goto fail;
}
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
(int) gid);
}
if (lchown(ctrl, -1, gid) < 0) {
wpa_printf(MSG_ERROR,
"lchown[global_ctrl_interface=%s,gid=%d]: %s",
ctrl, (int) gid, strerror(errno));
goto fail;
}
if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR,
"chmod[global_ctrl_interface=%s]: %s",
ctrl, strerror(errno));
goto fail;
}
} else {
if (chmod(ctrl, S_IRWXU) < 0) {
wpa_printf(MSG_DEBUG,
"chmod[global_ctrl_interface=%s](S_IRWXU): %s",
ctrl, strerror(errno));
/* continue anyway since group change was not required
*/
}
}
havesock:
/*
* Make socket non-blocking so that we don't hang forever if
* target dies unexpectedly.
*/
flags = fcntl(priv->sock, F_GETFL);
if (flags >= 0) {
flags |= O_NONBLOCK;
if (fcntl(priv->sock, F_SETFL, flags) < 0) {
wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
strerror(errno));
/* Not fatal, continue on.*/
}
}
eloop_register_read_sock(priv->sock,
wpa_supplicant_global_ctrl_iface_receive,
global, priv);
return 0;
fail:
if (priv->sock >= 0) {
close(priv->sock);
priv->sock = -1;
}
return -1;
}
struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
return NULL;
dl_list_init(&priv->ctrl_dst);
dl_list_init(&priv->msg_queue);
priv->global = global;
priv->sock = -1;
if (global->params.ctrl_interface == NULL)
return priv;
if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) {
os_free(priv);
return NULL;
}
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
}
static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
struct ctrl_iface_global_priv *priv)
{
int res;
if (priv->sock <= 0)
return -1;
/*
* On Android, the control socket being used may be the socket
* that is created when wpa_supplicant is started as a /init.*.rc
* service. Such a socket is maintained as a key-value pair in
* Android's environment. Closing this control socket would leave us
* in a bad state with an invalid socket descriptor.
*/
if (priv->android_control_socket)
return priv->sock;
eloop_unregister_read_sock(priv->sock);
close(priv->sock);
priv->sock = -1;
res = wpas_global_ctrl_iface_open_sock(global, priv);
if (res < 0)
return -1;
return priv->sock;
}
void
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
{
struct wpa_ctrl_dst *dst, *prev;
struct ctrl_iface_msg *msg, *prev_msg;
if (priv->sock >= 0) {
eloop_unregister_read_sock(priv->sock);
close(priv->sock);
}
if (priv->global->params.ctrl_interface)
unlink(priv->global->params.ctrl_interface);
dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
list) {
dl_list_del(&dst->list);
os_free(dst);
}
dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
struct ctrl_iface_msg, list) {
dl_list_del(&msg->list);
os_free(msg);
}
os_free(priv);
}
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index d137ad6ac5ab..e256ac50eec4 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -1,1557 +1,1555 @@
/*
* WPA Supplicant - test code
* 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.
*
* 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 "utils/ext_password.h"
#include "common/version.h"
#include "crypto/tls.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "eap_server/eap_methods.h"
#include "eloop.h"
#include "utils/base64.h"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "common/wpa_ctrl.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
#include "wpas_glue.h"
const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
struct extra_radius_attr {
u8 type;
char syntax;
char *data;
struct extra_radius_attr *next;
};
struct eapol_test_data {
struct wpa_supplicant *wpa_s;
int eapol_test_num_reauths;
int no_mppe_keys;
int num_mppe_ok, num_mppe_mismatch;
int req_eap_key_name;
u8 radius_identifier;
struct radius_msg *last_recv_radius;
struct in_addr own_ip_addr;
struct radius_client_data *radius;
struct hostapd_radius_servers *radius_conf;
/* last received EAP Response from Authentication Server */
struct wpabuf *last_eap_radius;
u8 authenticator_pmk[PMK_LEN];
size_t authenticator_pmk_len;
u8 authenticator_eap_key_name[256];
size_t authenticator_eap_key_name_len;
int radius_access_accept_received;
int radius_access_reject_received;
int auth_timed_out;
u8 *eap_identity;
size_t eap_identity_len;
char *connect_info;
u8 own_addr[ETH_ALEN];
struct extra_radius_attr *extra_attrs;
FILE *server_cert_file;
const char *pcsc_reader;
const char *pcsc_pin;
unsigned int ctrl_iface:1;
unsigned int id_req_sent:1;
};
static struct eapol_test_data eapol_test;
static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
int level, const char *txt, size_t len)
{
if (addr)
wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n",
MAC2STR(addr), txt);
else
wpa_printf(MSG_DEBUG, "%s", txt);
}
static int add_extra_attr(struct radius_msg *msg,
struct extra_radius_attr *attr)
{
size_t len;
char *pos;
u32 val;
char buf[RADIUS_MAX_ATTR_LEN + 1];
switch (attr->syntax) {
case 's':
os_snprintf(buf, sizeof(buf), "%s", attr->data);
len = os_strlen(buf);
break;
case 'n':
buf[0] = '\0';
len = 1;
break;
case 'x':
pos = attr->data;
if (pos[0] == '0' && pos[1] == 'x')
pos += 2;
len = os_strlen(pos);
if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
printf("Invalid extra attribute hexstring\n");
return -1;
}
len /= 2;
if (hexstr2bin(pos, (u8 *) buf, len) < 0) {
printf("Invalid extra attribute hexstring\n");
return -1;
}
break;
case 'd':
val = htonl(atoi(attr->data));
os_memcpy(buf, &val, 4);
len = 4;
break;
default:
printf("Incorrect extra attribute syntax specification\n");
return -1;
}
if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) {
printf("Could not add attribute %d\n", attr->type);
return -1;
}
return 0;
}
static int add_extra_attrs(struct radius_msg *msg,
struct extra_radius_attr *attrs)
{
struct extra_radius_attr *p;
for (p = attrs; p; p = p->next) {
if (add_extra_attr(msg, p) < 0)
return -1;
}
return 0;
}
static struct extra_radius_attr *
find_extra_attr(struct extra_radius_attr *attrs, u8 type)
{
struct extra_radius_attr *p;
for (p = attrs; p; p = p->next) {
if (p->type == type)
return p;
}
return NULL;
}
static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
const u8 *eap, size_t len)
{
struct radius_msg *msg;
char buf[RADIUS_MAX_ATTR_LEN + 1];
const struct eap_hdr *hdr;
const u8 *pos;
wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
"packet");
e->radius_identifier = radius_client_get_id(e->radius);
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
e->radius_identifier);
if (msg == NULL) {
printf("Could not create net RADIUS packet\n");
return;
}
radius_msg_make_authenticator(msg);
hdr = (const struct eap_hdr *) eap;
pos = (const u8 *) (hdr + 1);
if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
pos[0] == EAP_TYPE_IDENTITY) {
pos++;
os_free(e->eap_identity);
e->eap_identity_len = len - sizeof(*hdr) - 1;
e->eap_identity = os_malloc(e->eap_identity_len);
if (e->eap_identity) {
os_memcpy(e->eap_identity, pos, e->eap_identity_len);
wpa_hexdump(MSG_DEBUG, "Learned identity from "
"EAP-Response-Identity",
e->eap_identity, e->eap_identity_len);
}
}
if (e->eap_identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
e->eap_identity, e->eap_identity_len)) {
printf("Could not add User-Name\n");
goto fail;
}
if (e->req_eap_key_name &&
!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0",
1)) {
printf("Could not add EAP-Key-Name\n");
goto fail;
}
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
(u8 *) &e->own_ip_addr, 4)) {
printf("Could not add NAS-IP-Address\n");
goto fail;
}
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
MAC2STR(e->wpa_s->own_addr));
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
&&
!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
(u8 *) buf, os_strlen(buf))) {
printf("Could not add Calling-Station-Id\n");
goto fail;
}
/* TODO: should probably check MTU from driver config; 2304 is max for
* IEEE 802.11, but use 1400 to avoid problems with too large packets
*/
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
printf("Could not add Framed-MTU\n");
goto fail;
}
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
printf("Could not add NAS-Port-Type\n");
goto fail;
}
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_SERVICE_TYPE) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
RADIUS_SERVICE_TYPE_FRAMED)) {
printf("Could not add Service-Type\n");
goto fail;
}
os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
(u8 *) buf, os_strlen(buf))) {
printf("Could not add Connect-Info\n");
goto fail;
}
if (add_extra_attrs(msg, e->extra_attrs) < 0)
goto fail;
if (eap && !radius_msg_add_eap(msg, eap, len)) {
printf("Could not add EAP-Message\n");
goto fail;
}
/* State attribute must be copied if and only if this packet is
* Access-Request reply to the previous Access-Challenge */
if (e->last_recv_radius &&
radius_msg_get_hdr(e->last_recv_radius)->code ==
RADIUS_CODE_ACCESS_CHALLENGE) {
int res = radius_msg_copy_attr(msg, e->last_recv_radius,
RADIUS_ATTR_STATE);
if (res < 0) {
printf("Could not copy State attribute from previous "
"Access-Challenge\n");
goto fail;
}
if (res > 0) {
wpa_printf(MSG_DEBUG, " Copied RADIUS State "
"Attribute");
}
}
if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
< 0)
goto fail;
return;
fail:
radius_msg_free(msg);
}
static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
size_t len)
{
printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
type, (unsigned long) len);
if (type == IEEE802_1X_TYPE_EAP_PACKET) {
wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
ieee802_1x_encapsulate_radius(&eapol_test, buf, len);
}
return 0;
}
static void eapol_test_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
struct eapol_test_data *e = ctx;
wpa_config_set_blob(e->wpa_s->conf, blob);
}
static const struct wpa_config_blob *
eapol_test_get_config_blob(void *ctx, const char *name)
{
struct eapol_test_data *e = ctx;
return wpa_config_get_blob(e->wpa_s->conf, name);
}
static void eapol_test_eapol_done_cb(void *ctx)
{
struct eapol_test_data *e = ctx;
printf("WPA: EAPOL processing complete\n");
wpa_supplicant_cancel_auth_timeout(e->wpa_s);
wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED);
}
static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
{
struct eapol_test_data *e = eloop_ctx;
printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
e->radius_access_accept_received = 0;
send_eap_request_identity(e->wpa_s, NULL);
}
static int eapol_test_compare_pmk(struct eapol_test_data *e)
{
u8 pmk[PMK_LEN];
int ret = 1;
const u8 *sess_id;
size_t sess_id_len;
if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) {
printf("WARNING: PMK mismatch\n");
wpa_hexdump(MSG_DEBUG, "PMK from AS",
e->authenticator_pmk, PMK_LEN);
} else if (e->radius_access_accept_received)
ret = 0;
} else if (e->authenticator_pmk_len == 16 &&
eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) {
wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) {
printf("WARNING: PMK mismatch\n");
wpa_hexdump(MSG_DEBUG, "PMK from AS",
e->authenticator_pmk, 16);
} else if (e->radius_access_accept_received)
ret = 0;
} else if (e->radius_access_accept_received && e->no_mppe_keys) {
/* No keying material expected */
ret = 0;
}
if (ret && !e->no_mppe_keys)
e->num_mppe_mismatch++;
else if (!e->no_mppe_keys)
e->num_mppe_ok++;
sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len);
if (!sess_id)
return ret;
if (e->authenticator_eap_key_name_len == 0) {
wpa_printf(MSG_INFO, "No EAP-Key-Name received from server");
return ret;
}
if (e->authenticator_eap_key_name_len != sess_id_len ||
os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0)
{
wpa_printf(MSG_INFO,
"Locally derived EAP Session-Id does not match EAP-Key-Name from server");
wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len);
wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server",
e->authenticator_eap_key_name,
e->authenticator_eap_key_name_len);
} else {
wpa_printf(MSG_INFO,
"Locally derived EAP Session-Id matches EAP-Key-Name from server");
}
return ret;
}
static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result,
void *ctx)
{
struct eapol_test_data *e = ctx;
printf("eapol_sm_cb: result=%d\n", result);
e->id_req_sent = 0;
if (e->ctrl_iface)
return;
e->eapol_test_num_reauths--;
if (e->eapol_test_num_reauths < 0)
eloop_terminate();
else {
eapol_test_compare_pmk(e);
eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL);
}
}
static void eapol_test_write_cert(FILE *f, const char *subject,
const struct wpabuf *cert)
{
char *encoded;
encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
if (encoded == NULL)
return;
fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
"-----END CERTIFICATE-----\n\n", subject, encoded);
os_free(encoded);
}
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
const char *default_txt)
{
struct eapol_test_data *e = ctx;
struct wpa_supplicant *wpa_s = e->wpa_s;
struct wpa_ssid *ssid = wpa_s->current_ssid;
const char *field_name, *txt = NULL;
char *buf;
size_t buflen;
int len;
if (ssid == NULL)
return;
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;
}
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen,
WPA_CTRL_REQ "%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, "%s", buf);
os_free(buf);
}
#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
#define eapol_test_eap_param_needed NULL
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
static void eapol_test_cert_cb(void *ctx, struct tls_cert_data *cert,
const char *cert_hash)
{
struct eapol_test_data *e = ctx;
int i;
wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
"depth=%d subject='%s'%s%s",
cert->depth, cert->subject,
cert_hash ? " hash=" : "",
cert_hash ? cert_hash : "");
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(e->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);
}
if (e->server_cert_file)
eapol_test_write_cert(e->server_cert_file,
cert->subject, cert->cert);
}
for (i = 0; i < cert->num_altsubject; i++)
wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
"depth=%d %s", cert->depth, cert->altsubject[i]);
}
static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
{
struct eapol_test_data *e = ctx;
struct wpa_supplicant *wpa_s = e->wpa_s;
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;
}
}
static enum wpa_states eapol_test_get_state(void *ctx)
{
struct eapol_test_data *e = ctx;
struct wpa_supplicant *wpa_s = e->wpa_s;
return wpa_s->wpa_state;
}
static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct eapol_config eapol_conf;
struct eapol_ctx *ctx;
struct wpa_sm_ctx *wctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
printf("Failed to allocate EAPOL context.\n");
return -1;
}
ctx->ctx = e;
ctx->msg_ctx = wpa_s;
ctx->scard_ctx = wpa_s->scard;
ctx->cb = eapol_sm_cb;
ctx->cb_ctx = e;
ctx->eapol_send_ctx = wpa_s;
ctx->preauth = 0;
ctx->eapol_done_cb = eapol_test_eapol_done_cb;
ctx->eapol_send = eapol_test_eapol_send;
ctx->set_config_blob = eapol_test_set_config_blob;
ctx->get_config_blob = eapol_test_get_config_blob;
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->eap_param_needed = eapol_test_eap_param_needed;
ctx->cert_cb = eapol_test_cert_cb;
ctx->cert_in_cb = 1;
ctx->set_anon_id = eapol_test_set_anon_id;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
os_free(ctx);
printf("Failed to initialize EAPOL state machines.\n");
return -1;
}
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
wctx = os_zalloc(sizeof(*wctx));
if (wctx == NULL) {
os_free(ctx);
return -1;
}
wctx->ctx = e;
wctx->msg_ctx = wpa_s;
wctx->get_state = eapol_test_get_state;
wpa_s->wpa = wpa_sm_init(wctx);
if (!wpa_s->wpa) {
os_free(ctx);
os_free(wctx);
return -1;
}
if (!ssid)
return 0;
wpa_s->current_ssid = ssid;
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
eapol_conf.accept_802_1x_keys = 1;
eapol_conf.required_keys = 0;
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.external_sim = wpa_s->conf->external_sim;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
eapol_sm_notify_portValid(wpa_s->eapol, false);
/* 802.1X::portControl = Auto */
eapol_sm_notify_portEnabled(wpa_s->eapol, true);
return 0;
}
static void test_eapol_clean(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s)
{
struct extra_radius_attr *p, *prev;
wpa_sm_deinit(wpa_s->wpa);
wpa_s->wpa = NULL;
radius_client_deinit(e->radius);
wpabuf_free(e->last_eap_radius);
radius_msg_free(e->last_recv_radius);
e->last_recv_radius = NULL;
os_free(e->eap_identity);
e->eap_identity = NULL;
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
if (e->radius_conf && e->radius_conf->auth_server) {
os_free(e->radius_conf->auth_server->shared_secret);
os_free(e->radius_conf->auth_server);
}
os_free(e->radius_conf);
e->radius_conf = NULL;
scard_deinit(wpa_s->scard);
- if (wpa_s->ctrl_iface) {
- wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
- wpa_s->ctrl_iface = NULL;
- }
+ wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+ wpa_s->ctrl_iface = NULL;
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
wpa_config_free(wpa_s->conf);
p = e->extra_attrs;
while (p) {
prev = p;
p = p->next;
os_free(prev);
}
}
static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
u8 buf[100], *pos;
struct ieee802_1x_hdr *hdr;
struct eap_hdr *eap;
hdr = (struct ieee802_1x_hdr *) buf;
hdr->version = EAPOL_VERSION;
hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
hdr->length = htons(5);
eap = (struct eap_hdr *) (hdr + 1);
eap->code = EAP_CODE_REQUEST;
if (os_get_random((u8 *) &eap->identifier, sizeof(eap->identifier)) < 0)
eap->identifier = os_random() & 0xff;
eap->length = htons(5);
pos = (u8 *) (eap + 1);
*pos = EAP_TYPE_IDENTITY;
printf("Sending fake EAP-Request-Identity\n");
eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
sizeof(*hdr) + 5);
}
static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct eapol_test_data *e = eloop_ctx;
printf("EAPOL test timed out\n");
e->auth_timed_out = 1;
eloop_terminate();
}
static char *eap_type_text(u8 type)
{
switch (type) {
case EAP_TYPE_IDENTITY: return "Identity";
case EAP_TYPE_NOTIFICATION: return "Notification";
case EAP_TYPE_NAK: return "Nak";
case EAP_TYPE_TLS: return "TLS";
case EAP_TYPE_TTLS: return "TTLS";
case EAP_TYPE_PEAP: return "PEAP";
case EAP_TYPE_SIM: return "SIM";
case EAP_TYPE_GTC: return "GTC";
case EAP_TYPE_MD5: return "MD5";
case EAP_TYPE_OTP: return "OTP";
case EAP_TYPE_FAST: return "FAST";
case EAP_TYPE_SAKE: return "SAKE";
case EAP_TYPE_PSK: return "PSK";
default: return "Unknown";
}
}
static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
{
struct wpabuf *eap;
const struct eap_hdr *hdr;
int eap_type = -1;
char buf[64];
struct radius_msg *msg;
if (e->last_recv_radius == NULL)
return;
msg = e->last_recv_radius;
eap = radius_msg_get_eap(msg);
if (eap == NULL) {
/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
* RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
* attribute */
wpa_printf(MSG_DEBUG, "could not extract "
"EAP-Message from RADIUS message");
wpabuf_free(e->last_eap_radius);
e->last_eap_radius = NULL;
return;
}
if (wpabuf_len(eap) < sizeof(*hdr)) {
wpa_printf(MSG_DEBUG, "too short EAP packet "
"received from authentication server");
wpabuf_free(eap);
return;
}
if (wpabuf_len(eap) > sizeof(*hdr))
eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
hdr = wpabuf_head(eap);
switch (hdr->code) {
case EAP_CODE_REQUEST:
os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
eap_type >= 0 ? eap_type_text(eap_type) : "??",
eap_type);
break;
case EAP_CODE_RESPONSE:
os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
eap_type >= 0 ? eap_type_text(eap_type) : "??",
eap_type);
break;
case EAP_CODE_SUCCESS:
os_strlcpy(buf, "EAP Success", sizeof(buf));
/* LEAP uses EAP Success within an authentication, so must not
* stop here with eloop_terminate(); */
break;
case EAP_CODE_FAILURE:
os_strlcpy(buf, "EAP Failure", sizeof(buf));
if (e->ctrl_iface)
break;
eloop_terminate();
break;
default:
os_strlcpy(buf, "unknown EAP code", sizeof(buf));
wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
break;
}
wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
"id=%d len=%d) from RADIUS server: %s",
hdr->code, hdr->identifier, ntohs(hdr->length), buf);
/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
wpabuf_free(e->last_eap_radius);
e->last_eap_radius = eap;
{
struct ieee802_1x_hdr *dot1x;
dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
assert(dot1x != NULL);
dot1x->version = EAPOL_VERSION;
dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
dot1x->length = htons(wpabuf_len(eap));
os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
wpabuf_len(eap));
eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
(u8 *) dot1x,
sizeof(*dot1x) + wpabuf_len(eap));
os_free(dot1x);
}
}
static void ieee802_1x_get_keys(struct eapol_test_data *e,
struct radius_msg *msg, struct radius_msg *req,
const u8 *shared_secret,
size_t shared_secret_len)
{
struct radius_ms_mppe_keys *keys;
u8 *buf;
size_t len;
keys = radius_msg_get_ms_keys(msg, req, shared_secret,
shared_secret_len);
if (keys && keys->send == NULL && keys->recv == NULL) {
os_free(keys);
keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
shared_secret_len);
}
if (keys) {
if (keys->send) {
wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
keys->send, keys->send_len);
}
if (keys->recv) {
wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
keys->recv, keys->recv_len);
e->authenticator_pmk_len =
keys->recv_len > PMK_LEN ? PMK_LEN :
keys->recv_len;
os_memcpy(e->authenticator_pmk, keys->recv,
e->authenticator_pmk_len);
if (e->authenticator_pmk_len == 16 && keys->send &&
keys->send_len == 16) {
/* MS-CHAP-v2 derives 16 octet keys */
wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key "
"to extend PMK to 32 octets");
os_memcpy(e->authenticator_pmk +
e->authenticator_pmk_len,
keys->send, keys->send_len);
e->authenticator_pmk_len += keys->send_len;
}
}
os_free(keys->send);
os_free(keys->recv);
os_free(keys);
}
if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
NULL) == 0) {
os_memcpy(e->authenticator_eap_key_name, buf, len);
e->authenticator_eap_key_name_len = len;
} else {
e->authenticator_eap_key_name_len = 0;
}
}
/* Process the RADIUS frames from Authentication Server */
static RadiusRxResult
ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
const u8 *shared_secret, size_t shared_secret_len,
void *data)
{
struct eapol_test_data *e = data;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
* present when packet contains an EAP-Message attribute */
if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
0) < 0 &&
radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
wpa_printf(MSG_DEBUG, "Allowing RADIUS "
"Access-Reject without Message-Authenticator "
"since it does not include EAP-Message\n");
} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
req, 1)) {
printf("Incoming RADIUS packet did not have correct "
"Message-Authenticator - dropped\n");
return RADIUS_RX_UNKNOWN;
}
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
hdr->code != RADIUS_CODE_ACCESS_REJECT &&
hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
printf("Unknown RADIUS message code\n");
return RADIUS_RX_UNKNOWN;
}
e->radius_identifier = -1;
wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
radius_msg_free(e->last_recv_radius);
e->last_recv_radius = msg;
switch (hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
e->radius_access_accept_received = 1;
ieee802_1x_get_keys(e, msg, req, shared_secret,
shared_secret_len);
break;
case RADIUS_CODE_ACCESS_REJECT:
e->radius_access_reject_received = 1;
break;
}
ieee802_1x_decapsulate_radius(e);
if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
e->eapol_test_num_reauths < 0) ||
hdr->code == RADIUS_CODE_ACCESS_REJECT) {
if (!e->ctrl_iface)
eloop_terminate();
}
return RADIUS_RX_QUEUED;
}
static int driver_get_ssid(void *priv, u8 *ssid)
{
ssid[0] = 0;
return 0;
}
static int driver_get_bssid(void *priv, u8 *bssid)
{
struct eapol_test_data *e = priv;
if (e->ctrl_iface && !e->id_req_sent) {
eloop_register_timeout(0, 0, send_eap_request_identity,
e->wpa_s, NULL);
e->id_req_sent = 1;
}
os_memset(bssid, 0, ETH_ALEN);
bssid[5] = 1;
return 0;
}
static int driver_get_capa(void *priv, struct wpa_driver_capa *capa)
{
os_memset(capa, 0, sizeof(*capa));
capa->flags = WPA_DRIVER_FLAGS_WIRED;
return 0;
}
struct wpa_driver_ops eapol_test_drv_ops = {
.name = "test",
.get_ssid = driver_get_ssid,
.get_bssid = driver_get_bssid,
.get_capa = driver_get_capa,
};
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
int port, const char *secret,
const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
int res;
wpa_s->driver = &eapol_test_drv_ops;
wpa_s->drv_priv = e;
wpa_s->bssid[5] = 1;
os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
assert(e->radius_conf != NULL);
e->radius_conf->num_auth_servers = 1;
as = os_zalloc(sizeof(struct hostapd_radius_server));
assert(as != NULL);
#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
{
int a[4];
u8 *pos;
sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
pos = (u8 *) &as->addr.u.v4;
*pos++ = a[0];
*pos++ = a[1];
*pos++ = a[2];
*pos++ = a[3];
as->addr.af = AF_INET;
}
#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) {
wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
authsrv);
assert(0);
}
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
as->port = port;
as->shared_secret = (u8 *) os_strdup(secret);
as->shared_secret_len = os_strlen(secret);
e->radius_conf->auth_server = as;
e->radius_conf->auth_servers = as;
e->radius_conf->msg_dumps = 1;
if (cli_addr) {
if (hostapd_parse_ip_addr(cli_addr,
&e->radius_conf->client_addr) == 0)
e->radius_conf->force_client_addr = 1;
else {
wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
cli_addr);
assert(0);
}
}
e->radius = radius_client_init(wpa_s, e->radius_conf);
assert(e->radius != NULL);
res = radius_client_register(e->radius, RADIUS_AUTH,
ieee802_1x_receive_auth, e);
assert(res == 0);
}
static int scard_test(struct eapol_test_data *e)
{
struct scard_data *scard;
size_t len;
char imsi[20];
unsigned char _rand[16];
#ifdef PCSC_FUNCS
unsigned char sres[4];
unsigned char kc[8];
#endif /* PCSC_FUNCS */
#define num_triplets 5
unsigned char rand_[num_triplets][16];
unsigned char sres_[num_triplets][4];
unsigned char kc_[num_triplets][8];
int i, res;
size_t j;
#define AKA_RAND_LEN 16
#define AKA_AUTN_LEN 16
#define AKA_AUTS_LEN 14
#define RES_MAX_LEN 16
#define IK_LEN 16
#define CK_LEN 16
unsigned char aka_rand[AKA_RAND_LEN];
unsigned char aka_autn[AKA_AUTN_LEN];
unsigned char aka_auts[AKA_AUTS_LEN];
unsigned char aka_res[RES_MAX_LEN];
size_t aka_res_len;
unsigned char aka_ik[IK_LEN];
unsigned char aka_ck[CK_LEN];
scard = scard_init(e->pcsc_reader);
if (scard == NULL)
return -1;
if (scard_set_pin(scard, e->pcsc_pin)) {
wpa_printf(MSG_WARNING, "PIN validation failed");
scard_deinit(scard);
return -1;
}
len = sizeof(imsi);
if (scard_get_imsi(scard, imsi, &len))
goto failed;
wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
/* NOTE: Permanent Username: 1 | IMSI */
wpa_printf(MSG_DEBUG, "SCARD: MNC length %d",
scard_get_mnc_len(scard));
os_memset(_rand, 0, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
goto failed;
os_memset(_rand, 0xff, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
goto failed;
for (i = 0; i < num_triplets; i++) {
os_memset(rand_[i], i, sizeof(rand_[i]));
if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
goto failed;
}
for (i = 0; i < num_triplets; i++) {
printf("1");
for (j = 0; j < len; j++)
printf("%c", imsi[j]);
printf(",");
for (j = 0; j < 16; j++)
printf("%02X", rand_[i][j]);
printf(",");
for (j = 0; j < 4; j++)
printf("%02X", sres_[i][j]);
printf(",");
for (j = 0; j < 8; j++)
printf("%02X", kc_[i][j]);
printf("\n");
}
wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
/* seq 39 (0x28) */
os_memset(aka_rand, 0xaa, 16);
os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
"\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
aka_ik, aka_ck, aka_auts);
if (res == 0) {
wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
} else if (res == -2) {
wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
"failure");
wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
} else {
wpa_printf(MSG_DEBUG, "UMTS auth failed");
}
failed:
scard_deinit(scard);
return 0;
#undef num_triplets
}
static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
{
struct scard_data *scard;
size_t len;
char imsi[20];
unsigned char _rand[16];
unsigned char sres[4];
unsigned char kc[8];
int num_triplets;
int i;
size_t j;
if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
printf("invalid parameters for sim command\n");
return -1;
}
if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) {
/* disable debug output */
wpa_debug_level = 99;
}
scard = scard_init(e->pcsc_reader);
if (scard == NULL) {
printf("Failed to open smartcard connection\n");
return -1;
}
if (scard_set_pin(scard, argv[0])) {
wpa_printf(MSG_WARNING, "PIN validation failed");
scard_deinit(scard);
return -1;
}
len = sizeof(imsi);
if (scard_get_imsi(scard, imsi, &len)) {
scard_deinit(scard);
return -1;
}
for (i = 0; i < num_triplets; i++) {
os_memset(_rand, i, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
break;
/* IMSI:Kc:SRES:RAND */
for (j = 0; j < len; j++)
printf("%c", imsi[j]);
printf(":");
for (j = 0; j < 8; j++)
printf("%02X", kc[j]);
printf(":");
for (j = 0; j < 4; j++)
printf("%02X", sres[j]);
printf(":");
for (j = 0; j < 16; j++)
printf("%02X", _rand[j]);
printf("\n");
}
scard_deinit(scard);
return 0;
}
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();
}
static void usage(void)
{
printf("usage:\n"
"eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
" [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
"\n");
printf("options:\n"
" -c<conf> = configuration file\n"
" -a<AS IP> = IP address of the authentication server, "
"default 127.0.0.1\n"
" -p<AS port> = UDP port of the authentication server, "
"default 1812\n"
" -s<AS secret> = shared secret with the authentication "
"server, default 'radius'\n"
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
" -e = Request EAP-Key-Name\n"
" -W = wait for a control interface monitor before starting\n"
" -S = save configuration after authentication\n"
" -n = no MPPE keys expected\n"
" -v = show version\n"
" -t<timeout> = sets timeout in seconds (default: 30 s)\n"
" -C<Connect-Info> = RADIUS Connect-Info (default: "
"CONNECT 11Mbps 802.11b)\n"
" -M<client MAC address> = Set own MAC address "
"(Calling-Station-Id,\n"
" default: 02:00:00:00:00:01)\n"
" -o<server cert file> = Write received server certificate\n"
" chain to the specified file\n"
" -N<attr spec> = send arbitrary attribute specified by:\n"
" attr_id:syntax:value or attr_id\n"
" attr_id - number id of the attribute\n"
" syntax - one of: s, d, x\n"
" s = string\n"
" d = integer\n"
" x = octet string\n"
" value - attribute value.\n"
" When only attr_id is specified, NULL will be used as "
"value.\n"
" Multiple attributes can be specified by using the "
"option several times.\n");
}
int main(int argc, char *argv[])
{
struct wpa_global global;
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
int as_port = 1812;
char *as_secret = "radius";
char *cli_addr = NULL;
char *conf = NULL;
int timeout = 30;
char *pos;
struct extra_radius_attr *p = NULL, *p1;
const char *ifname = "test";
const char *ctrl_iface = NULL;
if (os_program_init())
return -1;
hostapd_logger_register_cb(hostapd_logger_cb);
os_memset(&eapol_test, 0, sizeof(eapol_test));
eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
eapol_test.pcsc_pin = "1234";
wpa_debug_level = 0;
wpa_debug_show_keys = 1;
for (;;) {
c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
if (c < 0)
break;
switch (c) {
case 'a':
as_addr = optarg;
break;
case 'A':
cli_addr = optarg;
break;
case 'c':
conf = optarg;
break;
case 'C':
eapol_test.connect_info = optarg;
break;
case 'e':
eapol_test.req_eap_key_name = 1;
break;
case 'i':
ifname = optarg;
break;
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
return -1;
}
break;
case 'n':
eapol_test.no_mppe_keys++;
break;
case 'o':
if (eapol_test.server_cert_file)
fclose(eapol_test.server_cert_file);
eapol_test.server_cert_file = fopen(optarg, "w");
if (eapol_test.server_cert_file == NULL) {
printf("Could not open '%s' for writing\n",
optarg);
return -1;
}
break;
case 'p':
as_port = atoi(optarg);
break;
case 'P':
eapol_test.pcsc_pin = optarg;
break;
case 'r':
eapol_test.eapol_test_num_reauths = atoi(optarg);
break;
case 'R':
eapol_test.pcsc_reader = optarg;
break;
case 's':
as_secret = optarg;
break;
case 'S':
save_config++;
break;
case 't':
timeout = atoi(optarg);
break;
case 'T':
ctrl_iface = optarg;
eapol_test.ctrl_iface = 1;
break;
case 'v':
printf("eapol_test v%s\n", VERSION_STR);
return 0;
case 'W':
wait_for_monitor++;
break;
case 'N':
p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
break;
if (!p)
eapol_test.extra_attrs = p1;
else
p->next = p1;
p = p1;
p->type = atoi(optarg);
pos = os_strchr(optarg, ':');
if (pos == NULL) {
p->syntax = 'n';
p->data = NULL;
break;
}
pos++;
if (pos[0] == '\0' || pos[1] != ':') {
printf("Incorrect format of attribute "
"specification\n");
break;
}
p->syntax = pos[0];
p->data = pos + 2;
break;
default:
usage();
return -1;
}
}
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
return scard_test(&eapol_test);
}
if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
return scard_get_triplets(&eapol_test, argc - optind - 1,
&argv[optind + 1]);
}
if (conf == NULL && !ctrl_iface) {
usage();
printf("Configuration file is required.\n");
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(&global, 0, sizeof(global));
os_memset(&wpa_s, 0, sizeof(wpa_s));
wpa_s.global = &global;
eapol_test.wpa_s = &wpa_s;
dl_list_init(&wpa_s.bss);
dl_list_init(&wpa_s.bss_id);
if (conf)
wpa_s.conf = wpa_config_read(conf, NULL);
else
wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", conf);
return -1;
}
if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
printf("No networks defined.\n");
return -1;
}
if (eapol_test.pcsc_reader) {
os_free(wpa_s.conf->pcsc_reader);
wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
}
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
cli_addr, ifname);
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 eapol_test process already "
"running or the file was\n"
"left by an unclean termination of eapol_test in "
"which case you will need\n"
"to manually remove this file before starting "
"eapol_test again.\n",
wpa_s.conf->ctrl_interface);
return -1;
}
if (wpa_s.conf->ssid &&
wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
return -1;
if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
return -1;
if (wpas_init_ext_pw(&wpa_s) < 0)
return -1;
if (wait_for_monitor)
wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
if (!ctrl_iface) {
eloop_register_timeout(timeout, 0, eapol_test_timeout,
&eapol_test, NULL);
eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s,
NULL);
}
eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
eloop_run();
eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL);
eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL);
if (eapol_test_compare_pmk(&eapol_test) == 0 ||
eapol_test.no_mppe_keys)
ret = 0;
if (eapol_test.auth_timed_out)
ret = -2;
if (eapol_test.radius_access_reject_received)
ret = -3;
if (save_config)
wpa_config_write(conf, wpa_s.conf);
test_eapol_clean(&eapol_test, &wpa_s);
eap_peer_unregister_methods();
#ifdef CONFIG_AP
eap_server_unregister_methods();
#endif /* CONFIG_AP */
eloop_destroy();
if (eapol_test.server_cert_file)
fclose(eapol_test.server_cert_file);
printf("MPPE keys OK: %d mismatch: %d\n",
eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
if (eapol_test.num_mppe_mismatch)
ret = -4;
if (ret)
printf("FAILURE\n");
else
printf("SUCCESS\n");
os_program_deinit();
return ret;
}
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 5e73ab406d02..b3c07f926b69 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,5550 +1,5549 @@
/*
* 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;
/* Start with assumption of no PMKSA cache entry match */
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;
- u8 rsnxe_capa = 0;
if (bss == orig_bss)
continue;
ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
- if (ie && ie[1] >= 1)
- rsnxe_capa = ie[2];
- if (!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)))
+ 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);
}
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_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;
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;
}
}
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_MSCS_RESP) {
wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa,
payload + 1, plen - 1);
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) {
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);
break;
case EVENT_INTERFACE_ENABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
eloop_cancel_timeout(wpas_clear_disabled_interface,
wpa_s, NULL);
wpa_supplicant_update_mac_addr(wpa_s);
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/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 834c7a1ccc88..65daa77c2c98 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -1,796 +1,795 @@
/*
* WPA Supplicant - Mesh RSN 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 "crypto/sha256.h"
#include "crypto/random.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "rsn_supp/wpa.h"
#include "ap/hostapd.h"
#include "ap/wpa_auth.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "wpas_glue.h"
#include "mesh_mpm.h"
#include "mesh_rsn.h"
#define MESH_AUTH_TIMEOUT 10
#define MESH_AUTH_RETRY 3
void mesh_auth_timer(void *eloop_ctx, void *user_data)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct sta_info *sta = user_data;
struct hostapd_data *hapd;
if (sta->sae->state != SAE_ACCEPTED) {
wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
" (attempt %d) ",
MAC2STR(sta->addr), sta->sae_auth_retry);
wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR,
MAC2STR(sta->addr));
if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
mesh_rsn_auth_sae_sta(wpa_s, sta);
} else {
hapd = wpa_s->ifmsh->bss[0];
if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
ap_free_sta(hapd, sta);
return;
}
/* block the STA if exceeded the number of attempts */
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
sta->sae->state = SAE_NOTHING;
wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr="
MACSTR " duration=%d",
MAC2STR(sta->addr),
hapd->conf->ap_max_inactivity);
}
sta->sae_auth_retry++;
}
}
static void auth_logger(void *ctx, const u8 *addr, logger_level level,
const char *txt)
{
if (addr)
wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
MAC2STR(addr), txt);
else
wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
}
static const u8 *auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr, const u8 *prev_psk,
size_t *psk_len, int *vlan_id)
{
struct mesh_rsn *mesh_rsn = ctx;
struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
struct sta_info *sta = ap_get_sta(hapd, addr);
if (psk_len)
*psk_len = PMK_LEN;
if (vlan_id)
*vlan_id = 0;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
__func__, MAC2STR(addr), prev_psk);
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
return sta->sae->pmk;
}
return NULL;
}
static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len,
enum key_flag key_flag)
{
struct mesh_rsn *mesh_rsn = ctx;
u8 seq[6];
os_memset(seq, 0, sizeof(seq));
if (addr) {
wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
" key_idx=%d)",
__func__, alg, MAC2STR(addr), idx);
} else {
wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
__func__, alg, idx);
}
wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
1, seq, 6, key, key_len, key_flag);
}
static int auth_start_ampe(void *ctx, const u8 *addr)
{
struct mesh_rsn *mesh_rsn = ctx;
struct hostapd_data *hapd;
struct sta_info *sta;
if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH)
return -1;
hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
sta = ap_get_sta(hapd, addr);
if (sta)
eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta);
mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr);
return 0;
}
static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
enum mfp_options ieee80211w, int ocv)
{
struct wpa_auth_config conf;
static const struct wpa_auth_callbacks cb = {
.logger = auth_logger,
.get_psk = auth_get_psk,
.set_key = auth_set_key,
.start_ampe = auth_start_ampe,
};
u8 seq[6] = {};
wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
os_memset(&conf, 0, sizeof(conf));
conf.wpa = WPA_PROTO_RSN;
conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
conf.wpa_pairwise = rsn->pairwise_cipher;
conf.rsn_pairwise = rsn->pairwise_cipher;
conf.wpa_group = rsn->group_cipher;
conf.eapol_version = 0;
conf.wpa_group_rekey = -1;
conf.wpa_group_update_count = 4;
conf.wpa_pairwise_update_count = 4;
conf.ieee80211w = ieee80211w;
if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
#ifdef CONFIG_OCV
conf.ocv = ocv;
#endif /* CONFIG_OCV */
rsn->auth = wpa_init(addr, &conf, &cb, rsn);
if (rsn->auth == NULL) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
return -1;
}
/* TODO: support rekeying */
rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group);
if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
return -1;
rsn->mgtk_key_id = 1;
if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher);
if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
return -1;
rsn->igtk_key_id = 4;
/* group mgmt */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
rsn->igtk, rsn->igtk_len);
wpa_drv_set_key(rsn->wpa_s,
wpa_cipher_to_alg(rsn->mgmt_group_cipher),
broadcast_ether_addr,
rsn->igtk_key_id, 1,
seq, sizeof(seq), rsn->igtk, rsn->igtk_len,
KEY_FLAG_GROUP_TX_DEFAULT);
}
/* group privacy / data frames */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
rsn->mgtk, rsn->mgtk_len);
wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
broadcast_ether_addr,
rsn->mgtk_key_id, 1, seq, sizeof(seq),
rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
return 0;
}
static void mesh_rsn_deinit(struct mesh_rsn *rsn)
{
os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
rsn->mgtk_len = 0;
os_memset(rsn->igtk, 0, sizeof(rsn->igtk));
rsn->igtk_len = 0;
if (rsn->auth)
wpa_deinit(rsn->auth);
}
struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
struct mesh_conf *conf)
{
struct mesh_rsn *mesh_rsn;
struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
const u8 *ie;
size_t ie_len;
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
struct external_pmksa_cache *entry;
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
if (mesh_rsn == NULL)
return NULL;
mesh_rsn->wpa_s = wpa_s;
mesh_rsn->pairwise_cipher = conf->pairwise_cipher;
mesh_rsn->group_cipher = conf->group_cipher;
mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
conf->ieee80211w, conf->ocv) < 0) {
mesh_rsn_deinit(mesh_rsn);
os_free(mesh_rsn);
return NULL;
}
bss->wpa_auth = mesh_rsn->auth;
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
struct external_pmksa_cache,
list)) != NULL) {
int ret;
ret = wpa_auth_pmksa_add_entry(bss->wpa_auth,
entry->pmksa_cache);
dl_list_del(&entry->list);
os_free(entry);
if (ret < 0)
return NULL;
}
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
conf->rsn_ie = (u8 *) ie;
conf->rsn_ie_len = ie_len;
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
return mesh_rsn;
}
static int index_within_array(const int *array, int idx)
{
int i;
for (i = 0; i < idx; i++) {
if (array[i] == -1)
return 0;
}
return 1;
}
static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s,
struct sae_data *sae)
{
int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups;
/* Configuration may have changed, so validate current index */
if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index))
return -1;
for (;;) {
int group = groups[wpa_s->mesh_rsn->sae_group_index];
if (group <= 0)
break;
if (sae_set_group(sae, group) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
sae->group);
return 0;
}
wpa_s->mesh_rsn->sae_group_index++;
}
return -1;
}
static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct sta_info *sta)
{
const char *password;
password = ssid->sae_password;
if (!password)
password = ssid->passphrase;
if (!password) {
wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
return -1;
}
if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) {
wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group");
return -1;
}
if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) {
sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id);
if (!sta->sae->tmp->pw_id)
return -1;
}
return sae_prepare_commit(wpa_s->own_addr, sta->addr,
(u8 *) password, os_strlen(password),
- ssid->sae_password_id,
sta->sae);
}
/* initiate new SAE authentication with sta */
int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
struct sta_info *sta)
{
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct rsn_pmksa_cache_entry *pmksa;
unsigned int rnd;
int ret;
if (!ssid) {
wpa_msg(wpa_s, MSG_DEBUG,
"AUTH: No current_ssid known to initiate new SAE");
return -1;
}
if (!sta->sae) {
sta->sae = os_zalloc(sizeof(*sta->sae));
if (sta->sae == NULL)
return -1;
}
pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL);
if (pmksa) {
if (!sta->wpa_sm)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (!sta->wpa_sm) {
wpa_printf(MSG_ERROR,
"mesh: Failed to initialize RSN state machine");
return -1;
}
wpa_printf(MSG_DEBUG,
"AUTH: Mesh PMKSA cache entry found for " MACSTR
" - try to use PMKSA caching instead of new SAE authentication",
MAC2STR(sta->addr));
wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
sta->sae->pmkid, sta->sae->pmk);
sae_accept_sta(hapd, sta);
sta->mesh_sae_pmksa_caching = 1;
return 0;
}
sta->mesh_sae_pmksa_caching = 0;
if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
return -1;
wpa_msg(wpa_s, MSG_DEBUG,
"AUTH: started authentication with SAE peer: " MACSTR,
MAC2STR(sta->addr));
ret = auth_sae_init_committed(hapd, sta);
if (ret)
return ret;
eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
rnd = rand() % MESH_AUTH_TIMEOUT;
eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer,
wpa_s, sta);
return 0;
}
void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid)
{
os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN);
}
static void
mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta)
{
u8 *myaddr = rsn->wpa_s->own_addr;
u8 *peer = sta->addr;
u8 *addr1, *addr2;
u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context;
/*
* AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite ||
* min(localMAC, peerMAC) || max(localMAC, peerMAC))
*/
/* Selected AKM Suite: SAE */
RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
ptr += RSN_SELECTOR_LEN;
if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
addr1 = myaddr;
addr2 = peer;
} else {
addr1 = peer;
addr2 = myaddr;
}
os_memcpy(ptr, addr1, ETH_ALEN);
ptr += ETH_ALEN;
os_memcpy(ptr, addr2, ETH_ALEN);
sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
context, sizeof(context), sta->aek, sizeof(sta->aek));
}
/* derive mesh temporal key from pmk */
int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
{
u8 *ptr;
u8 *min, *max;
u8 *myaddr = wpa_s->own_addr;
u8 *peer = sta->addr;
u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN];
/*
* MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce,
* peerNonce) || max(localNonce, peerNonce) || min(localLinkID,
* peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite ||
* min(localMAC, peerMAC) || max(localMAC, peerMAC))
*/
ptr = context;
if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) {
min = sta->my_nonce;
max = sta->peer_nonce;
} else {
min = sta->peer_nonce;
max = sta->my_nonce;
}
os_memcpy(ptr, min, WPA_NONCE_LEN);
ptr += WPA_NONCE_LEN;
os_memcpy(ptr, max, WPA_NONCE_LEN);
ptr += WPA_NONCE_LEN;
if (sta->my_lid < sta->peer_lid) {
WPA_PUT_LE16(ptr, sta->my_lid);
ptr += 2;
WPA_PUT_LE16(ptr, sta->peer_lid);
ptr += 2;
} else {
WPA_PUT_LE16(ptr, sta->peer_lid);
ptr += 2;
WPA_PUT_LE16(ptr, sta->my_lid);
ptr += 2;
}
/* Selected AKM Suite: SAE */
RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
ptr += RSN_SELECTOR_LEN;
if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
min = myaddr;
max = peer;
} else {
min = peer;
max = myaddr;
}
os_memcpy(ptr, min, ETH_ALEN);
ptr += ETH_ALEN;
os_memcpy(ptr, max, ETH_ALEN);
sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher);
sha256_prf(sta->sae->pmk, SAE_PMK_LEN,
"Temporal Key Derivation", context, sizeof(context),
sta->mtk, sta->mtk_len);
return 0;
}
void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
{
if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) {
wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
/* TODO: How to handle this more cleanly? */
}
os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN);
mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
}
/* insert AMPE and encrypted MIC at @ie.
* @mesh_rsn: mesh RSN context
* @sta: STA we're sending to
* @cat: pointer to category code in frame header.
* @buf: wpabuf to add encrypted AMPE and MIC to.
* */
int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
const u8 *cat, struct wpabuf *buf)
{
struct ieee80211_ampe_ie *ampe;
u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
u8 *ampe_ie, *pos, *mic_payload;
const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
int ret = 0;
size_t len;
len = sizeof(*ampe);
if (cat[1] == PLINK_OPEN)
len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
if (cat[1] == PLINK_OPEN && rsn->igtk_len)
len += 2 + 6 + rsn->igtk_len;
if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
wpa_printf(MSG_ERROR, "protect frame: buffer too small");
return -EINVAL;
}
ampe_ie = os_zalloc(2 + len);
if (!ampe_ie) {
wpa_printf(MSG_ERROR, "protect frame: out of memory");
return -ENOMEM;
}
/* IE: AMPE */
ampe_ie[0] = WLAN_EID_AMPE;
ampe_ie[1] = len;
ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
RSN_CIPHER_SUITE_CCMP);
os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN);
os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN);
pos = (u8 *) (ampe + 1);
if (cat[1] != PLINK_OPEN)
goto skip_keys;
/* TODO: Key Replay Counter[8] optionally for
* Mesh Group Key Inform/Acknowledge frames */
/* TODO: static mgtk for now since we don't support rekeying! */
/*
* GTKdata[variable]:
* MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
*/
os_memcpy(pos, rsn->mgtk, rsn->mgtk_len);
pos += rsn->mgtk_len;
wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos);
pos += WPA_KEY_RSC_LEN;
/* Use fixed GTKExpirationTime for now */
WPA_PUT_LE32(pos, 0xffffffff);
pos += 4;
/*
* IGTKdata[variable]:
* Key ID[2], IPN[6], IGTK[variable]
*/
if (rsn->igtk_len) {
WPA_PUT_LE16(pos, rsn->igtk_key_id);
pos += 2;
wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos);
pos += 6;
os_memcpy(pos, rsn->igtk, rsn->igtk_len);
}
skip_keys:
wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
ampe_ie, 2 + len);
/* IE: MIC */
wpabuf_put_u8(buf, WLAN_EID_MIC);
wpabuf_put_u8(buf, AES_BLOCK_SIZE);
/* MIC field is output ciphertext */
/* encrypt after MIC */
mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3,
aad, aad_len, mic_payload)) {
wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
ret = -ENOMEM;
}
os_free(ampe_ie);
return ret;
}
int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
struct ieee802_11_elems *elems, const u8 *cat,
const u8 *chosen_pmk,
const u8 *start, size_t elems_len)
{
int ret = 0;
struct ieee80211_ampe_ie *ampe;
u8 null_nonce[WPA_NONCE_LEN] = {};
u8 ampe_eid;
u8 ampe_ie_len;
u8 *ampe_buf, *crypt = NULL, *pos, *end;
size_t crypt_len;
const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
elems->mic ? (elems->mic - 2) - cat : 0 };
size_t key_len;
if (!sta->sae) {
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) {
wpa_printf(MSG_INFO,
"Mesh RSN: SAE is not prepared yet");
return -1;
}
mesh_rsn_auth_sae_sta(wpa_s, sta);
}
if (chosen_pmk &&
(!sta->sae ||
os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN) != 0)) {
wpa_msg(wpa_s, MSG_DEBUG,
"Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)");
return -1;
}
if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie");
return -1;
}
ampe_buf = (u8 *) elems->mic + elems->mic_len;
if ((int) elems_len < ampe_buf - start)
return -1;
crypt_len = elems_len - (elems->mic - start);
if (crypt_len < 2 + AES_BLOCK_SIZE) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
return -1;
}
/* crypt is modified by siv_decrypt */
crypt = os_zalloc(crypt_len);
if (!crypt) {
wpa_printf(MSG_ERROR, "Mesh RSN: out of memory");
ret = -ENOMEM;
goto free;
}
os_memcpy(crypt, elems->mic, crypt_len);
if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3,
aad, aad_len, ampe_buf)) {
wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
ret = -2;
goto free;
}
crypt_len -= AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element",
ampe_buf, crypt_len);
ampe_eid = *ampe_buf++;
ampe_ie_len = *ampe_buf++;
if (ampe_eid != WLAN_EID_AMPE ||
(size_t) 2 + ampe_ie_len > crypt_len ||
ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
ret = -1;
goto free;
}
ampe = (struct ieee80211_ampe_ie *) ampe_buf;
pos = (u8 *) (ampe + 1);
end = ampe_buf + ampe_ie_len;
if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 &&
os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
ret = -1;
goto free;
}
os_memcpy(sta->peer_nonce, ampe->local_nonce,
sizeof(ampe->local_nonce));
/* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge
* frames */
/*
* GTKdata shall not be included in Mesh Peering Confirm. While the
* standard does not state the same about IGTKdata, that same constraint
* needs to apply for it. It makes no sense to include the keys in Mesh
* Peering Close frames either, so while the standard does not seem to
* have a shall statement for these, they are described without
* mentioning GTKdata.
*
* An earlier implementation used to add GTKdata to both Mesh Peering
* Open and Mesh Peering Confirm frames, so ignore the possibly present
* GTKdata frame without rejecting the frame as a backwards
* compatibility mechanism.
*/
if (cat[1] != PLINK_OPEN) {
if (end > pos) {
wpa_hexdump_key(MSG_DEBUG,
"mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close",
pos, end - pos);
}
goto free;
}
/*
* GTKdata[variable]:
* MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
*/
sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */
key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher);
if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) {
wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element");
ret = -1;
goto free;
}
sta->mgtk_len = key_len;
os_memcpy(sta->mgtk, pos, sta->mgtk_len);
wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK",
sta->mgtk, sta->mgtk_len);
pos += sta->mgtk_len;
wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC",
pos, WPA_KEY_RSC_LEN);
os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc));
pos += WPA_KEY_RSC_LEN;
wpa_printf(MSG_DEBUG,
"mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds",
WPA_GET_LE32(pos));
pos += 4;
/*
* IGTKdata[variable]:
* Key ID[2], IPN[6], IGTK[variable]
*/
key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher);
if (end - pos >= (int) (2 + 6 + key_len)) {
sta->igtk_key_id = WPA_GET_LE16(pos);
wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u",
sta->igtk_key_id);
pos += 2;
os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc));
wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN",
sta->igtk_rsc, sizeof(sta->igtk_rsc));
pos += 6;
os_memcpy(sta->igtk, pos, key_len);
sta->igtk_len = key_len;
wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
sta->igtk, sta->igtk_len);
}
free:
os_free(crypt);
return ret;
}
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index e6adf73f3e42..baf4c2643e42 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -1,1584 +1,1710 @@
/*
* wpa_supplicant - PASN processing
*
* 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 "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/dragonfly.h"
#include "common/ptksa_cache.h"
#include "utils/eloop.h"
#include "drivers/driver.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "eap_common/eap_defs.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/pmksa_cache.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "bss.h"
#include "config.h"
static const int dot11RSNAConfigPMKLifetime = 43200;
struct wpa_pasn_auth_work {
u8 bssid[ETH_ALEN];
int akmp;
int cipher;
u16 group;
int network_id;
+ struct wpabuf *comeback;
};
+static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
+{
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+ os_free(awork);
+}
+
+
static void wpas_pasn_auth_work_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG, "PASN: Auth work timeout - stopping auth");
wpas_pasn_auth_stop(wpa_s);
}
static void wpas_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "PASN: Cancel pasn-start-auth work");
/* Remove pending/started work */
radio_remove_works(wpa_s, "pasn-start-auth", 0);
}
static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int akmp, int cipher, u8 status)
+ int akmp, int cipher, u8 status,
+ struct wpabuf *comeback,
+ u16 comeback_after)
{
+ if (comeback) {
+ size_t comeback_len = wpabuf_len(comeback);
+ size_t buflen = comeback_len * 2 + 1;
+ char *comeback_txt = os_malloc(buflen);
+
+ if (comeback_txt) {
+ wpa_snprintf_hex(comeback_txt, buflen,
+ wpabuf_head(comeback), comeback_len);
+
+ wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
+ " akmp=%s, status=%u comeback_after=%u comeback=%s",
+ MAC2STR(bssid),
+ wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ status, comeback_after, comeback_txt);
+
+ os_free(comeback_txt);
+ return;
+ }
+ }
+
wpa_msg(wpa_s, MSG_INFO,
PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
status);
}
#ifdef CONFIG_SAE
static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf = NULL;
- const char *password = NULL;
int ret;
- if (pasn->ssid) {
- password = pasn->ssid->sae_password;
- if (!password)
- password = pasn->ssid->passphrase;
- }
-
- if (!password) {
- wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
- return NULL;
- }
-
ret = sae_set_group(&pasn->sae, pasn->group);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
return NULL;
}
- /* TODO: SAE H2E */
- ret = sae_prepare_commit(wpa_s->own_addr, pasn->bssid,
- (const u8 *) password, os_strlen(password), 0,
- &pasn->sae);
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
+ wpa_s->own_addr, pasn->bssid,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
return NULL;
}
/* Need to add the entire Authentication frame body */
buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
if (!buf) {
wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
return NULL;
}
wpabuf_put_le16(buf, WLAN_AUTH_SAE);
wpabuf_put_le16(buf, 1);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
sae_write_commit(&pasn->sae, buf, NULL, 0);
pasn->sae.state = SAE_COMMITTED;
return buf;
}
static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
const u8 *data;
size_t buf_len;
u16 len, res, alg, seq, status;
int groups[] = { pasn->group, 0 };
int ret;
if (!wd)
return -1;
data = wpabuf_head_u8(wd);
buf_len = wpabuf_len(wd);
/* first handle the commit message */
if (buf_len < 2) {
wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
return -1;
}
len = WPA_GET_LE16(data);
if (len < 6 || buf_len - 2 < len) {
wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
return -1;
}
buf_len -= 2;
data += 2;
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);
- /* TODO: SAE H2E */
- if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
return -1;
}
res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
- 0);
+ 1);
if (res != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing 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;
}
buf_len -= len;
data += len;
/* Handle the confirm message */
if (buf_len < 2) {
wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
return -1;
}
len = WPA_GET_LE16(data);
if (len < 6 || buf_len - 2 < len) {
wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
return -1;
}
buf_len -= 2;
data += 2;
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, len - 6);
if (res != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
return -1;
}
wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
pasn->sae.state = SAE_ACCEPTED;
return 0;
}
static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf = NULL;
/* Need to add the entire authentication frame body */
buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
if (!buf) {
wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
return NULL;
}
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);
pasn->sae.state = SAE_CONFIRMED;
return buf;
}
+
+static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int group)
+{
+ const char *password = ssid->sae_password;
+ int groups[2] = { group, 0 };
+
+ if (!password)
+ password = ssid->passphrase;
+
+ if (!password) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+ return -1;
+ }
+
+ if (ssid->pt)
+ return 0; /* PT already derived */
+
+ ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+ (const u8 *) password, os_strlen(password),
+ ssid->sae_password_id);
+
+ return ssid->pt ? 0 : -1;
+}
+
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf = NULL;
struct wpabuf *erp_msg;
int ret;
erp_msg = eapol_sm_build_erp_reauth_start(wpa_s->eapol);
if (!erp_msg) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
return NULL;
}
if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
FILS_SESSION_LEN);
buf = wpabuf_alloc(1500);
if (!buf)
goto fail;
/* Add the authentication algorithm */
wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
/* Authentication Transaction seq# */
wpabuf_put_le16(buf, 1);
/* 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, pasn->fils.nonce, 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, pasn->fils.session, FILS_SESSION_LEN);
/* Wrapped Data (ERP) */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
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.
*/
ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
wpabuf_len(erp_msg),
pasn->fils.erp_pmkid);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
goto fail;
}
wpabuf_free(erp_msg);
erp_msg = NULL;
wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
return buf;
fail:
wpabuf_free(erp_msg);
wpabuf_free(buf);
return NULL;
}
static void wpas_pasn_initiate_eapol(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct eapol_config eapol_conf;
struct wpa_ssid *ssid = pasn->ssid;
wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
eapol_sm_notify_eap_success(wpa_s->eapol, false);
eapol_sm_notify_eap_fail(wpa_s->eapol, false);
eapol_sm_notify_portControl(wpa_s->eapol, Auto);
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
}
static struct wpabuf * wpas_pasn_wd_fils_auth(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpa_bss *bss;
const u8 *indic;
u16 fils_info;
wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
pasn->fils.completed);
/* Nothing to add as we are done */
if (pasn->fils.completed)
return NULL;
if (!pasn->ssid) {
wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
return NULL;
}
bss = wpa_bss_get_bssid(wpa_s, pasn->bssid);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: FILS: BSS not found");
return NULL;
}
indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
if (!indic || indic[1] < 2) {
wpa_printf(MSG_DEBUG, "PASN: Missing FILS Indication IE");
return NULL;
}
fils_info = WPA_GET_LE16(indic + 2);
if (!(fils_info & BIT(9))) {
wpa_printf(MSG_DEBUG,
"PASN: FILS auth without PFS not supported");
return NULL;
}
wpas_pasn_initiate_eapol(wpa_s);
return wpas_pasn_fils_build_auth(wpa_s);
}
static int wpas_pasn_wd_fils_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsne_data;
u8 rmsk[ERP_MAX_KEY_LEN];
size_t rmsk_len;
u8 anonce[FILS_NONCE_LEN];
const u8 *data;
size_t buf_len;
struct wpabuf *fils_wd = NULL;
u16 alg, seq, status;
int ret;
if (!wd)
return -1;
data = wpabuf_head(wd);
buf_len = wpabuf_len(wd);
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
data, buf_len);
/* first handle the header */
if (buf_len < 6) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
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: commit: alg=%u, seq=%u, status=%u",
alg, seq, status);
if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
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(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: ANonce", elems.fils_nonce,
FILS_NONCE_LEN);
os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
FILS_SESSION_LEN);
if (os_memcmp(pasn->fils.session, elems.fils_session,
FILS_SESSION_LEN)) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
return -1;
}
fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
WLAN_EID_EXT_WRAPPED_DATA);
if (!fils_wd) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Failed getting wrapped data");
return -1;
}
eapol_sm_process_erp_finish(wpa_s->eapol, wpabuf_head(fils_wd),
wpabuf_len(fils_wd));
wpabuf_free(fils_wd);
fils_wd = NULL;
if (eapol_sm_failed(wpa_s->eapol)) {
wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
return -1;
}
rmsk_len = ERP_MAX_KEY_LEN;
ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
if (ret == PMK_LEN) {
rmsk_len = PMK_LEN;
ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
}
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
return -1;
}
ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
pasn->fils.nonce, anonce, NULL, 0,
pasn->pmk, &pasn->pmk_len);
forced_memzero(rmsk, sizeof(rmsk));
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
return -1;
}
wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
PMKID_LEN);
wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
pasn->pmk_len, pasn->fils.erp_pmkid,
pasn->bssid, pasn->akmp);
pasn->fils.completed = true;
return 0;
}
#endif /* CONFIG_FILS */
static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
if (pasn->using_pmksa)
return NULL;
switch (pasn->akmp) {
case WPA_KEY_MGMT_PASN:
/* no wrapped data */
return NULL;
case WPA_KEY_MGMT_SAE:
#ifdef CONFIG_SAE
if (pasn->trans_seq == 0)
return wpas_pasn_wd_sae_commit(wpa_s);
if (pasn->trans_seq == 2)
return wpas_pasn_wd_sae_confirm(wpa_s);
#endif /* CONFIG_SAE */
wpa_printf(MSG_ERROR,
"PASN: SAE: Cannot derive wrapped data");
return NULL;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
#ifdef CONFIG_FILS
return wpas_pasn_wd_fils_auth(wpa_s);
#endif /* CONFIG_FILS */
case WPA_KEY_MGMT_FT_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
/*
* Wrapped data with these AKMs is optional and is only needed
* for further validation of FT security parameters. For now do
* not use them.
*/
return NULL;
default:
wpa_printf(MSG_ERROR,
"PASN: TODO: Wrapped data for akmp=0x%x",
pasn->akmp);
return NULL;
}
}
static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
{
if (pasn->using_pmksa)
return WPA_PASN_WRAPPED_DATA_NO;
/* Note: Valid AKMP is expected to already be validated */
switch (pasn->akmp) {
case WPA_KEY_MGMT_SAE:
return WPA_PASN_WRAPPED_DATA_SAE;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
return WPA_PASN_WRAPPED_DATA_FILS_SK;
case WPA_KEY_MGMT_FT_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
/*
* Wrapped data with these AKMs is optional and is only needed
* for further validation of FT security parameters. For now do
* not use them.
*/
return WPA_PASN_WRAPPED_DATA_NO;
case WPA_KEY_MGMT_PASN:
default:
return WPA_PASN_WRAPPED_DATA_NO;
}
}
-static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
+static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *comeback)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
const u8 *pmkid;
u8 wrapped_data;
int ret;
u16 capab;
wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
if (pasn->trans_seq)
return NULL;
buf = wpabuf_alloc(1500);
if (!buf)
goto fail;
/* Get public key */
pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
if (!pubkey) {
wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
goto fail;
}
wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
wpa_pasn_build_auth_header(buf, pasn->bssid,
wpa_s->own_addr, pasn->bssid,
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
pmkid = NULL;
if (wpa_key_mgmt_ft(pasn->akmp)) {
ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
pasn->bssid,
pasn->pmk_r1,
&pasn->pmk_r1_len,
pasn->pmk_r1_name);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: FT: Failed to derive keys");
goto fail;
}
pmkid = pasn->pmk_r1_name;
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
struct rsn_pmksa_cache_entry *pmksa;
pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
NULL, NULL, pasn->akmp);
if (pmksa)
pmkid = pmksa->pmkid;
/*
* Note: Even when PMKSA is available, also add wrapped data as
* it is possible that the PMKID is no longer valid at the AP.
*/
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
}
if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
goto fail;
if (!wrapped_data_buf)
wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
- pubkey, true, NULL, -1);
+ pubkey, true, comeback, -1);
if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
goto fail;
/* Add own RNSXE */
- /* TODO: How to handle protected TWT and SAE H2E? */
capab = 0;
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
wpa_pasn_add_rsnxe(buf, capab);
ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
wpabuf_len(buf) - IEEE80211_HDRLEN,
pasn->hash);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
goto fail;
}
pasn->trans_seq++;
wpabuf_free(wrapped_data_buf);
wpabuf_free(pubkey);
wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
return buf;
fail:
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(wrapped_data_buf);
wpabuf_free(pubkey);
wpabuf_free(buf);
return NULL;
}
static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf, *wrapped_data_buf = NULL;
u8 mic[WPA_PASN_MAX_MIC_LEN];
u8 mic_len, data_len;
const u8 *data;
u8 *ptr;
u8 wrapped_data;
int ret;
wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
if (pasn->trans_seq != 2)
return NULL;
buf = wpabuf_alloc(1500);
if (!buf)
goto fail;
wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
wpa_pasn_build_auth_header(buf, pasn->bssid,
wpa_s->own_addr, pasn->bssid,
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
if (!wrapped_data_buf)
wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
NULL, false, NULL, -1);
if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
goto fail;
wpabuf_free(wrapped_data_buf);
wrapped_data_buf = NULL;
/* Add the MIC */
mic_len = pasn_mic_len(pasn->akmp, 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);
data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
wpa_s->own_addr, pasn->bssid,
pasn->hash, mic_len * 2, data, data_len, mic);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
goto fail;
}
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->conf->pasn_corrupt_mic) {
wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
mic[0] = ~mic[0];
}
#endif /* CONFIG_TESTING_OPTIONS */
os_memcpy(ptr, mic, mic_len);
pasn->trans_seq++;
wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
return buf;
fail:
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(wrapped_data_buf);
wpabuf_free(buf);
return NULL;
}
static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
wpa_printf(MSG_DEBUG, "PASN: Reset");
crypto_ecdh_deinit(pasn->ecdh);
pasn->ecdh = NULL;
wpas_pasn_cancel_auth_work(wpa_s);
wpa_s->pasn_auth_work = NULL;
eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL);
pasn->akmp = 0;
pasn->cipher = 0;
pasn->group = 0;
pasn->trans_seq = 0;
pasn->pmk_len = 0;
pasn->using_pmksa = false;
forced_memzero(pasn->pmk, sizeof(pasn->pmk));
forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
forced_memzero(&pasn->hash, sizeof(pasn->hash));
wpabuf_free(pasn->beacon_rsne_rsnxe);
pasn->beacon_rsne_rsnxe = NULL;
+ wpabuf_free(pasn->comeback);
+ pasn->comeback = NULL;
+ pasn->comeback_after = 0;
+
#ifdef CONFIG_SAE
sae_clear_data(&pasn->sae);
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
os_memset(&pasn->fils, 0, sizeof(pasn->fils));
#endif /* CONFIG_FILS*/
#ifdef CONFIG_IEEE80211R
forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
pasn->pmk_r1_len = 0;
os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
#endif /* CONFIG_IEEE80211R */
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
struct wpa_ie_data *rsn_data,
struct wpa_pasn_params_data *pasn_data,
struct wpabuf *wrapped_data)
{
static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
struct wpas_pasn *pasn = &wpa_s->pasn;
os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
pasn->pmk_len = 0;
if (pasn->akmp == WPA_KEY_MGMT_PASN) {
wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
pasn->pmk_len = WPA_PASN_PMK_LEN;
os_memcpy(pasn->pmk, pasn_default_pmk,
sizeof(pasn_default_pmk));
return 0;
}
if (wpa_key_mgmt_ft(pasn->akmp)) {
#ifdef CONFIG_IEEE80211R
wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
pasn->pmk_len = pasn->pmk_r1_len;
os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
pasn->using_pmksa = true;
return 0;
#else /* CONFIG_IEEE80211R */
wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
return -1;
#endif /* CONFIG_IEEE80211R */
}
if (rsn_data->num_pmkid) {
struct rsn_pmksa_cache_entry *pmksa;
pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
rsn_data->pmkid, NULL,
pasn->akmp);
if (pmksa) {
wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
pasn->pmk_len = pmksa->pmk_len;
os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
pasn->using_pmksa = true;
return 0;
}
}
#ifdef CONFIG_SAE
if (pasn->akmp == WPA_KEY_MGMT_SAE) {
int ret;
ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed processing SAE wrapped data");
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
return -1;
}
wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
pasn->pmk_len = PMK_LEN;
os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
pasn->pmk_len, pasn->sae.pmkid,
pasn->bssid, pasn->akmp);
return 0;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
int ret;
ret = wpas_pasn_wd_fils_rx(wpa_s, wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed processing FILS wrapped data");
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
return -1;
}
return 0;
}
#endif /* CONFIG_FILS */
/* TODO: Derive PMK based on wrapped data */
wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
return -1;
}
static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
int akmp, int cipher, u16 group, int freq,
const u8 *beacon_rsne, u8 beacon_rsne_len,
const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
- int network_id)
+ int network_id, struct wpabuf *comeback)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpa_ssid *ssid = NULL;
struct wpabuf *frame;
int ret;
/* TODO: Currently support only ECC groups */
if (!dragonfly_suitable_group(group, 1)) {
wpa_printf(MSG_DEBUG,
"PASN: Reject unsuitable group %u", group);
return -1;
}
ssid = wpa_config_get_network(wpa_s->conf, network_id);
switch (akmp) {
case WPA_KEY_MGMT_PASN:
break;
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
if (!ssid) {
wpa_printf(MSG_DEBUG,
"PASN: No network profile found for SAE");
return -1;
}
+
+ if (!ieee802_11_rsnx_capab(beacon_rsnxe,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support SAE H2E");
+ return -1;
+ }
+
+ if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive PT");
+ return -1;
+ }
+
pasn->sae.state = SAE_NOTHING;
pasn->sae.send_confirm = 0;
pasn->ssid = ssid;
break;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
pasn->ssid = ssid;
break;
#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:
break;
#endif /* CONFIG_IEEE80211R */
default:
wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
return -1;
}
pasn->ecdh = crypto_ecdh_init(group);
if (!pasn->ecdh) {
wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
goto fail;
}
pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
beacon_rsnxe_len);
if (!pasn->beacon_rsne_rsnxe) {
wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE/RSNXE");
goto fail;
}
wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, beacon_rsne_len);
if (beacon_rsnxe && beacon_rsnxe_len)
wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
beacon_rsnxe_len);
pasn->akmp = akmp;
pasn->cipher = cipher;
pasn->group = group;
pasn->freq = freq;
+
+ if (wpa_s->conf->force_kdk_derivation ||
+ (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF &&
+ ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
os_memcpy(pasn->bssid, bssid, ETH_ALEN);
wpa_printf(MSG_DEBUG,
"PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
pasn->group);
- frame = wpas_pasn_build_auth_1(wpa_s);
+ frame = wpas_pasn_build_auth_1(wpa_s, comeback);
if (!frame) {
wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
goto fail;
}
ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
pasn->freq, 1000);
wpabuf_free(frame);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
goto fail;
}
eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
return 0;
fail:
return -1;
}
static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
const u8 *bssid, int akmp, int cipher)
{
struct wpa_bss *bss;
const u8 *rsne;
struct wpa_ie_data rsne_data;
int ret;
if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Not doing authentication with current BSS");
return NULL;
}
bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: BSS not found");
return NULL;
}
rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (!rsne) {
wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
return NULL;
}
ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data");
return NULL;
}
if (!(rsne_data.key_mgmt & akmp) ||
!(rsne_data.pairwise_cipher & cipher)) {
wpa_printf(MSG_DEBUG,
"PASN: AP does not support requested AKMP or cipher");
return NULL;
}
return bss;
}
static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpa_pasn_auth_work *awork = work->ctx;
struct wpa_bss *bss;
const u8 *rsne, *rsnxe;
int ret;
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
if (deinit) {
if (work->started) {
eloop_cancel_timeout(wpas_pasn_auth_work_timeout,
wpa_s, NULL);
wpa_s->pasn_auth_work = NULL;
}
- os_free(awork);
+
+ wpas_pasn_free_auth_work(awork);
return;
}
/*
* It is possible that by the time the callback is called, the PASN
* authentication is not allowed, e.g., a connection with the AP was
* established.
*/
bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
awork->cipher);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed");
goto fail;
}
rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (!rsne) {
wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
goto fail;
}
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
awork->group, bss->freq, rsne, *(rsne + 1) + 2,
rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
- awork->network_id);
+ awork->network_id, awork->comeback);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to start PASN authentication");
goto fail;
}
+ /* comeback token is no longer needed at this stage */
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+
wpa_s->pasn_auth_work = work;
return;
fail:
- os_free(awork);
+ wpas_pasn_free_auth_work(awork);
work->ctx = NULL;
radio_work_done(work);
}
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int akmp, int cipher, u16 group, int network_id)
+ int akmp, int cipher, u16 group, int network_id,
+ const u8 *comeback, size_t comeback_len)
{
struct wpa_pasn_auth_work *awork;
struct wpa_bss *bss;
wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x",
MAC2STR(bssid), akmp, cipher);
/*
* TODO: Consider modifying the offchannel logic to handle additional
* Management frames other then Action frames. For now allow PASN only
* with drivers that support off-channel TX.
*/
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) {
wpa_printf(MSG_DEBUG,
"PASN: Driver does not support offchannel TX");
return -1;
}
if (radio_work_pending(wpa_s, "pasn-start-auth")) {
wpa_printf(MSG_DEBUG,
"PASN: send_auth: Work is already pending");
return -1;
}
if (wpa_s->pasn_auth_work) {
wpa_printf(MSG_DEBUG, "PASN: send_auth: Already in progress");
return -1;
}
bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
if (!bss)
return -1;
wpas_pasn_reset(wpa_s);
awork = os_zalloc(sizeof(*awork));
if (!awork)
return -1;
os_memcpy(awork->bssid, bssid, ETH_ALEN);
awork->akmp = akmp;
awork->cipher = cipher;
awork->group = group;
awork->network_id = network_id;
+ if (comeback && comeback_len) {
+ awork->comeback = wpabuf_alloc_copy(comeback, comeback_len);
+ if (!awork->comeback) {
+ wpas_pasn_free_auth_work(awork);
+ return -1;
+ }
+ }
+
if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
wpas_pasn_auth_start_cb, awork) < 0) {
- os_free(awork);
+ wpas_pasn_free_auth_work(awork);
return -1;
}
wpa_printf(MSG_DEBUG, "PASN: Auth work successfully added");
return 0;
}
void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
if (!wpa_s->pasn.ecdh)
return;
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
- pasn->status);
+ pasn->status, pasn->comeback,
+ pasn->comeback_after);
wpas_pasn_reset(wpa_s);
}
+static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
+ struct wpas_pasn *pasn,
+ struct wpa_pasn_params_data *params)
+{
+ int akmp = pasn->akmp;
+ int cipher = pasn->cipher;
+ u16 group = pasn->group;
+ u8 bssid[ETH_ALEN];
+ int network_id = pasn->ssid ? pasn->ssid->id : 0;
+
+ wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
+ os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+ wpas_pasn_reset(wpa_s);
+
+ return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
+ network_id,
+ params->comeback, params->comeback_len);
+}
+
+
int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
u8 mic_len;
u16 status;
int ret, inc_y;
u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_AUTH << 4));
if (!wpa_s->pasn_auth_work || !mgmt ||
len < offsetof(struct ieee80211_mgmt, u.auth.variable))
return -2;
/* Not an Authentication frame; do nothing */
if ((mgmt->frame_control & fc) != fc)
return -2;
/* Not our frame; do nothing */
if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) != 0 ||
os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
return -2;
/* Not PASN; do nothing */
if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
return -2;
if (mgmt->u.auth.auth_transaction !=
host_to_le16(pasn->trans_seq + 1)) {
wpa_printf(MSG_DEBUG,
"PASN: RX: Invalid transaction sequence: (%u != %u)",
le_to_host16(mgmt->u.auth.auth_transaction),
pasn->trans_seq + 1);
return -1;
}
status = le_to_host16(mgmt->u.auth.status_code);
if (status != WLAN_STATUS_SUCCESS &&
status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
wpa_printf(MSG_DEBUG,
"PASN: Authentication rejected - status=%u", status);
pasn->status = status;
wpas_pasn_auth_stop(wpa_s);
return -1;
}
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(pasn->akmp, pasn->cipher);
if (status == WLAN_STATUS_SUCCESS) {
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 be modifying the
* received message buffer. */
os_memset((u8 *) elems.mic, 0, mic_len);
}
}
if (!elems.pasn_params || !elems.pasn_params_len) {
wpa_printf(MSG_DEBUG,
"PASN: Missing PASN Parameters IE");
goto fail;
}
ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
elems.pasn_params_len + 3,
true, &pasn_params);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed validation PASN of Parameters IE");
goto fail;
}
- /* TODO: handle comeback flow */
if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
wpa_printf(MSG_DEBUG,
"PASN: Authentication temporarily rejected");
+
+ if (pasn_params.comeback && pasn_params.comeback_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Comeback token available. After=%u",
+ pasn_params.after);
+
+ if (!pasn_params.after)
+ return wpas_pasn_immediate_retry(wpa_s, pasn,
+ &pasn_params);
+
+ pasn->comeback = wpabuf_alloc_copy(
+ pasn_params.comeback, pasn_params.comeback_len);
+ if (pasn->comeback)
+ pasn->comeback_after = pasn_params.after;
+ }
+
+ pasn->status = status;
goto fail;
}
ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
goto fail;
}
ret = wpa_pasn_validate_rsne(&rsn_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
goto fail;
}
if (pasn->akmp != rsn_data.key_mgmt ||
pasn->cipher != rsn_data.pairwise_cipher) {
wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
goto fail;
}
if (pasn->group != pasn_params.group) {
wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
goto fail;
}
if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
goto fail;
}
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]);
goto fail;
}
secret = crypto_ecdh_set_peerkey(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");
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;
}
}
ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
goto fail;
}
ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
wpa_s->own_addr, pasn->bssid,
wpabuf_head(secret), wpabuf_len(secret),
&pasn->ptk, pasn->akmp, pasn->cipher,
- WPA_KDK_MAX_LEN);
+ pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
goto fail;
}
wpabuf_free(wrapped_data);
wrapped_data = NULL;
wpabuf_free(secret);
secret = NULL;
/* Verify the MIC */
ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
pasn->bssid, wpa_s->own_addr,
wpabuf_head(pasn->beacon_rsne_rsnxe),
wpabuf_len(pasn->beacon_rsne_rsnxe),
(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;
}
pasn->trans_seq++;
wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
frame = wpas_pasn_build_auth_3(wpa_s);
if (!frame) {
wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
goto fail;
}
ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
pasn->freq, 100);
wpabuf_free(frame);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
goto fail;
}
wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher,
dot11RSNAConfigPMKLifetime, &pasn->ptk);
forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
pasn->status = WLAN_STATUS_SUCCESS;
return 0;
fail:
wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
wpabuf_free(wrapped_data);
wpabuf_free(secret);
/*
* TODO: In case of an error the standard allows to silently drop
* the frame and terminate the authentication exchange. However, better
* reply to the AP with an error status.
*/
- pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (status == WLAN_STATUS_SUCCESS)
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ pasn->status = status;
+
wpas_pasn_auth_stop(wpa_s);
return -1;
}
int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
const u8 *data, size_t data_len, u8 acked)
{
struct wpas_pasn *pasn = &wpa_s->pasn;
const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data;
u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_AUTH << 4));
wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
if (!wpa_s->pasn_auth_work) {
wpa_printf(MSG_DEBUG,
"PASN: auth_tx_status: no work in progress");
return -1;
}
if (!mgmt ||
data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
return -1;
/* Not an authentication frame; do nothing */
if ((mgmt->frame_control & fc) != fc)
return -1;
/* Not our frame; do nothing */
if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) ||
os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
return -1;
/* Not PASN; do nothing */
if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
return -1;
if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
wpa_printf(MSG_ERROR,
"PASN: Invalid transaction sequence: (%u != %u)",
pasn->trans_seq,
le_to_host16(mgmt->u.auth.auth_transaction));
return 0;
}
wpa_printf(MSG_ERROR,
"PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
acked);
/*
* Even if the frame was not acked, do not treat this is an error, and
* try to complete the flow, relying on the PASN timeout callback to
* clean up.
*/
if (pasn->trans_seq == 3) {
wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
MAC2STR(pasn->bssid));
/*
* Either frame was not ACKed or it was ACKed but the trans_seq
* != 1, i.e., not expecting an RX frame, so we are done.
*/
wpas_pasn_auth_stop(wpa_s);
}
return 0;
}
int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_bss *bss;
struct wpabuf *buf;
struct ieee80211_mgmt *deauth;
int ret;
if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"PASN: Cannot deauthenticate from current BSS");
return -1;
}
wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
MACSTR, MAC2STR(bssid));
ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_DEBUG, "PASN: deauth: BSS not found");
return -1;
}
buf = wpabuf_alloc(64);
if (!buf) {
wpa_printf(MSG_DEBUG, "PASN: deauth: Failed wpabuf allocate");
return -1;
}
deauth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.deauth.variable));
deauth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_DEAUTH << 4));
os_memcpy(deauth->da, bssid, ETH_ALEN);
os_memcpy(deauth->sa, wpa_s->own_addr, ETH_ALEN);
os_memcpy(deauth->bssid, bssid, ETH_ALEN);
deauth->u.deauth.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
/*
* Since we do not expect any response from the AP, implement the
* Deauthentication frame transmission using direct call to the driver
* without a radio work.
*/
ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
bss->freq, 0);
wpabuf_free(buf);
wpa_printf(MSG_DEBUG, "PASN: deauth: send_mlme ret=%d", ret);
return ret;
}
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index de49948f71e4..97c16fb80d27 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -1,373 +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);
- if (wpa_s->ctrl_iface) {
- wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
- wpa_s->ctrl_iface = NULL;
- }
+ 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 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;
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/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index f6da56eeda9b..39d282f0870d 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -1,155 +1,155 @@
/*
* 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 "common/wpa_ctrl.h"
#include "common/ieee802_11_common.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "bss.h"
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;
}
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;
}
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 = *buf;
+ 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;
}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index dde80863a988..72aa9b5b8f18 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1,2943 +1,2942 @@
/*
* wpa_supplicant - SME
* Copyright (c) 2009-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/ieee802_11_common.h"
#include "common/ocv.h"
#include "common/hw_features_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "common/wpa_common.h"
#include "common/sae.h"
#include "common/dpp.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/pmksa_cache.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "p2p_supplicant.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
#include "sme.h"
#include "hs20_supplicant.h"
#define SME_AUTH_TIMEOUT 5
#define SME_ASSOC_TIMEOUT 5
static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
#ifdef CONFIG_SAE
static int index_within_array(const int *array, int idx)
{
int i;
for (i = 0; i < idx; i++) {
if (array[i] <= 0)
return 0;
}
return 1;
}
static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
{
int *groups = wpa_s->conf->sae_groups;
int default_groups[] = { 19, 20, 21, 0 };
if (!groups || groups[0] <= 0)
groups = default_groups;
/* Configuration may have changed, so validate current index */
if (!index_within_array(groups, wpa_s->sme.sae_group_index))
return -1;
for (;;) {
int group = groups[wpa_s->sme.sae_group_index];
if (group <= 0)
break;
if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
wpa_s->sme.sae.group);
return 0;
}
wpa_s->sme.sae_group_index++;
}
return -1;
}
static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const u8 *bssid, int external,
int reuse, int *ret_use_pt,
bool *ret_use_pk)
{
struct wpabuf *buf;
size_t len;
const char *password;
struct wpa_bss *bss;
int use_pt = 0;
bool use_pk = false;
u8 rsnxe_capa = 0;
if (ret_use_pt)
*ret_use_pt = 0;
if (ret_use_pk)
*ret_use_pk = false;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->sae_commit_override) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
if (!buf)
return NULL;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
}
wpabuf_put_buf(buf, wpa_s->sae_commit_override);
return buf;
}
#endif /* CONFIG_TESTING_OPTIONS */
password = ssid->sae_password;
if (!password)
password = ssid->passphrase;
if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
if (reuse && wpa_s->sme.sae.tmp &&
os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"SAE: Reuse previously generated PWE on a retry with the same AP");
use_pt = wpa_s->sme.sae.h2e;
use_pk = wpa_s->sme.sae.pk;
goto reuse_data;
}
if (sme_set_sae_group(wpa_s) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
return NULL;
}
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
if (bss) {
const u8 *rsnxe;
rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (rsnxe && rsnxe[1] >= 1)
rsnxe_capa = rsnxe[2];
}
if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
use_pt = 1;
#ifdef CONFIG_SAE_PK
if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
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)))) {
use_pt = 1;
use_pk = true;
}
if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use PK with the selected AP");
return NULL;
}
#endif /* CONFIG_SAE_PK */
if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
wpa_s->conf->sae_pwe != 3 &&
!use_pt) {
wpa_printf(MSG_DEBUG,
"SAE: Cannot use H2E with the selected AP");
return NULL;
}
}
if (use_pt &&
sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
wpa_s->own_addr, bssid,
wpa_s->sme.sae_rejected_groups, NULL) < 0)
return NULL;
if (!use_pt &&
sae_prepare_commit(wpa_s->own_addr, bssid,
(u8 *) password, os_strlen(password),
- ssid->sae_password_id,
&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
if (wpa_s->sme.sae.tmp) {
os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
if (use_pt && use_pk)
wpa_s->sme.sae.pk = 1;
#ifdef CONFIG_SAE_PK
os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
ETH_ALEN);
os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
sae_pk_set_password(&wpa_s->sme.sae, password);
#endif /* CONFIG_SAE_PK */
}
reuse_data:
len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0;
if (ssid->sae_password_id)
len += 4 + os_strlen(ssid->sae_password_id);
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
if (buf == NULL)
return NULL;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
if (use_pk)
wpabuf_put_le16(buf, WLAN_STATUS_SAE_PK);
else if (use_pt)
wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
else
wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS);
}
if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
ssid->sae_password_id) < 0) {
wpabuf_free(buf);
return NULL;
}
if (ret_use_pt)
*ret_use_pt = use_pt;
if (ret_use_pk)
*ret_use_pk = use_pk;
return buf;
}
static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s,
int external)
{
struct wpabuf *buf;
buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN);
if (buf == NULL)
return NULL;
if (!external) {
wpabuf_put_le16(buf, 2); /* Transaction seq# */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
}
sae_write_confirm(&wpa_s->sme.sae, buf);
return buf;
}
#endif /* CONFIG_SAE */
/**
* sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt
* @wpa_s: Pointer to wpa_supplicant data
* @bss: Pointer to the bss which is the target of authentication attempt
*/
static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
const u8 rrm_ie_len = 5;
u8 *pos;
const u8 *rrm_ie;
wpa_s->rrm.rrm_used = 0;
wpa_printf(MSG_DEBUG,
"RRM: Determining whether RRM can be used - device support: 0x%x",
wpa_s->drv_rrm_flags);
rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES);
if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) {
wpa_printf(MSG_DEBUG, "RRM: No RRM in network");
return;
}
if (!((wpa_s->drv_rrm_flags &
WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) {
wpa_printf(MSG_DEBUG,
"RRM: Insufficient RRM support in driver - do not use RRM");
return;
}
if (sizeof(wpa_s->sme.assoc_req_ie) <
wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) {
wpa_printf(MSG_INFO,
"RRM: Unable to use RRM, no room for RRM IE");
return;
}
wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request");
pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
os_memset(pos, 0, 2 + rrm_ie_len);
*pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
*pos++ = rrm_ie_len;
/* Set supported capabilities flags */
if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
if (wpa_s->lci)
pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT;
wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
wpa_s->rrm.rrm_used = 1;
}
static void sme_send_authentication(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
int start)
{
struct wpa_driver_auth_params params;
struct wpa_ssid *old_ssid;
#ifdef CONFIG_IEEE80211R
const u8 *ie;
#endif /* CONFIG_IEEE80211R */
#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
const u8 *md = NULL;
#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
int bssid_changed;
struct wpabuf *resp = NULL;
u8 ext_capab[18];
int ext_capab_len;
int skip_auth;
u8 *wpa_ie;
size_t wpa_ie_len;
#ifdef CONFIG_MBO
const u8 *mbo_ie;
#endif /* CONFIG_MBO */
int omit_rsnxe = 0;
if (bss == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
"the network");
wpas_connect_work_done(wpa_s);
return;
}
skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
wpa_s->reassoc_same_bss;
wpa_s->current_bss = bss;
os_memset(&params, 0, sizeof(params));
wpa_s->reassociate = 0;
params.freq = bss->freq;
params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
params.p2p = ssid->p2p_group;
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
wpa_s->sme.prev_bssid_set = 0;
wpa_s->sme.freq = params.freq;
os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
wpa_s->sme.ssid_len = params.ssid_len;
params.auth_alg = WPA_AUTH_ALG_OPEN;
#ifdef IEEE8021X_EAPOL
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (ssid->leap) {
if (ssid->non_leap == 0)
params.auth_alg = WPA_AUTH_ALG_LEAP;
else
params.auth_alg |= WPA_AUTH_ALG_LEAP;
}
}
#endif /* IEEE8021X_EAPOL */
wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
params.auth_alg);
if (ssid->auth_alg) {
params.auth_alg = ssid->auth_alg;
wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
"0x%x", params.auth_alg);
}
#ifdef CONFIG_SAE
wpa_s->sme.sae_pmksa_caching = 0;
if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
const u8 *rsn;
struct wpa_ie_data ied;
rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (!rsn) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE enabled, but target BSS does not advertise RSN");
#ifdef CONFIG_DPP
} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
(ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
(ied.key_mgmt & WPA_KEY_MGMT_DPP)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled");
#endif /* CONFIG_DPP */
} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
wpa_key_mgmt_sae(ied.key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
params.auth_alg = WPA_AUTH_ALG_SAE;
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
}
}
#endif /* CONFIG_SAE */
#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_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,
wpa_s->current_ssid,
try_opportunistic, cache_id,
0) == 0)
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
return;
}
#ifdef CONFIG_HS20
} else if (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_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites");
wpas_connect_work_done(wpa_s);
return;
}
#endif /* CONFIG_HS20 */
} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
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_s->sme.assoc_req_ie_len = 0;
} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
wpas_connect_work_done(wpa_s);
return;
}
#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) <=
sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
wpa_s->sme.assoc_req_ie_len);
} else
wpa_s->sme.assoc_req_ie_len = 0;
wpabuf_free(wps_ie);
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
#endif /* CONFIG_WPS */
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_s->sme.assoc_req_ie_len = 0;
}
/* In case the WPA vendor IE is used, it should be placed after all the
* non-vendor IEs, as the lower layer expects the IEs to be ordered as
* defined in the standard. Store the WPA IE so it can later be
* inserted at the correct location.
*/
wpa_ie = NULL;
wpa_ie_len = 0;
if (wpa_s->wpa_proto == WPA_PROTO_WPA) {
wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
if (wpa_ie) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE");
wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
wpa_s->sme.assoc_req_ie_len = 0;
} else {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE");
wpas_connect_work_done(wpa_s);
return;
}
}
#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 && (!wpa_key_mgmt_ft(ssid->key_mgmt) ||
!wpa_key_mgmt_ft(wpa_s->key_mgmt)))
md = NULL;
if (md) {
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
if (md) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
md[0], md[1]);
omit_rsnxe = !wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (wpa_s->sme.assoc_req_ie_len + 5 <
sizeof(wpa_s->sme.assoc_req_ie)) {
struct rsn_mdie *mdie;
u8 *pos = wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len;
*pos++ = WLAN_EID_MOBILITY_DOMAIN;
*pos++ = sizeof(*mdie);
mdie = (struct rsn_mdie *) pos;
os_memcpy(mdie->mobility_domain, md,
MOBILITY_DOMAIN_ID_LEN);
mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN];
wpa_s->sme.assoc_req_ie_len += 5;
}
if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
wpa_sm_has_ptk(wpa_s->wpa)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
"over-the-air");
params.auth_alg = WPA_AUTH_ALG_FT;
params.ie = wpa_s->sme.ft_ies;
params.ie_len = wpa_s->sme.ft_ies_len;
}
}
#endif /* CONFIG_IEEE80211R */
wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
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, "SME: Selected AP supports "
"MFP: require MFP");
wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#ifdef CONFIG_P2P
if (wpa_s->global->p2p) {
u8 *pos;
size_t len;
int res;
pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
len = sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len;
res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
ssid->p2p_group);
if (res >= 0)
wpa_s->sme.assoc_req_ie_len += res;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_FST
if (wpa_s->fst_ies) {
int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
sizeof(wpa_s->sme.assoc_req_ie)) {
os_memcpy(wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
wpabuf_head(wpa_s->fst_ies),
fst_ies_len);
wpa_s->sme.assoc_req_ie_len += fst_ies_len;
}
}
#endif /* CONFIG_FST */
sme_auth_handle_rrm(wpa_s, bss);
wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
wpa_s, ssid, bss,
wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len);
if (params.p2p)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
else
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) {
u8 *pos = wpa_s->sme.assoc_req_ie;
if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
pos += 2 + pos[1];
os_memmove(pos + ext_capab_len, pos,
wpa_s->sme.assoc_req_ie_len -
(pos - wpa_s->sme.assoc_req_ie));
wpa_s->sme.assoc_req_ie_len += ext_capab_len;
os_memcpy(pos, ext_capab, ext_capab_len);
}
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->rsnxe_override_assoc &&
wpabuf_len(wpa_s->rsnxe_override_assoc) <=
sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) {
wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override");
os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
wpabuf_head(wpa_s->rsnxe_override_assoc),
wpabuf_len(wpa_s->rsnxe_override_assoc));
wpa_s->sme.assoc_req_ie_len +=
wpabuf_len(wpa_s->rsnxe_override_assoc);
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->rsnxe_len > 0 &&
wpa_s->rsnxe_len <=
sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len &&
!omit_rsnxe) {
os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
wpa_s->rsnxe, wpa_s->rsnxe_len);
wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_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 = sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len;
if (wpabuf_len(hs20) <= len) {
os_memcpy(wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
wpabuf_head(hs20), wpabuf_len(hs20));
wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
}
wpabuf_free(hs20);
}
}
#endif /* CONFIG_HS20 */
if (wpa_ie) {
size_t len;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE");
len = sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len;
if (len > wpa_ie_len) {
os_memcpy(wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
wpa_ie, wpa_ie_len);
wpa_s->sme.assoc_req_ie_len += wpa_ie_len;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE");
}
os_free(wpa_ie);
}
if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
len = sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len;
if (wpabuf_len(buf) <= len) {
os_memcpy(wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
wpabuf_head(buf), wpabuf_len(buf));
wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
}
}
#ifdef CONFIG_MBO
mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
if (!wpa_s->disable_mbo_oce && mbo_ie) {
int len;
len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len,
!!mbo_attr_from_mbo_ie(mbo_ie,
OCE_ATTR_ID_CAPA_IND));
if (len >= 0)
wpa_s->sme.assoc_req_ie_len += len;
}
#endif /* CONFIG_MBO */
#ifdef CONFIG_SAE
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
NULL,
wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ?
WPA_KEY_MGMT_FT_SAE :
WPA_KEY_MGMT_SAE) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
params.auth_alg = WPA_AUTH_ALG_OPEN;
wpa_s->sme.sae_pmksa_caching = 1;
}
if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
if (start)
resp = sme_auth_build_sae_commit(wpa_s, ssid,
bss->bssid, 0,
start == 2, NULL,
NULL);
else
resp = sme_auth_build_sae_confirm(wpa_s, 0);
if (resp == NULL) {
wpas_connection_failed(wpa_s, bss->bssid);
return;
}
params.auth_data = wpabuf_head(resp);
params.auth_data_len = wpabuf_len(resp);
wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
}
#endif /* CONFIG_SAE */
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);
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
#ifdef CONFIG_FILS
/* TODO: FILS operations can in some cases be done between different
* network_ctx (i.e., same credentials can be used with multiple
* networks). */
if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
wpa_key_mgmt_fils(ssid->key_mgmt)) {
const u8 *indic;
u16 fils_info;
const u8 *realm, *username, *rrk;
size_t realm_len, username_len, rrk_len;
u16 next_seq_num;
/*
* Check FILS Indication element (FILS Information field) bits
* indicating supported authentication algorithms against local
* configuration (ssid->fils_dh_group). Try to use FILS
* authentication only if the AP supports the combination in the
* network profile. */
indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
if (!indic || indic[1] < 2) {
wpa_printf(MSG_DEBUG, "SME: " MACSTR
" does not include FILS Indication element - cannot use FILS authentication with it",
MAC2STR(bss->bssid));
goto no_fils;
}
fils_info = WPA_GET_LE16(indic + 2);
if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) {
wpa_printf(MSG_DEBUG, "SME: " MACSTR
" does not support FILS SK without PFS - cannot use FILS authentication with it",
MAC2STR(bss->bssid));
goto no_fils;
}
if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) {
wpa_printf(MSG_DEBUG, "SME: " MACSTR
" does not support FILS SK with PFS - cannot use FILS authentication with it",
MAC2STR(bss->bssid));
goto no_fils;
}
if (wpa_s->last_con_fail_realm &&
eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
&username, &username_len,
&realm, &realm_len, &next_seq_num,
&rrk, &rrk_len) == 0 &&
realm && realm_len == wpa_s->last_con_fail_realm_len &&
os_memcmp(realm, wpa_s->last_con_fail_realm,
realm_len) == 0) {
wpa_printf(MSG_DEBUG,
"SME: FILS authentication for this realm failed last time - try to regenerate ERP key hierarchy");
goto no_fils;
}
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
ssid, 0,
wpa_bss_get_fils_cache_id(bss),
0) == 0)
wpa_printf(MSG_DEBUG,
"SME: Try to use FILS with PMKSA caching");
resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md);
if (resp) {
int auth_alg;
if (ssid->fils_dh_group)
wpa_printf(MSG_DEBUG,
"SME: Try to use FILS SK authentication with PFS (DH Group %u)",
ssid->fils_dh_group);
else
wpa_printf(MSG_DEBUG,
"SME: Try to use FILS SK authentication without PFS");
auth_alg = ssid->fils_dh_group ?
WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
params.auth_alg = auth_alg;
params.auth_data = wpabuf_head(resp);
params.auth_data_len = wpabuf_len(resp);
wpa_s->sme.auth_alg = auth_alg;
}
}
no_fils:
#endif /* CONFIG_FILS */
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
eapol_sm_notify_portValid(wpa_s->eapol, false);
wpa_clear_keys(wpa_s, bss->bssid);
wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
#ifdef CONFIG_HS20
hs20_configure_frame_filters(wpa_s);
#endif /* CONFIG_HS20 */
#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) {
wpa_printf(MSG_DEBUG,
"Conflicting frequency found (%d != %d)",
freq, params.freq);
if (wpas_p2p_handle_frequency_conflicts(wpa_s,
params.freq,
ssid) < 0) {
wpas_connection_failed(wpa_s, bss->bssid);
wpa_supplicant_mark_disassoc(wpa_s);
wpabuf_free(resp);
wpas_connect_work_done(wpa_s);
return;
}
}
}
#endif /* CONFIG_P2P */
if (skip_auth) {
wpa_msg(wpa_s, MSG_DEBUG,
"SME: Skip authentication step on reassoc-to-same-BSS");
wpabuf_free(resp);
sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN);
return;
}
wpa_s->sme.auth_alg = params.auth_alg;
if (wpa_drv_authenticate(wpa_s, &params) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
"driver failed");
wpas_connection_failed(wpa_s, bss->bssid);
wpa_supplicant_mark_disassoc(wpa_s);
wpabuf_free(resp);
wpas_connect_work_done(wpa_s);
return;
}
eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
NULL);
/*
* Association will be started based on the authentication event from
* the driver.
*/
wpabuf_free(resp);
}
static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_connect_work *cwork = work->ctx;
struct wpa_supplicant *wpa_s = work->wpa_s;
wpa_s->roam_in_progress = false;
if (deinit) {
if (work->started)
wpa_s->connect_work = NULL;
wpas_connect_work_free(cwork);
return;
}
wpa_s->connect_work = work;
if (cwork->bss_removed ||
!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
wpas_network_disabled(wpa_s, cwork->ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
}
/* Starting new connection, 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;
sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
}
void sme_authenticate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
if (bss == NULL || ssid == NULL)
return;
if (wpa_s->connect_work) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
return;
}
if (wpa_s->roam_in_progress) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Reject sme_authenticate() in favor of explicit roam request");
return;
}
if (radio_work_pending(wpa_s, "sme-connect")) {
/*
* The previous sme-connect work might no longer be valid due to
* the fact that the BSS list was updated. In addition, it makes
* sense to adhere to the 'newer' decision.
*/
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Remove previous pending sme-connect");
radio_remove_works(wpa_s, "sme-connect", 0);
}
wpas_abort_ongoing_scan(wpa_s);
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
cwork->bss = bss;
cwork->ssid = ssid;
cwork->sme = 1;
#ifdef CONFIG_SAE
wpa_s->sme.sae.state = SAE_NOTHING;
wpa_s->sme.sae.send_confirm = 0;
wpa_s->sme.sae_group_index = 0;
#endif /* CONFIG_SAE */
if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
sme_auth_start_cb, cwork) < 0)
wpas_connect_work_free(cwork);
}
#ifdef CONFIG_SAE
static int sme_external_auth_build_buf(struct wpabuf *buf,
struct wpabuf *params,
const u8 *sa, const u8 *da,
u16 auth_transaction, u16 seq_num,
u16 status_code)
{
struct ieee80211_mgmt *resp;
resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.auth.variable));
resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_AUTH << 4));
os_memcpy(resp->da, da, ETH_ALEN);
os_memcpy(resp->sa, sa, ETH_ALEN);
os_memcpy(resp->bssid, da, ETH_ALEN);
resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE);
resp->seq_ctrl = host_to_le16(seq_num << 4);
resp->u.auth.auth_transaction = host_to_le16(auth_transaction);
resp->u.auth.status_code = host_to_le16(status_code);
if (params)
wpabuf_put_buf(buf, params);
return 0;
}
static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
const u8 *bssid,
struct wpa_ssid *ssid)
{
struct wpabuf *resp, *buf;
int use_pt;
bool use_pk;
u16 status;
resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
&use_pk);
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
return -1;
}
wpa_s->sme.sae.state = SAE_COMMITTED;
buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
if (!buf) {
wpabuf_free(resp);
return -1;
}
wpa_s->sme.seq_num++;
if (use_pk)
status = WLAN_STATUS_SAE_PK;
else if (use_pt)
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
else
status = WLAN_STATUS_SUCCESS;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
bssid, 1, wpa_s->sme.seq_num, status);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
wpabuf_free(resp);
wpabuf_free(buf);
return 0;
}
static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
u16 status)
{
struct external_auth params;
os_memset(&params, 0, sizeof(params));
params.status = status;
params.ssid = wpa_s->sme.ext_auth_ssid;
params.ssid_len = wpa_s->sme.ext_auth_ssid_len;
params.bssid = wpa_s->sme.ext_auth_bssid;
if (wpa_s->conf->sae_pmkid_in_assoc && status == WLAN_STATUS_SUCCESS)
params.pmkid = wpa_s->sme.sae.pmkid;
wpa_drv_send_external_auth_status(wpa_s, &params);
}
static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_ssid *ssid;
size_t ssid_str_len = data->external_auth.ssid_len;
const u8 *ssid_str = data->external_auth.ssid;
/* Get the SSID conf from the ssid string obtained */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (!wpas_network_disabled(wpa_s, ssid) &&
ssid_str_len == ssid->ssid_len &&
os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
(ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
break;
}
if (!ssid ||
sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
ssid) < 0)
return -1;
return 0;
}
static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
const u8 *da)
{
struct wpabuf *resp, *buf;
resp = sme_auth_build_sae_confirm(wpa_s, 1);
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
return;
}
wpa_s->sme.sae.state = SAE_CONFIRMED;
buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
if (!buf) {
wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
wpabuf_free(resp);
return;
}
wpa_s->sme.seq_num++;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
da, 2, wpa_s->sme.seq_num,
WLAN_STATUS_SUCCESS);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
wpabuf_free(resp);
wpabuf_free(buf);
}
void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
RSN_AUTH_KEY_MGMT_SAE)
return;
if (data->external_auth.action == EXT_AUTH_START) {
if (!data->external_auth.bssid || !data->external_auth.ssid)
return;
os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid,
ETH_ALEN);
os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid,
data->external_auth.ssid_len);
wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
wpa_s->sme.seq_num = 0;
wpa_s->sme.sae.state = SAE_NOTHING;
wpa_s->sme.sae.send_confirm = 0;
wpa_s->sme.sae_group_index = 0;
if (sme_handle_external_auth_start(wpa_s, data) < 0)
sme_send_external_auth_status(wpa_s,
WLAN_STATUS_UNSPECIFIED_FAILURE);
} else if (data->external_auth.action == EXT_AUTH_ABORT) {
/* Report failure to driver for the wrong trigger */
sme_send_external_auth_status(wpa_s,
WLAN_STATUS_UNSPECIFIED_FAILURE);
}
}
static int sme_sae_is_group_enabled(struct wpa_supplicant *wpa_s, int group)
{
int *groups = wpa_s->conf->sae_groups;
int default_groups[] = { 19, 20, 21, 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 sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s,
const struct wpabuf *groups)
{
size_t i, count;
const u8 *pos;
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 = sme_sae_is_group_enabled(wpa_s, group);
wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
group, enabled ? "enabled" : "disabled");
if (enabled)
return 1;
}
return 0;
}
static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
u16 status_code, const u8 *data, size_t len,
int external, const u8 *sa)
{
int *groups;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
"status code %u", auth_transaction, status_code);
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
wpa_s->sme.sae.state == SAE_COMMITTED &&
(external || wpa_s->current_bss) && wpa_s->current_ssid) {
int default_groups[] = { 19, 20, 21, 0 };
u16 group;
const u8 *token_pos;
size_t token_len;
int h2e = 0;
groups = wpa_s->conf->sae_groups;
if (!groups || groups[0] <= 0)
groups = default_groups;
wpa_hexdump(MSG_DEBUG, "SME: SAE anti-clogging token request",
data, len);
if (len < sizeof(le16)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Too short SAE anti-clogging token request");
return -1;
}
group = WPA_GET_LE16(data);
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: SAE anti-clogging token requested (group %u)",
group);
if (sae_group_allowed(&wpa_s->sme.sae, groups, group) !=
WLAN_STATUS_SUCCESS) {
wpa_dbg(wpa_s, MSG_ERROR,
"SME: SAE group %u of anti-clogging request is invalid",
group);
return -1;
}
wpabuf_free(wpa_s->sme.sae_token);
token_pos = data + sizeof(le16);
token_len = len - sizeof(le16);
h2e = wpa_s->sme.sae.h2e;
if (h2e) {
if (token_len < 3) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Too short SAE anti-clogging token container");
return -1;
}
if (token_pos[0] != WLAN_EID_EXTENSION ||
token_pos[1] == 0 ||
token_pos[1] > token_len - 2 ||
token_pos[2] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Invalid SAE anti-clogging token container header");
return -1;
}
token_len = token_pos[1] - 1;
token_pos += 3;
}
wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
wpa_s->sme.sae_token);
if (!external)
sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 2);
else
sme_external_auth_send_sae_commit(
wpa_s, wpa_s->sme.ext_auth_bssid,
wpa_s->current_ssid);
return 0;
}
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
wpa_s->sme.sae.state == SAE_COMMITTED &&
(external || wpa_s->current_bss) && wpa_s->current_ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
wpa_s->sme.sae.group);
wpa_s->sme.sae_group_index++;
if (sme_set_sae_group(wpa_s) < 0)
return -1; /* no other groups enabled */
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
if (!external)
sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 1);
else
sme_external_auth_send_sae_commit(
wpa_s, wpa_s->sme.ext_auth_bssid,
wpa_s->current_ssid);
return 0;
}
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
const u8 *bssid = sa ? sa : wpa_s->pending_bssid;
wpa_msg(wpa_s, MSG_INFO,
WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR,
MAC2STR(bssid));
return -1;
}
if (status_code != WLAN_STATUS_SUCCESS &&
status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT &&
status_code != WLAN_STATUS_SAE_PK)
return -1;
if (auth_transaction == 1) {
u16 res;
groups = wpa_s->conf->sae_groups;
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
if ((!external && wpa_s->current_bss == NULL) ||
wpa_s->current_ssid == NULL)
return -1;
if (wpa_s->sme.sae.state != SAE_COMMITTED) {
wpa_printf(MSG_DEBUG,
"SAE: Ignore commit message while waiting for confirm");
return 0;
}
if (wpa_s->sme.sae.h2e && status_code == WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"SAE: Unexpected use of status code 0 in SAE commit when H2E was expected");
return -1;
}
if ((!wpa_s->sme.sae.h2e || wpa_s->sme.sae.pk) &&
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
wpa_printf(MSG_DEBUG,
"SAE: Unexpected use of status code for H2E in SAE commit when H2E was not expected");
return -1;
}
if (!wpa_s->sme.sae.pk &&
status_code == WLAN_STATUS_SAE_PK) {
wpa_printf(MSG_DEBUG,
"SAE: Unexpected use of status code for PK in SAE commit when PK was not expected");
return -1;
}
if (groups && groups[0] <= 0)
groups = NULL;
res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK);
if (res == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message due to reflection attack");
return 0;
}
if (res != WLAN_STATUS_SUCCESS)
return -1;
if (wpa_s->sme.sae.tmp &&
sme_check_sae_rejected_groups(
wpa_s,
wpa_s->sme.sae.tmp->peer_rejected_groups))
return -1;
if (sae_process_commit(&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
"commit");
return -1;
}
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL;
if (!external)
sme_send_authentication(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, 0);
else
sme_external_auth_send_sae_confirm(wpa_s, sa);
return 0;
} else if (auth_transaction == 2) {
if (status_code != WLAN_STATUS_SUCCESS)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
if (wpa_s->sme.sae.state != SAE_CONFIRMED)
return -1;
if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
return -1;
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
if (external) {
/* Report success to driver */
sme_send_external_auth_status(wpa_s,
WLAN_STATUS_SUCCESS);
}
return 1;
}
return -1;
}
static int sme_sae_set_pmk(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
wpa_printf(MSG_DEBUG,
"SME: SAE completed - setting PMK for 4-way handshake");
wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
wpa_s->sme.sae.pmkid, bssid);
if (wpa_s->conf->sae_pmkid_in_assoc) {
/* Update the own RSNE contents now that we have set the PMK
* and added a PMKSA cache entry based on the successfully
* completed SAE exchange. In practice, this will add the PMKID
* into RSNE. */
if (wpa_s->sme.assoc_req_ie_len + 2 + PMKID_LEN >
sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_msg(wpa_s, MSG_WARNING,
"RSN: Not enough room for inserting own PMKID into RSNE");
return -1;
}
if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
wpa_s->sme.sae.pmkid) < 0)
return -1;
wpa_hexdump(MSG_DEBUG,
"SME: Updated Association Request IEs",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
}
return 0;
}
void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
const u8 *auth_frame, size_t len)
{
const struct ieee80211_mgmt *header;
size_t auth_length;
header = (const struct ieee80211_mgmt *) auth_frame;
auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
if (len < auth_length) {
/* Notify failure to the driver */
sme_send_external_auth_status(wpa_s,
WLAN_STATUS_UNSPECIFIED_FAILURE);
return;
}
if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
int res;
res = sme_sae_auth(
wpa_s, le_to_host16(header->u.auth.auth_transaction),
le_to_host16(header->u.auth.status_code),
header->u.auth.variable,
len - auth_length, 1, header->sa);
if (res < 0) {
/* Notify failure to the driver */
sme_send_external_auth_status(
wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
return;
}
if (res != 1)
return;
if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0)
return;
}
}
#endif /* CONFIG_SAE */
void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
"when network is not selected");
return;
}
if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
"when not in authenticating state");
return;
}
if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
"unexpected peer " MACSTR,
MAC2STR(data->auth.peer));
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
" auth_type=%d auth_transaction=%d status_code=%d",
MAC2STR(data->auth.peer), data->auth.auth_type,
data->auth.auth_transaction, data->auth.status_code);
wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
data->auth.ies, data->auth.ies_len);
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
#ifdef CONFIG_SAE
if (data->auth.auth_type == WLAN_AUTH_SAE) {
int res;
res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
data->auth.status_code, data->auth.ies,
data->auth.ies_len, 0, NULL);
if (res < 0) {
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
}
if (res != 1)
return;
if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
return;
}
#endif /* CONFIG_SAE */
if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
char *ie_txt = NULL;
if (data->auth.ies && data->auth.ies_len) {
size_t buflen = 2 * data->auth.ies_len + 1;
ie_txt = os_malloc(buflen);
if (ie_txt) {
wpa_snprintf_hex(ie_txt, buflen, data->auth.ies,
data->auth.ies_len);
}
}
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
" auth_type=%u auth_transaction=%u status_code=%u%s%s",
MAC2STR(data->auth.peer), data->auth.auth_type,
data->auth.auth_transaction, data->auth.status_code,
ie_txt ? " ie=" : "",
ie_txt ? ie_txt : "");
os_free(ie_txt);
#ifdef CONFIG_FILS
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS)
fils_connection_failure(wpa_s);
#endif /* CONFIG_FILS */
if (data->auth.status_code !=
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
wpa_s->sme.auth_alg == data->auth.auth_type ||
wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
wpas_connect_work_done(wpa_s);
switch (data->auth.auth_type) {
case WLAN_AUTH_OPEN:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
case WLAN_AUTH_SHARED_KEY:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
default:
return;
}
}
#ifdef CONFIG_IEEE80211R
if (data->auth.auth_type == WLAN_AUTH_FT) {
const u8 *ric_ies = NULL;
size_t ric_ies_len = 0;
if (wpa_s->ric_ies) {
ric_ies = wpabuf_head(wpa_s->ric_ies);
ric_ies_len = wpabuf_len(wpa_s->ric_ies);
}
if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
data->auth.ies_len, 0,
data->auth.peer,
ric_ies, ric_ies_len) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: FT Authentication response processing failed");
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
MACSTR
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_FILS
if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
u16 expect_auth_type;
expect_auth_type = wpa_s->sme.auth_alg ==
WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
WLAN_AUTH_FILS_SK;
if (data->auth.auth_type != expect_auth_type) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: FILS Authentication response used different auth alg (%u; expected %u)",
data->auth.auth_type, expect_auth_type);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
MACSTR
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
data->auth.ies, data->auth.ies_len) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: FILS Authentication response processing failed");
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
MACSTR
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
}
#endif /* CONFIG_FILS */
sme_associate(wpa_s, ssid->mode, data->auth.peer,
data->auth.auth_type);
}
#ifdef CONFIG_IEEE80211R
static void remove_ie(u8 *buf, size_t *len, u8 eid)
{
u8 *pos, *next, *end;
pos = (u8 *) get_ie(buf, *len, eid);
if (pos) {
next = pos + 2 + pos[1];
end = buf + *len;
*len -= 2 + pos[1];
os_memmove(pos, next, end - next);
}
}
#endif /* CONFIG_IEEE80211R */
void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
const u8 *bssid, u16 auth_type)
{
struct wpa_driver_associate_params params;
struct ieee802_11_elems elems;
struct wpa_ssid *ssid = wpa_s->current_ssid;
#ifdef CONFIG_FILS
u8 nonces[2 * FILS_NONCE_LEN];
#endif /* CONFIG_FILS */
#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 */
os_memset(&params, 0, sizeof(params));
#ifdef CONFIG_FILS
if (auth_type == WLAN_AUTH_FILS_SK ||
auth_type == WLAN_AUTH_FILS_SK_PFS) {
struct wpabuf *buf;
const u8 *snonce, *anonce;
const unsigned int max_hlp = 20;
struct wpabuf *hlp[max_hlp];
unsigned int i, num_hlp = 0;
struct fils_hlp_req *req;
dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
list) {
hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 +
wpabuf_len(req->pkt));
if (!hlp[num_hlp])
break;
wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN);
wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr,
ETH_ALEN);
wpabuf_put_data(hlp[num_hlp],
"\xaa\xaa\x03\x00\x00\x00", 6);
wpabuf_put_buf(hlp[num_hlp], req->pkt);
num_hlp++;
if (num_hlp >= max_hlp)
break;
}
buf = fils_build_assoc_req(wpa_s->wpa, &params.fils_kek,
&params.fils_kek_len, &snonce,
&anonce,
(const struct wpabuf **) hlp,
num_hlp);
for (i = 0; i < num_hlp; i++)
wpabuf_free(hlp[i]);
if (!buf)
return;
wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
/* Remove RSNE and MDE to allow them to be overridden
* with FILS+FT specific values from
* fils_build_assoc_req(). */
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_RSN);
wpa_hexdump(MSG_DEBUG,
"FILS: assoc_req after RSNE removal",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_MOBILITY_DOMAIN);
wpa_hexdump(MSG_DEBUG,
"FILS: assoc_req after MDE removal",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
}
#endif /* CONFIG_IEEE80211R */
/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_printf(MSG_ERROR,
"FILS: Not enough buffer room for own AssocReq elements");
wpabuf_free(buf);
return;
}
os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
wpabuf_head(buf), wpabuf_len(buf));
wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
wpabuf_free(buf);
wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
os_memcpy(nonces, snonce, FILS_NONCE_LEN);
os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
params.fils_nonces = nonces;
params.fils_nonces_len = sizeof(nonces);
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
#ifdef CONFIG_TESTING_OPTIONS
if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len,
WLAN_EID_EXT_OWE_DH_PARAM)) {
wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (auth_type == WLAN_AUTH_OPEN &&
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
struct wpabuf *owe_ie;
u16 group;
if (ssid && 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) {
wpa_printf(MSG_ERROR,
"OWE: Failed to build IE for Association Request frame");
return;
}
if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) >
sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_printf(MSG_ERROR,
"OWE: Not enough buffer room for own Association Request frame elements");
wpabuf_free(owe_ie);
return;
}
os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
wpabuf_head(owe_ie), wpabuf_len(owe_ie));
wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie);
wpabuf_free(owe_ie);
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && ssid &&
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 (wpa_s->sme.assoc_req_ie_len +
wpabuf_len(wpa_s->dpp_pfs->ie) >
sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_printf(MSG_ERROR,
"DPP: Not enough buffer room for own Association Request frame elements");
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = NULL;
goto pfs_fail;
}
os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
wpabuf_head(wpa_s->dpp_pfs->ie),
wpabuf_len(wpa_s->dpp_pfs->ie));
wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie);
}
pfs_fail:
#endif /* CONFIG_DPP2 */
wpa_s->mscs_setup_done = false;
if (wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS) &&
wpa_s->robust_av.valid_config) {
struct wpabuf *mscs_ie;
size_t mscs_ie_len, buf_len, *wpa_ie_len, max_ie_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;
}
wpa_ie_len = &wpa_s->sme.assoc_req_ie_len;
max_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
if ((*wpa_ie_len + wpabuf_len(mscs_ie)) <= max_ie_len) {
wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
mscs_ie_len = wpabuf_len(mscs_ie);
os_memcpy(wpa_s->sme.assoc_req_ie + *wpa_ie_len,
wpabuf_head(mscs_ie), mscs_ie_len);
*wpa_ie_len += mscs_ie_len;
}
wpabuf_free(mscs_ie);
}
mscs_fail:
if (ssid && ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
multi_ap_ie_len = add_multi_ap_ie(
wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len,
MULTI_AP_BACKHAUL_STA);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
return;
}
wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len;
}
params.bssid = bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
params.freq.freq = wpa_s->sme.freq;
params.bg_scan_period = ssid ? ssid->bg_scan_period : -1;
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs",
params.wpa_ie, params.wpa_ie_len);
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;
#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_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
WLAN_EID_RIC_DATA)) {
/* There seems to be a pretty inconvenient bug in the Linux
* kernel IE splitting functionality when RIC is used. For now,
* skip correct behavior in IE construction here (i.e., drop the
* additional non-FT-specific IEs) to avoid kernel issues. This
* is fine since RIC is used only for testing purposes in the
* current implementation. */
wpa_printf(MSG_INFO,
"SME: Linux kernel workaround - do not try to include additional IEs with RIC");
params.wpa_ie = wpa_s->sme.ft_ies;
params.wpa_ie_len = wpa_s->sme.ft_ies_len;
} else if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
const u8 *rm_en, *pos, *end;
size_t rm_en_len = 0;
u8 *rm_en_dup = NULL, *wpos;
/* Remove RSNE, MDE, FTE to allow them to be overridden with
* FT specific values */
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_RSN);
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_MOBILITY_DOMAIN);
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_FAST_BSS_TRANSITION);
rm_en = get_ie(wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len,
WLAN_EID_RRM_ENABLED_CAPABILITIES);
if (rm_en) {
/* Need to remove RM Enabled Capabilities element as
* well temporarily, so that it can be placed between
* RSNE and MDE. */
rm_en_len = 2 + rm_en[1];
rm_en_dup = os_memdup(rm_en, rm_en_len);
remove_ie(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
WLAN_EID_RRM_ENABLED_CAPABILITIES);
}
wpa_hexdump(MSG_DEBUG,
"SME: Association Request IEs after FT IE removal",
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
if (wpa_s->sme.assoc_req_ie_len + wpa_s->sme.ft_ies_len +
rm_en_len > sizeof(wpa_s->sme.assoc_req_ie)) {
wpa_printf(MSG_ERROR,
"SME: Not enough buffer room for FT IEs in Association Request frame");
os_free(rm_en_dup);
return;
}
os_memmove(wpa_s->sme.assoc_req_ie + wpa_s->sme.ft_ies_len +
rm_en_len,
wpa_s->sme.assoc_req_ie,
wpa_s->sme.assoc_req_ie_len);
pos = wpa_s->sme.ft_ies;
end = pos + wpa_s->sme.ft_ies_len;
wpos = wpa_s->sme.assoc_req_ie;
if (*pos == WLAN_EID_RSN) {
os_memcpy(wpos, pos, 2 + pos[1]);
wpos += 2 + pos[1];
pos += 2 + pos[1];
}
if (rm_en_dup) {
os_memcpy(wpos, rm_en_dup, rm_en_len);
wpos += rm_en_len;
os_free(rm_en_dup);
}
os_memcpy(wpos, pos, end - pos);
wpa_s->sme.assoc_req_ie_len += wpa_s->sme.ft_ies_len +
rm_en_len;
params.wpa_ie = wpa_s->sme.assoc_req_ie;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
wpa_hexdump(MSG_DEBUG,
"SME: Association Request IEs after FT override",
params.wpa_ie, params.wpa_ie_len);
}
#endif /* CONFIG_IEEE80211R */
params.mode = mode;
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_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
params.freq.freq);
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
if (params.wpa_ie == NULL ||
ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
< 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
os_memset(&elems, 0, sizeof(elems));
}
if (elems.rsn_ie) {
params.wpa_proto = WPA_PROTO_RSN;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
elems.rsn_ie_len + 2);
} else if (elems.wpa_ie) {
params.wpa_proto = WPA_PROTO_WPA;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
elems.wpa_ie_len + 2);
} else if (elems.osen) {
params.wpa_proto = WPA_PROTO_OSEN;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
elems.osen_len + 2);
} else
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
if (elems.rsnxe)
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, elems.rsnxe - 2,
elems.rsnxe_len + 2);
else
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
if (ssid && ssid->p2p_group)
params.p2p = 1;
if (wpa_s->p2pdev->set_sta_uapsd)
params.uapsd = wpa_s->p2pdev->sta_uapsd;
else
params.uapsd = -1;
if (wpa_drv_associate(wpa_s, &params) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
"driver failed");
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;
}
eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
NULL);
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
wpa_s->last_assoc_req_wpa_ie = NULL;
if (params.wpa_ie)
wpa_s->last_assoc_req_wpa_ie =
wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len);
#endif /* CONFIG_TESTING_OPTIONS */
}
int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
const u8 *ies, size_t ies_len)
{
if (md == NULL || ies == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain");
os_free(wpa_s->sme.ft_ies);
wpa_s->sme.ft_ies = NULL;
wpa_s->sme.ft_ies_len = 0;
wpa_s->sme.ft_used = 0;
return 0;
}
os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
os_free(wpa_s->sme.ft_ies);
wpa_s->sme.ft_ies = os_memdup(ies, ies_len);
if (wpa_s->sme.ft_ies == NULL)
return -1;
wpa_s->sme.ft_ies_len = ies_len;
return 0;
}
static void sme_deauth(struct wpa_supplicant *wpa_s)
{
int bssid_changed;
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
WLAN_REASON_DEAUTH_LEAVING) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
"failed");
}
wpa_s->sme.prev_bssid_set = 0;
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
}
void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
"status code %d", MAC2STR(wpa_s->pending_bssid),
data->assoc_reject.status_code);
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
#ifdef CONFIG_SAE
if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication");
wpa_sm_aborted_cached(wpa_s->wpa);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
if (wpa_s->current_bss) {
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
WLAN_REASON_DEAUTH_LEAVING);
wpas_connect_work_done(wpa_s);
wpa_supplicant_mark_disassoc(wpa_s);
wpa_supplicant_connect(wpa_s, bss, ssid);
return;
}
}
#endif /* CONFIG_SAE */
/*
* For now, unconditionally terminate the previous authentication. In
* theory, this should not be needed, but mac80211 gets quite confused
* if the authentication is left pending.. Some roaming cases might
* benefit from using the previous authentication, so this could be
* optimized in the future.
*/
sme_deauth(wpa_s);
}
void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
}
void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
}
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
struct disassoc_info *info)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
if (wpa_s->sme.prev_bssid_set) {
/*
* cfg80211/mac80211 can get into somewhat confused state if
* the AP only disassociates us and leaves us in authenticated
* state. For now, force the state to be cleared to avoid
* confusing errors if we try to associate with the AP again.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear "
"driver state");
wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
WLAN_REASON_DEAUTH_LEAVING);
}
}
static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
sme_deauth(wpa_s);
}
}
static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state == WPA_ASSOCIATING) {
wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
sme_deauth(wpa_s);
}
}
void sme_state_changed(struct wpa_supplicant *wpa_s)
{
/* Make sure timers are cleaned up appropriately. */
if (wpa_s->wpa_state != WPA_ASSOCIATING)
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
if (wpa_s->wpa_state != WPA_AUTHENTICATING)
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
}
void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
const u8 *prev_pending_bssid)
{
/*
* mac80211-workaround to force deauth on failed auth cmd,
* requires us to remain in authenticating state to allow the
* second authentication attempt to be continued properly.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
"to proceed after disconnection event");
wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
/*
* Re-arm authentication timer in case auth fails for whatever reason.
*/
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
NULL);
}
void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
{
wpa_s->sme.prev_bssid_set = 0;
#ifdef CONFIG_SAE
wpabuf_free(wpa_s->sme.sae_token);
wpa_s->sme.sae_token = NULL;
sae_clear_data(&wpa_s->sme.sae);
#endif /* CONFIG_SAE */
#ifdef CONFIG_IEEE80211R
if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used)
sme_update_ft_ies(wpa_s, NULL, NULL, 0);
#endif /* CONFIG_IEEE80211R */
sme_stop_sa_query(wpa_s);
}
void sme_deinit(struct wpa_supplicant *wpa_s)
{
sme_clear_on_disassoc(wpa_s);
#ifdef CONFIG_SAE
os_free(wpa_s->sme.sae_rejected_groups);
wpa_s->sme.sae_rejected_groups = NULL;
#endif /* CONFIG_SAE */
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
}
static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
const u8 *chan_list, u8 num_channels,
u8 num_intol)
{
struct ieee80211_2040_bss_coex_ie *bc_ie;
struct ieee80211_2040_intol_chan_report *ic_report;
struct wpabuf *buf;
wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR
" (num_channels=%u num_intol=%u)",
MAC2STR(wpa_s->bssid), num_channels, num_intol);
wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels",
chan_list, num_channels);
buf = wpabuf_alloc(2 + /* action.category + action_code */
sizeof(struct ieee80211_2040_bss_coex_ie) +
sizeof(struct ieee80211_2040_intol_chan_report) +
num_channels);
if (buf == NULL)
return;
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
bc_ie->length = 1;
if (num_intol)
bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
if (num_channels > 0) {
ic_report = wpabuf_put(buf, sizeof(*ic_report));
ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
ic_report->length = num_channels + 1;
ic_report->op_class = 0;
os_memcpy(wpabuf_put(buf, num_channels), chan_list,
num_channels);
}
if (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) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"SME: Failed to send 20/40 BSS Coexistence frame");
}
wpabuf_free(buf);
}
int sme_proc_obss_scan(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
const u8 *ie;
u8 chan_list[P2P_MAX_CHANNELS], channel;
u8 num_channels = 0, num_intol = 0, i;
size_t j;
int pri_freq, sec_freq;
if (!wpa_s->sme.sched_obss_scan)
return 0;
wpa_s->sme.sched_obss_scan = 0;
if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
return 1;
/*
* Check whether AP uses regulatory triplet or channel triplet in
* country info. Right now the operating class of the BSS channel
* width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
* based on the assumption that operating class triplet is not used in
* beacon frame. If the First Channel Number/Operating Extension
* Identifier octet has a positive integer value of 201 or greater,
* then its operating class triplet.
*
* TODO: If Supported Operating Classes element is present in beacon
* frame, have to lookup operating class in Annex E and fill them in
* 2040 coex frame.
*/
ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
if (ie && (ie[1] >= 6) && (ie[5] >= 201))
return 1;
os_memset(chan_list, 0, sizeof(chan_list));
pri_freq = wpa_s->assoc_freq;
switch (wpa_s->sme.ht_sec_chan) {
case HT_SEC_CHAN_ABOVE:
sec_freq = pri_freq + 20;
break;
case HT_SEC_CHAN_BELOW:
sec_freq = pri_freq - 20;
break;
case HT_SEC_CHAN_UNKNOWN:
default:
wpa_msg(wpa_s, MSG_WARNING,
"Undefined secondary channel: drop OBSS scan results");
return 1;
}
for (j = 0; j < scan_res->num; j++) {
struct wpa_scan_res *bss = scan_res->res[j];
enum hostapd_hw_mode mode;
int res;
/* Skip other band bss */
mode = ieee80211_freq_to_chan(bss->freq, &channel);
if (mode != HOSTAPD_MODE_IEEE80211G &&
mode != HOSTAPD_MODE_IEEE80211B)
continue;
res = check_bss_coex_40mhz(bss, pri_freq, sec_freq);
if (res) {
if (res == 2)
num_intol++;
/* Check whether the channel is already considered */
for (i = 0; i < num_channels; i++) {
if (channel == chan_list[i])
break;
}
if (i != num_channels)
continue;
chan_list[num_channels++] = channel;
}
}
sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
return 1;
}
static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
/* Include only affected channels */
struct hostapd_hw_modes *mode;
int count, i;
int start, end;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
HOSTAPD_MODE_IEEE80211G, false);
if (mode == NULL) {
/* No channels supported in this band - use empty list */
params->freqs = os_zalloc(sizeof(int));
return;
}
if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN &&
wpa_s->current_bss) {
const u8 *ie;
ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION);
if (ie && ie[1] >= 2) {
u8 o;
o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE;
else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW;
}
}
start = wpa_s->assoc_freq - 10;
end = wpa_s->assoc_freq + 10;
switch (wpa_s->sme.ht_sec_chan) {
case HT_SEC_CHAN_UNKNOWN:
/* HT40+ possible on channels 1..9 */
if (wpa_s->assoc_freq <= 2452)
start -= 20;
/* HT40- possible on channels 5-13 */
if (wpa_s->assoc_freq >= 2432)
end += 20;
break;
case HT_SEC_CHAN_ABOVE:
end += 20;
break;
case HT_SEC_CHAN_BELOW:
start -= 20;
break;
}
wpa_printf(MSG_DEBUG,
"OBSS: assoc_freq %d possible affected range %d-%d",
wpa_s->assoc_freq, start, end);
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
if (params->freqs == NULL)
return;
for (count = 0, i = 0; i < mode->num_channels; i++) {
int freq;
if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
freq = mode->channels[i].freq;
if (freq - 10 >= end || freq + 10 <= start)
continue; /* not affected */
params->freqs[count++] = freq;
}
}
static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_driver_scan_params params;
if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
return;
}
os_memset(&params, 0, sizeof(params));
wpa_obss_scan_freqs_list(wpa_s, &params);
params.low_priority = 1;
wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
if (wpa_supplicant_trigger_scan(wpa_s, &params))
wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
else
wpa_s->sme.sched_obss_scan = 1;
os_free(params.freqs);
eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
sme_obss_scan_timeout, wpa_s, NULL);
}
void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
{
const u8 *ie;
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct hostapd_hw_modes *hw_mode = NULL;
int i;
eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
wpa_s->sme.sched_obss_scan = 0;
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN;
if (!enable)
return;
/*
* Schedule OBSS scan if driver is using station SME in wpa_supplicant
* or it expects OBSS scan to be performed by wpa_supplicant.
*/
if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
ssid == NULL || ssid->mode != WPAS_MODE_INFRA)
return;
if (!wpa_s->hw.modes)
return;
/* only HT caps in 11g mode are relevant */
for (i = 0; i < wpa_s->hw.num_modes; i++) {
hw_mode = &wpa_s->hw.modes[i];
if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G)
break;
}
/* Driver does not support HT40 for 11g or doesn't have 11g. */
if (i == wpa_s->hw.num_modes || !hw_mode ||
!(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
return;
if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
return; /* Not associated on 2.4 GHz band */
/* Check whether AP supports HT40 */
ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
if (!ie || ie[1] < 2 ||
!(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
return; /* AP does not support HT40 */
ie = wpa_bss_get_ie(wpa_s->current_bss,
WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
if (!ie || ie[1] < 14)
return; /* AP does not request OBSS scans */
wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
if (wpa_s->sme.obss_scan_int < 10) {
wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
"replaced with the minimum 10 sec",
wpa_s->sme.obss_scan_int);
wpa_s->sme.obss_scan_int = 10;
}
wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
wpa_s->sme.obss_scan_int);
eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
sme_obss_scan_timeout, wpa_s, NULL);
}
static const unsigned int sa_query_max_timeout = 1000;
static const unsigned int sa_query_retry_timeout = 201;
static const unsigned int sa_query_ch_switch_max_delay = 5000; /* in usec */
static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
{
u32 tu;
struct os_reltime now, passed;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
if (sa_query_max_timeout < tu) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
sme_stop_sa_query(wpa_s);
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID);
return 1;
}
return 0;
}
static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
const u8 *trans_id)
{
u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN];
u8 req_len = 2 + WLAN_SA_QUERY_TR_ID_LEN;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
MACSTR, MAC2STR(wpa_s->bssid));
wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
req[0] = WLAN_ACTION_SA_QUERY;
req[1] = WLAN_SA_QUERY_REQUEST;
os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
struct wpa_channel_info ci;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in SA Query Request frame");
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->oci_freq_override_saquery_req) {
wpa_printf(MSG_INFO,
"TEST: Override SA Query Request OCI frequency %d -> %d MHz",
ci.frequency,
wpa_s->oci_freq_override_saquery_req);
ci.frequency = wpa_s->oci_freq_override_saquery_req;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (ocv_insert_extended_oci(&ci, req + req_len) < 0)
return;
req_len += OCV_OCI_EXTENDED_LEN;
}
#endif /* CONFIG_OCV */
if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
req, req_len, 0) < 0)
wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
"Request");
}
static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
unsigned int timeout, sec, usec;
u8 *trans_id, *nbuf;
if (wpa_s->sme.sa_query_count > 0 &&
sme_check_sa_query_timeout(wpa_s))
return;
nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
wpa_s->sme.sa_query_count + 1,
WLAN_SA_QUERY_TR_ID_LEN);
if (nbuf == NULL) {
sme_stop_sa_query(wpa_s);
return;
}
if (wpa_s->sme.sa_query_count == 0) {
/* Starting a new SA Query procedure */
os_get_reltime(&wpa_s->sme.sa_query_start);
}
trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
wpa_s->sme.sa_query_trans_id = nbuf;
wpa_s->sme.sa_query_count++;
if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
sme_stop_sa_query(wpa_s);
return;
}
timeout = sa_query_retry_timeout;
sec = ((timeout / 1000) * 1024) / 1000;
usec = (timeout % 1000) * 1024;
eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL);
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d",
wpa_s->sme.sa_query_count);
sme_send_sa_query_req(wpa_s, trans_id);
}
static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
{
sme_sa_query_timer(wpa_s, NULL);
}
static void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
{
if (wpa_s->sme.sa_query_trans_id)
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Stop SA Query");
eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
os_free(wpa_s->sme.sa_query_trans_id);
wpa_s->sme.sa_query_trans_id = NULL;
wpa_s->sme.sa_query_count = 0;
}
void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
const u8 *da, u16 reason_code)
{
struct wpa_ssid *ssid;
struct os_reltime now;
if (wpa_s->wpa_state != WPA_COMPLETED)
return;
ssid = wpa_s->current_ssid;
if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
return;
if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
return;
if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
return;
if (wpa_s->sme.sa_query_count > 0)
return;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->disable_sa_query)
return;
#endif /* CONFIG_TESTING_OPTIONS */
os_get_reltime(&now);
if (wpa_s->sme.last_unprot_disconnect.sec &&
!os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
return; /* limit SA Query procedure frequency */
wpa_s->sme.last_unprot_disconnect = now;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
"possible AP/STA state mismatch - trigger SA Query");
sme_start_sa_query(wpa_s);
}
void sme_event_ch_switch(struct wpa_supplicant *wpa_s)
{
unsigned int usec;
u32 _rand;
if (wpa_s->wpa_state != WPA_COMPLETED ||
!wpa_sm_ocv_enabled(wpa_s->wpa))
return;
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Channel switch completed - trigger new SA Query to verify new operating channel");
sme_stop_sa_query(wpa_s);
if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
_rand = os_random();
usec = _rand % (sa_query_ch_switch_max_delay + 1);
eloop_register_timeout(0, usec, sme_sa_query_timer, wpa_s, NULL);
}
static void sme_process_sa_query_request(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *data,
size_t len)
{
u8 resp[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN];
u8 resp_len = 2 + WLAN_SA_QUERY_TR_ID_LEN;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Response to "
MACSTR, MAC2STR(wpa_s->bssid));
resp[0] = WLAN_ACTION_SA_QUERY;
resp[1] = WLAN_SA_QUERY_RESPONSE;
os_memcpy(resp + 2, data + 1, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
struct wpa_channel_info ci;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in SA Query Response frame");
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->oci_freq_override_saquery_resp) {
wpa_printf(MSG_INFO,
"TEST: Override SA Query Response OCI frequency %d -> %d MHz",
ci.frequency,
wpa_s->oci_freq_override_saquery_resp);
ci.frequency = wpa_s->oci_freq_override_saquery_resp;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0)
return;
resp_len += OCV_OCI_EXTENDED_LEN;
}
#endif /* CONFIG_OCV */
if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
resp, resp_len, 0) < 0)
wpa_msg(wpa_s, MSG_INFO,
"SME: Failed to send SA Query Response");
}
static void sme_process_sa_query_response(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *data,
size_t len)
{
int i;
if (!wpa_s->sme.sa_query_trans_id)
return;
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
return;
for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
if (os_memcmp(wpa_s->sme.sa_query_trans_id +
i * WLAN_SA_QUERY_TR_ID_LEN,
data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0)
break;
}
if (i >= wpa_s->sme.sa_query_count) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query "
"transaction identifier found");
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received "
"from " MACSTR, MAC2STR(sa));
sme_stop_sa_query(wpa_s);
}
void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa,
const u8 *data, size_t len)
{
if (len < 1 + WLAN_SA_QUERY_TR_ID_LEN)
return;
if (is_multicast_ether_addr(da)) {
wpa_printf(MSG_DEBUG,
"IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")",
MAC2STR(da), MAC2STR(sa));
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query frame from "
MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(wpa_s->wpa)) {
struct ieee802_11_elems elems;
struct wpa_channel_info ci;
if (ieee802_11_parse_elems(data + 1 + WLAN_SA_QUERY_TR_ID_LEN,
len - 1 - WLAN_SA_QUERY_TR_ID_LEN,
&elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"SA Query: Failed to parse elements");
return;
}
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in SA Query Action frame");
return;
}
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != OCI_SUCCESS) {
wpa_msg(wpa_s, MSG_INFO, OCV_FAILURE "addr=" MACSTR
" frame=saquery%s error=%s",
MAC2STR(sa), data[0] == WLAN_SA_QUERY_REQUEST ?
"req" : "resp", ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (data[0] == WLAN_SA_QUERY_REQUEST)
sme_process_sa_query_request(wpa_s, sa, data, len);
else if (data[0] == WLAN_SA_QUERY_RESPONSE)
sme_process_sa_query_response(wpa_s, sa, data, len);
}
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 90e8a466aba2..835b33575760 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,8368 +1,8366 @@
/*
* 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;
}
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) {
eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
dl_list_del(&bss->list);
os_free(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 */
}
/**
* 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_s->ctrl_iface) {
- wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+ 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);
}
/**
* 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));
#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)
{
*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 */
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 */
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 */
}
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;
}
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 };
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;
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;
#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 */
vht_freq.vht_enabled = vht_supported(mode);
if (!vht_freq.vht_enabled)
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)
break;
}
if (j == ARRAY_SIZE(vht80))
return;
for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
struct hostapd_channel_data *chan;
chan = hw_get_channel_chan(mode, i, NULL);
if (!chan)
return;
/* Back to HT configuration if channel not usable */
if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
return;
}
chwidth = CHANWIDTH_80MHZ;
seg0 = vht80[j] + 6;
seg1 = 0;
if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) {
/* setup center_freq2, bandwidth */
for (k = 0; k < ARRAY_SIZE(vht80); k++) {
/* Only accept 80 MHz segments separated by a gap */
if (j == k || abs(vht80[j] - vht80[k]) == 16)
continue;
for (i = vht80[k]; i < vht80[k] + 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 (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;
#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 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;
}
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;
}
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:
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) {
/*
* 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);
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);
}
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
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 */
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);
- if (wpa_s->ctrl_iface) {
- wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
- wpa_s->ctrl_iface = NULL;
- }
+ 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;
#ifdef CONFIG_DRIVER_NDIS
{
void driver_ndis_init_ops(void);
driver_ndis_init_ops();
}
#endif /* CONFIG_DRIVER_NDIS */
#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_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 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;
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;
}
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) {
dl_list_del(&tmp->list);
os_free(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)
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/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7ed8c0ee4d92..49007cfc2e8f 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1,1742 +1,1747 @@
/*
* 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;
};
#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 */
/**
* 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;
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 */
};
/* 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);
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);
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
};
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 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);
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_pasn_auth_start(struct wpa_supplicant *wpa_s,
const u8 *bssid, int akmp, int cipher,
- u16 group, int network_id);
+ 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 */

File Metadata

Mime Type
application/octet-stream
Expires
Wed, May 8, 7:16 PM (2 d)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
.apBSO3s9hYX
Default Alt Text
(6 MB)

Event Timeline