mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-26 17:28:29 -05:00
152cff6ba6
The option of handling upper layer P2P management operations within the driver/firmware was originally planned to be used with wpa_supplicant, but this has not really happened and there is no clear sign of this being needed in the near term either. This functionality has not been completed and it is certainly not being kept up-to-date or tested. As such, it is best to remove it for now and if this or something similar is needed in the future, it can be brought back once a clear need for it has been demonstrated first. Signed-hostap: Jouni Malinen <j@w1.fi>
2679 lines
68 KiB
C
2679 lines
68 KiB
C
/*
|
|
* Testing driver interface for a simulated network driver
|
|
* Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */
|
|
#include "build_config.h"
|
|
#ifdef CONFIG_NATIVE_WINDOWS
|
|
#include <winsock2.h>
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
#include "utils/includes.h"
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
#include <sys/un.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#define DRIVER_TEST_UNIX
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
#include "utils/common.h"
|
|
#include "utils/eloop.h"
|
|
#include "utils/list.h"
|
|
#include "utils/trace.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "crypto/sha1.h"
|
|
#include "l2_packet/l2_packet.h"
|
|
#include "wps/wps.h"
|
|
#include "driver.h"
|
|
|
|
|
|
struct test_client_socket {
|
|
struct test_client_socket *next;
|
|
u8 addr[ETH_ALEN];
|
|
struct sockaddr_un un;
|
|
socklen_t unlen;
|
|
struct test_driver_bss *bss;
|
|
};
|
|
|
|
struct test_driver_bss {
|
|
struct wpa_driver_test_data *drv;
|
|
struct dl_list list;
|
|
void *bss_ctx;
|
|
char ifname[IFNAMSIZ];
|
|
u8 bssid[ETH_ALEN];
|
|
u8 *ie;
|
|
size_t ielen;
|
|
u8 *wps_beacon_ie;
|
|
size_t wps_beacon_ie_len;
|
|
u8 *wps_probe_resp_ie;
|
|
size_t wps_probe_resp_ie_len;
|
|
u8 ssid[32];
|
|
size_t ssid_len;
|
|
int privacy;
|
|
};
|
|
|
|
struct wpa_driver_test_global {
|
|
int bss_add_used;
|
|
u8 req_addr[ETH_ALEN];
|
|
};
|
|
|
|
struct wpa_driver_test_data {
|
|
struct wpa_driver_test_global *global;
|
|
void *ctx;
|
|
WPA_TRACE_REF(ctx);
|
|
u8 own_addr[ETH_ALEN];
|
|
int test_socket;
|
|
#ifdef DRIVER_TEST_UNIX
|
|
struct sockaddr_un hostapd_addr;
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
int hostapd_addr_set;
|
|
struct sockaddr_in hostapd_addr_udp;
|
|
int hostapd_addr_udp_set;
|
|
char *own_socket_path;
|
|
char *test_dir;
|
|
#define MAX_SCAN_RESULTS 30
|
|
struct wpa_scan_res *scanres[MAX_SCAN_RESULTS];
|
|
size_t num_scanres;
|
|
int use_associnfo;
|
|
u8 assoc_wpa_ie[80];
|
|
size_t assoc_wpa_ie_len;
|
|
int associated;
|
|
u8 *probe_req_ie;
|
|
size_t probe_req_ie_len;
|
|
u8 probe_req_ssid[32];
|
|
size_t probe_req_ssid_len;
|
|
int ibss;
|
|
int ap;
|
|
|
|
struct test_client_socket *cli;
|
|
struct dl_list bss;
|
|
int udp_port;
|
|
|
|
int alloc_iface_idx;
|
|
|
|
int probe_req_report;
|
|
unsigned int remain_on_channel_freq;
|
|
unsigned int remain_on_channel_duration;
|
|
|
|
int current_freq;
|
|
};
|
|
|
|
|
|
static void wpa_driver_test_deinit(void *priv);
|
|
static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
|
|
const char *dir, int ap);
|
|
static void wpa_driver_test_close_test_socket(
|
|
struct wpa_driver_test_data *drv);
|
|
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
|
|
|
|
|
|
static void test_driver_free_bss(struct test_driver_bss *bss)
|
|
{
|
|
os_free(bss->ie);
|
|
os_free(bss->wps_beacon_ie);
|
|
os_free(bss->wps_probe_resp_ie);
|
|
os_free(bss);
|
|
}
|
|
|
|
|
|
static void test_driver_free_bsses(struct wpa_driver_test_data *drv)
|
|
{
|
|
struct test_driver_bss *bss, *tmp;
|
|
|
|
dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss,
|
|
list) {
|
|
dl_list_del(&bss->list);
|
|
test_driver_free_bss(bss);
|
|
}
|
|
}
|
|
|
|
|
|
static struct test_client_socket *
|
|
test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from,
|
|
socklen_t fromlen)
|
|
{
|
|
struct test_client_socket *cli = drv->cli;
|
|
|
|
while (cli) {
|
|
if (cli->unlen == fromlen &&
|
|
strncmp(cli->un.sun_path, from->sun_path,
|
|
fromlen - sizeof(cli->un.sun_family)) == 0)
|
|
return cli;
|
|
cli = cli->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data,
|
|
size_t data_len, int encrypt,
|
|
const u8 *own_addr, u32 flags)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_client_socket *cli;
|
|
struct msghdr msg;
|
|
struct iovec io[3];
|
|
struct l2_ethhdr eth;
|
|
|
|
if (drv->test_socket < 0)
|
|
return -1;
|
|
|
|
cli = drv->cli;
|
|
while (cli) {
|
|
if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
|
|
break;
|
|
cli = cli->next;
|
|
}
|
|
|
|
if (!cli) {
|
|
wpa_printf(MSG_DEBUG, "%s: no destination client entry",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(eth.h_dest, addr, ETH_ALEN);
|
|
memcpy(eth.h_source, own_addr, ETH_ALEN);
|
|
eth.h_proto = host_to_be16(ETH_P_EAPOL);
|
|
|
|
io[0].iov_base = "EAPOL ";
|
|
io[0].iov_len = 6;
|
|
io[1].iov_base = ð
|
|
io[1].iov_len = sizeof(eth);
|
|
io[2].iov_base = (u8 *) data;
|
|
io[2].iov_len = data_len;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = io;
|
|
msg.msg_iovlen = 3;
|
|
msg.msg_name = &cli->un;
|
|
msg.msg_namelen = cli->unlen;
|
|
return sendmsg(drv->test_socket, &msg, 0);
|
|
}
|
|
|
|
|
|
static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
|
|
u16 proto, const u8 *data, size_t data_len)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct msghdr msg;
|
|
struct iovec io[3];
|
|
struct l2_ethhdr eth;
|
|
char desttxt[30];
|
|
struct sockaddr_un addr;
|
|
struct dirent *dent;
|
|
DIR *dir;
|
|
int ret = 0, broadcast = 0, count = 0;
|
|
|
|
if (drv->test_socket < 0 || drv->test_dir == NULL) {
|
|
wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d "
|
|
"test_dir=%p)",
|
|
__func__, drv->test_socket, drv->test_dir);
|
|
return -1;
|
|
}
|
|
|
|
broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
|
|
snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst));
|
|
|
|
memcpy(eth.h_dest, dst, ETH_ALEN);
|
|
memcpy(eth.h_source, src, ETH_ALEN);
|
|
eth.h_proto = host_to_be16(proto);
|
|
|
|
io[0].iov_base = "ETHER ";
|
|
io[0].iov_len = 6;
|
|
io[1].iov_base = ð
|
|
io[1].iov_len = sizeof(eth);
|
|
io[2].iov_base = (u8 *) data;
|
|
io[2].iov_len = data_len;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = io;
|
|
msg.msg_iovlen = 3;
|
|
|
|
dir = opendir(drv->test_dir);
|
|
if (dir == NULL) {
|
|
perror("test_driver: opendir");
|
|
return -1;
|
|
}
|
|
while ((dent = readdir(dir))) {
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
/* Skip the file if it is not a socket. Also accept
|
|
* DT_UNKNOWN (0) in case the C library or underlying file
|
|
* system does not support d_type. */
|
|
if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
|
|
continue;
|
|
#endif /* _DIRENT_HAVE_D_TYPE */
|
|
if (strcmp(dent->d_name, ".") == 0 ||
|
|
strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
|
|
drv->test_dir, dent->d_name);
|
|
|
|
if (strcmp(addr.sun_path, drv->own_socket_path) == 0)
|
|
continue;
|
|
if (!broadcast && strstr(dent->d_name, desttxt) == NULL)
|
|
continue;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s",
|
|
__func__, dent->d_name);
|
|
|
|
msg.msg_name = &addr;
|
|
msg.msg_namelen = sizeof(addr);
|
|
ret = sendmsg(drv->test_socket, &msg, 0);
|
|
if (ret < 0)
|
|
perror("driver_test: sendmsg");
|
|
count++;
|
|
}
|
|
closedir(dir);
|
|
|
|
if (!broadcast && count == 0) {
|
|
wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found",
|
|
__func__, MAC2STR(dst));
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
|
|
size_t data_len, int noack)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct msghdr msg;
|
|
struct iovec io[2];
|
|
const u8 *dest;
|
|
struct sockaddr_un addr;
|
|
struct dirent *dent;
|
|
DIR *dir;
|
|
int broadcast;
|
|
int ret = 0;
|
|
struct ieee80211_hdr *hdr;
|
|
u16 fc;
|
|
char cmd[50];
|
|
int freq;
|
|
#ifdef HOSTAPD
|
|
char desttxt[30];
|
|
#endif /* HOSTAPD */
|
|
union wpa_event_data event;
|
|
|
|
wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len);
|
|
if (drv->test_socket < 0 || data_len < 10) {
|
|
wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu"
|
|
" test_dir=%p)",
|
|
__func__, drv->test_socket,
|
|
(unsigned long) data_len,
|
|
drv->test_dir);
|
|
return -1;
|
|
}
|
|
|
|
dest = data + 4;
|
|
broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
|
|
|
|
#ifdef HOSTAPD
|
|
snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest));
|
|
#endif /* HOSTAPD */
|
|
|
|
if (drv->remain_on_channel_freq)
|
|
freq = drv->remain_on_channel_freq;
|
|
else
|
|
freq = drv->current_freq;
|
|
wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz",
|
|
dbss->ifname, freq);
|
|
os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq);
|
|
io[0].iov_base = cmd;
|
|
io[0].iov_len = os_strlen(cmd);
|
|
io[1].iov_base = (void *) data;
|
|
io[1].iov_len = data_len;
|
|
|
|
os_memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = io;
|
|
msg.msg_iovlen = 2;
|
|
|
|
#ifdef HOSTAPD
|
|
if (drv->test_dir == NULL) {
|
|
wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__);
|
|
return -1;
|
|
}
|
|
|
|
dir = opendir(drv->test_dir);
|
|
if (dir == NULL) {
|
|
perror("test_driver: opendir");
|
|
return -1;
|
|
}
|
|
while ((dent = readdir(dir))) {
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
/* Skip the file if it is not a socket. Also accept
|
|
* DT_UNKNOWN (0) in case the C library or underlying file
|
|
* system does not support d_type. */
|
|
if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
|
|
continue;
|
|
#endif /* _DIRENT_HAVE_D_TYPE */
|
|
if (os_strcmp(dent->d_name, ".") == 0 ||
|
|
os_strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
|
|
os_memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
|
|
drv->test_dir, dent->d_name);
|
|
|
|
if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0)
|
|
continue;
|
|
if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL)
|
|
continue;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: Send management frame to %s",
|
|
__func__, dent->d_name);
|
|
|
|
msg.msg_name = &addr;
|
|
msg.msg_namelen = sizeof(addr);
|
|
ret = sendmsg(drv->test_socket, &msg, 0);
|
|
if (ret < 0)
|
|
perror("driver_test: sendmsg(test_socket)");
|
|
}
|
|
closedir(dir);
|
|
#else /* HOSTAPD */
|
|
|
|
if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
|
|
drv->test_dir == NULL) {
|
|
if (drv->hostapd_addr_udp_set) {
|
|
msg.msg_name = &drv->hostapd_addr_udp;
|
|
msg.msg_namelen = sizeof(drv->hostapd_addr_udp);
|
|
} else {
|
|
#ifdef DRIVER_TEST_UNIX
|
|
msg.msg_name = &drv->hostapd_addr;
|
|
msg.msg_namelen = sizeof(drv->hostapd_addr);
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
}
|
|
} else if (broadcast) {
|
|
dir = opendir(drv->test_dir);
|
|
if (dir == NULL)
|
|
return -1;
|
|
while ((dent = readdir(dir))) {
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
/* Skip the file if it is not a socket.
|
|
* Also accept DT_UNKNOWN (0) in case
|
|
* the C library or underlying file
|
|
* system does not support d_type. */
|
|
if (dent->d_type != DT_SOCK &&
|
|
dent->d_type != DT_UNKNOWN)
|
|
continue;
|
|
#endif /* _DIRENT_HAVE_D_TYPE */
|
|
if (os_strcmp(dent->d_name, ".") == 0 ||
|
|
os_strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s",
|
|
__func__, dent->d_name);
|
|
os_memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
os_snprintf(addr.sun_path, sizeof(addr.sun_path),
|
|
"%s/%s", drv->test_dir, dent->d_name);
|
|
|
|
msg.msg_name = &addr;
|
|
msg.msg_namelen = sizeof(addr);
|
|
|
|
ret = sendmsg(drv->test_socket, &msg, 0);
|
|
if (ret < 0)
|
|
perror("driver_test: sendmsg(test_socket)");
|
|
}
|
|
closedir(dir);
|
|
return ret;
|
|
} else {
|
|
struct stat st;
|
|
os_memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
os_snprintf(addr.sun_path, sizeof(addr.sun_path),
|
|
"%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest));
|
|
if (stat(addr.sun_path, &st) < 0) {
|
|
os_snprintf(addr.sun_path, sizeof(addr.sun_path),
|
|
"%s/STA-" MACSTR,
|
|
drv->test_dir, MAC2STR(dest));
|
|
}
|
|
msg.msg_name = &addr;
|
|
msg.msg_namelen = sizeof(addr);
|
|
}
|
|
|
|
if (sendmsg(drv->test_socket, &msg, 0) < 0) {
|
|
perror("sendmsg(test_socket)");
|
|
return -1;
|
|
}
|
|
#endif /* HOSTAPD */
|
|
|
|
hdr = (struct ieee80211_hdr *) data;
|
|
fc = le_to_host16(hdr->frame_control);
|
|
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.tx_status.type = WLAN_FC_GET_TYPE(fc);
|
|
event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
|
|
event.tx_status.dst = hdr->addr1;
|
|
event.tx_status.data = data;
|
|
event.tx_status.data_len = data_len;
|
|
event.tx_status.ack = ret >= 0;
|
|
wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void test_driver_scan(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen,
|
|
char *data)
|
|
{
|
|
char buf[512], *pos, *end;
|
|
int ret;
|
|
struct test_driver_bss *bss;
|
|
u8 sa[ETH_ALEN];
|
|
u8 ie[512];
|
|
size_t ielen;
|
|
union wpa_event_data event;
|
|
|
|
/* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */
|
|
|
|
wpa_printf(MSG_DEBUG, "test_driver: SCAN");
|
|
|
|
if (*data) {
|
|
if (*data != ' ' ||
|
|
hwaddr_aton(data + 1, sa)) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN "
|
|
"command format");
|
|
return;
|
|
}
|
|
|
|
data += 18;
|
|
while (*data == ' ')
|
|
data++;
|
|
ielen = os_strlen(data) / 2;
|
|
if (ielen > sizeof(ie))
|
|
ielen = sizeof(ie);
|
|
if (hexstr2bin(data, ie, ielen) < 0)
|
|
ielen = 0;
|
|
|
|
wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR,
|
|
MAC2STR(sa));
|
|
wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen);
|
|
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.rx_probe_req.sa = sa;
|
|
event.rx_probe_req.ie = ie;
|
|
event.rx_probe_req.ie_len = ielen;
|
|
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
|
|
}
|
|
|
|
dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
|
|
pos = buf;
|
|
end = buf + sizeof(buf);
|
|
|
|
/* reply: SCANRESP BSSID SSID IEs */
|
|
ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
|
|
MAC2STR(bss->bssid));
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos,
|
|
bss->ssid, bss->ssid_len);
|
|
ret = snprintf(pos, end - pos, " ");
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen);
|
|
pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie,
|
|
bss->wps_probe_resp_ie_len);
|
|
|
|
if (bss->privacy) {
|
|
ret = snprintf(pos, end - pos, " PRIVACY");
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
}
|
|
|
|
sendto(drv->test_socket, buf, pos - buf, 0,
|
|
(struct sockaddr *) from, fromlen);
|
|
}
|
|
}
|
|
|
|
|
|
static void test_driver_assoc(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen,
|
|
char *data)
|
|
{
|
|
struct test_client_socket *cli;
|
|
u8 ie[256], ssid[32];
|
|
size_t ielen, ssid_len = 0;
|
|
char *pos, *pos2, cmd[50];
|
|
struct test_driver_bss *bss, *tmp;
|
|
|
|
/* data: STA-addr SSID(hex) IEs(hex) */
|
|
|
|
cli = os_zalloc(sizeof(*cli));
|
|
if (cli == NULL)
|
|
return;
|
|
|
|
if (hwaddr_aton(data, cli->addr)) {
|
|
printf("test_socket: Invalid MAC address '%s' in ASSOC\n",
|
|
data);
|
|
os_free(cli);
|
|
return;
|
|
}
|
|
pos = data + 17;
|
|
while (*pos == ' ')
|
|
pos++;
|
|
pos2 = strchr(pos, ' ');
|
|
ielen = 0;
|
|
if (pos2) {
|
|
ssid_len = (pos2 - pos) / 2;
|
|
if (hexstr2bin(pos, ssid, ssid_len) < 0) {
|
|
wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__);
|
|
os_free(cli);
|
|
return;
|
|
}
|
|
wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID",
|
|
ssid, ssid_len);
|
|
|
|
pos = pos2 + 1;
|
|
ielen = strlen(pos) / 2;
|
|
if (ielen > sizeof(ie))
|
|
ielen = sizeof(ie);
|
|
if (hexstr2bin(pos, ie, ielen) < 0)
|
|
ielen = 0;
|
|
}
|
|
|
|
bss = NULL;
|
|
dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) {
|
|
if (tmp->ssid_len == ssid_len &&
|
|
os_memcmp(tmp->ssid, ssid, ssid_len) == 0) {
|
|
bss = tmp;
|
|
break;
|
|
}
|
|
}
|
|
if (bss == NULL) {
|
|
wpa_printf(MSG_DEBUG, "%s: No matching SSID found from "
|
|
"configured BSSes", __func__);
|
|
os_free(cli);
|
|
return;
|
|
}
|
|
|
|
cli->bss = bss;
|
|
memcpy(&cli->un, from, sizeof(cli->un));
|
|
cli->unlen = fromlen;
|
|
cli->next = drv->cli;
|
|
drv->cli = cli;
|
|
wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path",
|
|
(const u8 *) cli->un.sun_path,
|
|
cli->unlen - sizeof(cli->un.sun_family));
|
|
|
|
snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0",
|
|
MAC2STR(bss->bssid));
|
|
sendto(drv->test_socket, cmd, strlen(cmd), 0,
|
|
(struct sockaddr *) from, fromlen);
|
|
|
|
drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0);
|
|
}
|
|
|
|
|
|
static void test_driver_disassoc(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen)
|
|
{
|
|
struct test_client_socket *cli;
|
|
|
|
cli = test_driver_get_cli(drv, from, fromlen);
|
|
if (!cli)
|
|
return;
|
|
|
|
drv_event_disassoc(drv->ctx, cli->addr);
|
|
}
|
|
|
|
|
|
static void test_driver_eapol(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen,
|
|
u8 *data, size_t datalen)
|
|
{
|
|
#ifdef HOSTAPD
|
|
struct test_client_socket *cli;
|
|
#endif /* HOSTAPD */
|
|
const u8 *src = NULL;
|
|
|
|
if (datalen > 14) {
|
|
/* Skip Ethernet header */
|
|
src = data + ETH_ALEN;
|
|
wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src="
|
|
MACSTR " proto=%04x",
|
|
MAC2STR(data), MAC2STR(src),
|
|
WPA_GET_BE16(data + 2 * ETH_ALEN));
|
|
data += 14;
|
|
datalen -= 14;
|
|
}
|
|
|
|
#ifdef HOSTAPD
|
|
cli = test_driver_get_cli(drv, from, fromlen);
|
|
if (cli) {
|
|
drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data,
|
|
datalen);
|
|
} else {
|
|
wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown "
|
|
"client");
|
|
}
|
|
#else /* HOSTAPD */
|
|
if (src)
|
|
drv_event_eapol_rx(drv->ctx, src, data, datalen);
|
|
#endif /* HOSTAPD */
|
|
}
|
|
|
|
|
|
static void test_driver_ether(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen,
|
|
u8 *data, size_t datalen)
|
|
{
|
|
struct l2_ethhdr *eth;
|
|
|
|
if (datalen < sizeof(*eth))
|
|
return;
|
|
|
|
eth = (struct l2_ethhdr *) data;
|
|
wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src="
|
|
MACSTR " proto=%04x",
|
|
MAC2STR(eth->h_dest), MAC2STR(eth->h_source),
|
|
be_to_host16(eth->h_proto));
|
|
|
|
#ifdef CONFIG_IEEE80211R
|
|
if (be_to_host16(eth->h_proto) == ETH_P_RRB) {
|
|
union wpa_event_data ev;
|
|
os_memset(&ev, 0, sizeof(ev));
|
|
ev.ft_rrb_rx.src = eth->h_source;
|
|
ev.ft_rrb_rx.data = data + sizeof(*eth);
|
|
ev.ft_rrb_rx.data_len = datalen - sizeof(*eth);
|
|
}
|
|
#endif /* CONFIG_IEEE80211R */
|
|
}
|
|
|
|
|
|
static void test_driver_mlme(struct wpa_driver_test_data *drv,
|
|
struct sockaddr_un *from, socklen_t fromlen,
|
|
u8 *data, size_t datalen)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
u16 fc;
|
|
union wpa_event_data event;
|
|
int freq = 0, own_freq;
|
|
struct test_driver_bss *bss;
|
|
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
|
|
if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) {
|
|
size_t pos;
|
|
for (pos = 5; pos < datalen; pos++) {
|
|
if (data[pos] == ' ')
|
|
break;
|
|
}
|
|
if (pos < datalen) {
|
|
freq = atoi((const char *) &data[5]);
|
|
wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
|
|
"freq %d MHz", bss->ifname, freq);
|
|
pos++;
|
|
data += pos;
|
|
datalen -= pos;
|
|
}
|
|
}
|
|
|
|
if (drv->remain_on_channel_freq)
|
|
own_freq = drv->remain_on_channel_freq;
|
|
else
|
|
own_freq = drv->current_freq;
|
|
|
|
if (freq && own_freq && freq != own_freq) {
|
|
wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
|
|
"another frequency %d MHz (own %d MHz)",
|
|
bss->ifname, freq, own_freq);
|
|
return;
|
|
}
|
|
|
|
hdr = (struct ieee80211_hdr *) data;
|
|
|
|
if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) {
|
|
struct test_client_socket *cli;
|
|
cli = os_zalloc(sizeof(*cli));
|
|
if (cli == NULL)
|
|
return;
|
|
wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR,
|
|
MAC2STR(hdr->addr2));
|
|
memcpy(cli->addr, hdr->addr2, ETH_ALEN);
|
|
memcpy(&cli->un, from, sizeof(cli->un));
|
|
cli->unlen = fromlen;
|
|
cli->next = drv->cli;
|
|
drv->cli = cli;
|
|
}
|
|
|
|
wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame",
|
|
data, datalen);
|
|
fc = le_to_host16(hdr->frame_control);
|
|
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) {
|
|
wpa_printf(MSG_ERROR, "%s: received non-mgmt frame",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.rx_mgmt.frame = data;
|
|
event.rx_mgmt.frame_len = datalen;
|
|
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
|
|
}
|
|
|
|
|
|
static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx)
|
|
{
|
|
struct wpa_driver_test_data *drv = eloop_ctx;
|
|
char buf[2000];
|
|
int res;
|
|
struct sockaddr_un from;
|
|
socklen_t fromlen = sizeof(from);
|
|
|
|
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
|
|
(struct sockaddr *) &from, &fromlen);
|
|
if (res < 0) {
|
|
perror("recvfrom(test_socket)");
|
|
return;
|
|
}
|
|
buf[res] = '\0';
|
|
|
|
wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
|
|
|
|
if (strncmp(buf, "SCAN", 4) == 0) {
|
|
test_driver_scan(drv, &from, fromlen, buf + 4);
|
|
} else if (strncmp(buf, "ASSOC ", 6) == 0) {
|
|
test_driver_assoc(drv, &from, fromlen, buf + 6);
|
|
} else if (strcmp(buf, "DISASSOC") == 0) {
|
|
test_driver_disassoc(drv, &from, fromlen);
|
|
} else if (strncmp(buf, "EAPOL ", 6) == 0) {
|
|
test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6,
|
|
res - 6);
|
|
} else if (strncmp(buf, "ETHER ", 6) == 0) {
|
|
test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6,
|
|
res - 6);
|
|
} else if (strncmp(buf, "MLME ", 5) == 0) {
|
|
test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5);
|
|
} else {
|
|
wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
|
|
(u8 *) buf, res);
|
|
}
|
|
}
|
|
|
|
|
|
static int test_driver_set_generic_elem(void *priv,
|
|
const u8 *elem, size_t elem_len)
|
|
{
|
|
struct test_driver_bss *bss = priv;
|
|
|
|
os_free(bss->ie);
|
|
|
|
if (elem == NULL) {
|
|
bss->ie = NULL;
|
|
bss->ielen = 0;
|
|
return 0;
|
|
}
|
|
|
|
bss->ie = os_malloc(elem_len);
|
|
if (bss->ie == NULL) {
|
|
bss->ielen = 0;
|
|
return -1;
|
|
}
|
|
|
|
memcpy(bss->ie, elem, elem_len);
|
|
bss->ielen = elem_len;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
|
|
const struct wpabuf *proberesp,
|
|
const struct wpabuf *assocresp)
|
|
{
|
|
struct test_driver_bss *bss = priv;
|
|
|
|
if (beacon == NULL)
|
|
wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE");
|
|
else
|
|
wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE",
|
|
beacon);
|
|
|
|
os_free(bss->wps_beacon_ie);
|
|
|
|
if (beacon == NULL) {
|
|
bss->wps_beacon_ie = NULL;
|
|
bss->wps_beacon_ie_len = 0;
|
|
} else {
|
|
bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon));
|
|
if (bss->wps_beacon_ie == NULL) {
|
|
bss->wps_beacon_ie_len = 0;
|
|
return -1;
|
|
}
|
|
|
|
os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon),
|
|
wpabuf_len(beacon));
|
|
bss->wps_beacon_ie_len = wpabuf_len(beacon);
|
|
}
|
|
|
|
if (proberesp == NULL)
|
|
wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS "
|
|
"IE");
|
|
else
|
|
wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS "
|
|
"IE", proberesp);
|
|
|
|
os_free(bss->wps_probe_resp_ie);
|
|
|
|
if (proberesp == NULL) {
|
|
bss->wps_probe_resp_ie = NULL;
|
|
bss->wps_probe_resp_ie_len = 0;
|
|
} else {
|
|
bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp));
|
|
if (bss->wps_probe_resp_ie == NULL) {
|
|
bss->wps_probe_resp_ie_len = 0;
|
|
return -1;
|
|
}
|
|
|
|
os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp),
|
|
wpabuf_len(proberesp));
|
|
bss->wps_probe_resp_ie_len = wpabuf_len(proberesp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_sta_deauth(void *priv, const u8 *own_addr,
|
|
const u8 *addr, int reason)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_client_socket *cli;
|
|
|
|
if (drv->test_socket < 0)
|
|
return -1;
|
|
|
|
cli = drv->cli;
|
|
while (cli) {
|
|
if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
|
|
break;
|
|
cli = cli->next;
|
|
}
|
|
|
|
if (!cli)
|
|
return -1;
|
|
|
|
return sendto(drv->test_socket, "DEAUTH", 6, 0,
|
|
(struct sockaddr *) &cli->un, cli->unlen);
|
|
}
|
|
|
|
|
|
static int test_driver_sta_disassoc(void *priv, const u8 *own_addr,
|
|
const u8 *addr, int reason)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_client_socket *cli;
|
|
|
|
if (drv->test_socket < 0)
|
|
return -1;
|
|
|
|
cli = drv->cli;
|
|
while (cli) {
|
|
if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
|
|
break;
|
|
cli = cli->next;
|
|
}
|
|
|
|
if (!cli)
|
|
return -1;
|
|
|
|
return sendto(drv->test_socket, "DISASSOC", 8, 0,
|
|
(struct sockaddr *) &cli->un, cli->unlen);
|
|
}
|
|
|
|
|
|
static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid,
|
|
void *bss_ctx, void **drv_priv)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_driver_bss *bss;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")",
|
|
__func__, ifname, MAC2STR(bssid));
|
|
|
|
bss = os_zalloc(sizeof(*bss));
|
|
if (bss == NULL)
|
|
return -1;
|
|
|
|
bss->bss_ctx = bss_ctx;
|
|
bss->drv = drv;
|
|
os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
|
|
os_memcpy(bss->bssid, bssid, ETH_ALEN);
|
|
|
|
dl_list_add(&drv->bss, &bss->list);
|
|
if (drv->global) {
|
|
drv->global->bss_add_used = 1;
|
|
os_memcpy(drv->global->req_addr, bssid, ETH_ALEN);
|
|
}
|
|
|
|
if (drv_priv)
|
|
*drv_priv = bss;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_bss_remove(void *priv, const char *ifname)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_driver_bss *bss;
|
|
struct test_client_socket *cli, *prev_c;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname);
|
|
|
|
dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
|
|
if (strcmp(bss->ifname, ifname) != 0)
|
|
continue;
|
|
|
|
for (prev_c = NULL, cli = drv->cli; cli;
|
|
prev_c = cli, cli = cli->next) {
|
|
if (cli->bss != bss)
|
|
continue;
|
|
if (prev_c)
|
|
prev_c->next = cli->next;
|
|
else
|
|
drv->cli = cli->next;
|
|
os_free(cli);
|
|
break;
|
|
}
|
|
|
|
dl_list_del(&bss->list);
|
|
test_driver_free_bss(bss);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int test_driver_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)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)",
|
|
__func__, type, ifname, bss_ctx);
|
|
if (addr)
|
|
os_memcpy(if_addr, addr, ETH_ALEN);
|
|
else {
|
|
drv->alloc_iface_idx++;
|
|
if_addr[0] = 0x02; /* locally administered */
|
|
sha1_prf(drv->own_addr, ETH_ALEN,
|
|
"hostapd test addr generation",
|
|
(const u8 *) &drv->alloc_iface_idx,
|
|
sizeof(drv->alloc_iface_idx),
|
|
if_addr + 1, ETH_ALEN - 1);
|
|
}
|
|
if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
|
|
type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
|
|
return test_driver_bss_add(priv, ifname, if_addr, bss_ctx,
|
|
drv_priv);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type,
|
|
const char *ifname)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname);
|
|
if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
|
|
type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
|
|
return test_driver_bss_remove(priv, ifname);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
|
|
{
|
|
struct test_driver_bss *bss = priv;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
|
|
if (len < 0)
|
|
return -1;
|
|
wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
|
|
|
|
if ((size_t) len > sizeof(bss->ssid))
|
|
return -1;
|
|
|
|
os_memcpy(bss->ssid, buf, len);
|
|
bss->ssid_len = len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_set_privacy(void *priv, int enabled)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(enabled=%d)", __func__, enabled);
|
|
dbss->privacy = enabled;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_set_sta_vlan(void *priv, const u8 *addr,
|
|
const char *ifname, int vlan_id)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)",
|
|
__func__, MAC2STR(addr), ifname, vlan_id);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_driver_sta_add(void *priv,
|
|
struct hostapd_sta_add_params *params)
|
|
{
|
|
struct test_driver_bss *bss = priv;
|
|
struct wpa_driver_test_data *drv = bss->drv;
|
|
struct test_client_socket *cli;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d "
|
|
"capability=0x%x listen_interval=%d)",
|
|
__func__, bss->ifname, MAC2STR(params->addr), params->aid,
|
|
params->capability, params->listen_interval);
|
|
wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates",
|
|
params->supp_rates, params->supp_rates_len);
|
|
|
|
cli = drv->cli;
|
|
while (cli) {
|
|
if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0)
|
|
break;
|
|
cli = cli->next;
|
|
}
|
|
if (!cli) {
|
|
wpa_printf(MSG_DEBUG, "%s: no matching client entry",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
|
|
cli->bss = bss;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct wpa_driver_test_data * test_alloc_data(void *ctx,
|
|
const char *ifname)
|
|
{
|
|
struct wpa_driver_test_data *drv;
|
|
struct test_driver_bss *bss;
|
|
|
|
drv = os_zalloc(sizeof(struct wpa_driver_test_data));
|
|
if (drv == NULL) {
|
|
wpa_printf(MSG_ERROR, "Could not allocate memory for test "
|
|
"driver data");
|
|
return NULL;
|
|
}
|
|
|
|
bss = os_zalloc(sizeof(struct test_driver_bss));
|
|
if (bss == NULL) {
|
|
os_free(drv);
|
|
return NULL;
|
|
}
|
|
|
|
drv->ctx = ctx;
|
|
wpa_trace_add_ref(drv, ctx, ctx);
|
|
dl_list_init(&drv->bss);
|
|
dl_list_add(&drv->bss, &bss->list);
|
|
os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
|
|
bss->bss_ctx = ctx;
|
|
bss->drv = drv;
|
|
|
|
/* Generate a MAC address to help testing with multiple STAs */
|
|
drv->own_addr[0] = 0x02; /* locally administered */
|
|
sha1_prf((const u8 *) ifname, os_strlen(ifname),
|
|
"test mac addr generation",
|
|
NULL, 0, drv->own_addr + 1, ETH_ALEN - 1);
|
|
|
|
return drv;
|
|
}
|
|
|
|
|
|
static void * test_driver_init(struct hostapd_data *hapd,
|
|
struct wpa_init_params *params)
|
|
{
|
|
struct wpa_driver_test_data *drv;
|
|
struct sockaddr_un addr_un;
|
|
struct sockaddr_in addr_in;
|
|
struct sockaddr *addr;
|
|
socklen_t alen;
|
|
struct test_driver_bss *bss;
|
|
|
|
drv = test_alloc_data(hapd, params->ifname);
|
|
if (drv == NULL)
|
|
return NULL;
|
|
drv->ap = 1;
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
drv->global = params->global_priv;
|
|
|
|
bss->bss_ctx = hapd;
|
|
os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
|
|
os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN);
|
|
|
|
if (params->test_socket) {
|
|
if (os_strlen(params->test_socket) >=
|
|
sizeof(addr_un.sun_path)) {
|
|
printf("Too long test_socket path\n");
|
|
wpa_driver_test_deinit(bss);
|
|
return NULL;
|
|
}
|
|
if (strncmp(params->test_socket, "DIR:", 4) == 0) {
|
|
size_t len = strlen(params->test_socket) + 30;
|
|
drv->test_dir = os_strdup(params->test_socket + 4);
|
|
drv->own_socket_path = os_malloc(len);
|
|
if (drv->own_socket_path) {
|
|
snprintf(drv->own_socket_path, len,
|
|
"%s/AP-" MACSTR,
|
|
params->test_socket + 4,
|
|
MAC2STR(params->own_addr));
|
|
}
|
|
} else if (strncmp(params->test_socket, "UDP:", 4) == 0) {
|
|
drv->udp_port = atoi(params->test_socket + 4);
|
|
} else {
|
|
drv->own_socket_path = os_strdup(params->test_socket);
|
|
}
|
|
if (drv->own_socket_path == NULL && drv->udp_port == 0) {
|
|
wpa_driver_test_deinit(bss);
|
|
return NULL;
|
|
}
|
|
|
|
drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX,
|
|
SOCK_DGRAM, 0);
|
|
if (drv->test_socket < 0) {
|
|
perror("socket");
|
|
wpa_driver_test_deinit(bss);
|
|
return NULL;
|
|
}
|
|
|
|
if (drv->udp_port) {
|
|
os_memset(&addr_in, 0, sizeof(addr_in));
|
|
addr_in.sin_family = AF_INET;
|
|
addr_in.sin_port = htons(drv->udp_port);
|
|
addr = (struct sockaddr *) &addr_in;
|
|
alen = sizeof(addr_in);
|
|
} else {
|
|
os_memset(&addr_un, 0, sizeof(addr_un));
|
|
addr_un.sun_family = AF_UNIX;
|
|
os_strlcpy(addr_un.sun_path, drv->own_socket_path,
|
|
sizeof(addr_un.sun_path));
|
|
addr = (struct sockaddr *) &addr_un;
|
|
alen = sizeof(addr_un);
|
|
}
|
|
if (bind(drv->test_socket, addr, alen) < 0) {
|
|
perror("test-driver-init: bind(PF_UNIX)");
|
|
close(drv->test_socket);
|
|
if (drv->own_socket_path)
|
|
unlink(drv->own_socket_path);
|
|
wpa_driver_test_deinit(bss);
|
|
return NULL;
|
|
}
|
|
eloop_register_read_sock(drv->test_socket,
|
|
test_driver_receive_unix, drv, NULL);
|
|
} else
|
|
drv->test_socket = -1;
|
|
|
|
return bss;
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_driver_test_data *drv = eloop_ctx;
|
|
|
|
#ifdef DRIVER_TEST_UNIX
|
|
if (drv->associated && drv->hostapd_addr_set) {
|
|
struct stat st;
|
|
if (stat(drv->hostapd_addr.sun_path, &st) < 0) {
|
|
wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s",
|
|
__func__, strerror(errno));
|
|
drv->associated = 0;
|
|
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
|
|
}
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
|
|
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
|
|
}
|
|
|
|
|
|
#ifdef DRIVER_TEST_UNIX
|
|
static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
|
|
const char *path)
|
|
{
|
|
struct dirent *dent;
|
|
DIR *dir;
|
|
struct sockaddr_un addr;
|
|
char cmd[512], *pos, *end;
|
|
int ret;
|
|
|
|
dir = opendir(path);
|
|
if (dir == NULL)
|
|
return;
|
|
|
|
end = cmd + sizeof(cmd);
|
|
pos = cmd;
|
|
ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
|
|
MAC2STR(drv->own_addr));
|
|
if (ret >= 0 && ret < end - pos)
|
|
pos += ret;
|
|
if (drv->probe_req_ie) {
|
|
ret = os_snprintf(pos, end - pos, " ");
|
|
if (ret >= 0 && ret < end - pos)
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie,
|
|
drv->probe_req_ie_len);
|
|
}
|
|
if (drv->probe_req_ssid_len) {
|
|
/* Add SSID IE */
|
|
ret = os_snprintf(pos, end - pos, "%02x%02x",
|
|
WLAN_EID_SSID,
|
|
(unsigned int) drv->probe_req_ssid_len);
|
|
if (ret >= 0 && ret < end - pos)
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid,
|
|
drv->probe_req_ssid_len);
|
|
}
|
|
end[-1] = '\0';
|
|
|
|
while ((dent = readdir(dir))) {
|
|
if (os_strncmp(dent->d_name, "AP-", 3) != 0 &&
|
|
os_strncmp(dent->d_name, "STA-", 4) != 0)
|
|
continue;
|
|
if (drv->own_socket_path) {
|
|
size_t olen, dlen;
|
|
olen = os_strlen(drv->own_socket_path);
|
|
dlen = os_strlen(dent->d_name);
|
|
if (olen >= dlen &&
|
|
os_strcmp(dent->d_name,
|
|
drv->own_socket_path + olen - dlen) == 0)
|
|
continue;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name);
|
|
|
|
os_memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
|
|
path, dent->d_name);
|
|
|
|
if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
|
|
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
|
|
static int wpa_driver_test_scan(void *priv,
|
|
struct wpa_driver_scan_params *params)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
size_t i;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
|
|
|
|
os_free(drv->probe_req_ie);
|
|
if (params->extra_ies) {
|
|
drv->probe_req_ie = os_malloc(params->extra_ies_len);
|
|
if (drv->probe_req_ie == NULL) {
|
|
drv->probe_req_ie_len = 0;
|
|
return -1;
|
|
}
|
|
os_memcpy(drv->probe_req_ie, params->extra_ies,
|
|
params->extra_ies_len);
|
|
drv->probe_req_ie_len = params->extra_ies_len;
|
|
} else {
|
|
drv->probe_req_ie = NULL;
|
|
drv->probe_req_ie_len = 0;
|
|
}
|
|
|
|
for (i = 0; i < params->num_ssids; i++)
|
|
wpa_hexdump(MSG_DEBUG, "Scan SSID",
|
|
params->ssids[i].ssid, params->ssids[i].ssid_len);
|
|
drv->probe_req_ssid_len = 0;
|
|
if (params->num_ssids) {
|
|
os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid,
|
|
params->ssids[0].ssid_len);
|
|
drv->probe_req_ssid_len = params->ssids[0].ssid_len;
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)",
|
|
params->extra_ies, params->extra_ies_len);
|
|
|
|
drv->num_scanres = 0;
|
|
|
|
#ifdef DRIVER_TEST_UNIX
|
|
if (drv->test_socket >= 0 && drv->test_dir)
|
|
wpa_driver_scan_dir(drv, drv->test_dir);
|
|
|
|
if (drv->test_socket >= 0 && drv->hostapd_addr_set &&
|
|
sendto(drv->test_socket, "SCAN", 4, 0,
|
|
(struct sockaddr *) &drv->hostapd_addr,
|
|
sizeof(drv->hostapd_addr)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
|
|
sendto(drv->test_socket, "SCAN", 4, 0,
|
|
(struct sockaddr *) &drv->hostapd_addr_udp,
|
|
sizeof(drv->hostapd_addr_udp)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
}
|
|
|
|
eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
|
|
eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv,
|
|
drv->ctx);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct wpa_scan_results *res;
|
|
size_t i;
|
|
|
|
res = os_zalloc(sizeof(*res));
|
|
if (res == NULL)
|
|
return NULL;
|
|
|
|
res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *));
|
|
if (res->res == NULL) {
|
|
os_free(res);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < drv->num_scanres; i++) {
|
|
struct wpa_scan_res *r;
|
|
if (drv->scanres[i] == NULL)
|
|
continue;
|
|
r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len);
|
|
if (r == NULL)
|
|
break;
|
|
os_memcpy(r, drv->scanres[i],
|
|
sizeof(*r) + drv->scanres[i]->ie_len);
|
|
res->res[res->num++] = r;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_set_key(const char *ifname, void *priv,
|
|
enum wpa_alg alg, const u8 *addr,
|
|
int key_idx, int set_tx,
|
|
const u8 *seq, size_t seq_len,
|
|
const u8 *key, size_t key_len)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d "
|
|
"set_tx=%d",
|
|
__func__, ifname, priv, alg, key_idx, set_tx);
|
|
if (addr)
|
|
wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
|
|
if (seq)
|
|
wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len);
|
|
if (key)
|
|
wpa_hexdump_key(MSG_DEBUG, " key", key, key_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap)
|
|
{
|
|
if (ap && !drv->ap) {
|
|
wpa_driver_test_close_test_socket(drv);
|
|
wpa_driver_test_attach(drv, drv->test_dir, 1);
|
|
drv->ap = 1;
|
|
} else if (!ap && drv->ap) {
|
|
wpa_driver_test_close_test_socket(drv);
|
|
wpa_driver_test_attach(drv, drv->test_dir, 0);
|
|
drv->ap = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_associate(
|
|
void *priv, struct wpa_driver_associate_params *params)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
|
|
"group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
|
|
__func__, priv, params->freq, params->pairwise_suite,
|
|
params->group_suite, params->key_mgmt_suite,
|
|
params->auth_alg, params->mode);
|
|
wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
|
|
if (params->bssid) {
|
|
wpa_printf(MSG_DEBUG, " bssid=" MACSTR,
|
|
MAC2STR(params->bssid));
|
|
}
|
|
if (params->ssid) {
|
|
wpa_hexdump_ascii(MSG_DEBUG, " ssid",
|
|
params->ssid, params->ssid_len);
|
|
}
|
|
if (params->wpa_ie) {
|
|
wpa_hexdump(MSG_DEBUG, " wpa_ie",
|
|
params->wpa_ie, params->wpa_ie_len);
|
|
drv->assoc_wpa_ie_len = params->wpa_ie_len;
|
|
if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie))
|
|
drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie);
|
|
os_memcpy(drv->assoc_wpa_ie, params->wpa_ie,
|
|
drv->assoc_wpa_ie_len);
|
|
} else
|
|
drv->assoc_wpa_ie_len = 0;
|
|
|
|
wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
|
|
|
|
drv->ibss = params->mode == IEEE80211_MODE_IBSS;
|
|
dbss->privacy = params->key_mgmt_suite &
|
|
(WPA_KEY_MGMT_IEEE8021X |
|
|
WPA_KEY_MGMT_PSK |
|
|
WPA_KEY_MGMT_WPA_NONE |
|
|
WPA_KEY_MGMT_FT_IEEE8021X |
|
|
WPA_KEY_MGMT_FT_PSK |
|
|
WPA_KEY_MGMT_IEEE8021X_SHA256 |
|
|
WPA_KEY_MGMT_PSK_SHA256);
|
|
if (params->wep_key_len[params->wep_tx_keyidx])
|
|
dbss->privacy = 1;
|
|
|
|
#ifdef DRIVER_TEST_UNIX
|
|
if (drv->test_dir && params->bssid &&
|
|
params->mode != IEEE80211_MODE_IBSS) {
|
|
os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
|
|
drv->hostapd_addr.sun_family = AF_UNIX;
|
|
os_snprintf(drv->hostapd_addr.sun_path,
|
|
sizeof(drv->hostapd_addr.sun_path),
|
|
"%s/AP-" MACSTR,
|
|
drv->test_dir, MAC2STR(params->bssid));
|
|
drv->hostapd_addr_set = 1;
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
if (params->mode == IEEE80211_MODE_AP) {
|
|
os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
|
|
dbss->ssid_len = params->ssid_len;
|
|
os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN);
|
|
if (params->wpa_ie && params->wpa_ie_len) {
|
|
dbss->ie = os_malloc(params->wpa_ie_len);
|
|
if (dbss->ie) {
|
|
os_memcpy(dbss->ie, params->wpa_ie,
|
|
params->wpa_ie_len);
|
|
dbss->ielen = params->wpa_ie_len;
|
|
}
|
|
}
|
|
} else if (drv->test_socket >= 0 &&
|
|
(drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) {
|
|
char cmd[200], *pos, *end;
|
|
int ret;
|
|
end = cmd + sizeof(cmd);
|
|
pos = cmd;
|
|
ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ",
|
|
MAC2STR(drv->own_addr));
|
|
if (ret >= 0 && ret < end - pos)
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
|
|
params->ssid_len);
|
|
ret = os_snprintf(pos, end - pos, " ");
|
|
if (ret >= 0 && ret < end - pos)
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie,
|
|
params->wpa_ie_len);
|
|
end[-1] = '\0';
|
|
#ifdef DRIVER_TEST_UNIX
|
|
if (drv->hostapd_addr_set &&
|
|
sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
|
|
(struct sockaddr *) &drv->hostapd_addr,
|
|
sizeof(drv->hostapd_addr)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
return -1;
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
if (drv->hostapd_addr_udp_set &&
|
|
sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
|
|
(struct sockaddr *) &drv->hostapd_addr_udp,
|
|
sizeof(drv->hostapd_addr_udp)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
return -1;
|
|
}
|
|
|
|
os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
|
|
dbss->ssid_len = params->ssid_len;
|
|
} else {
|
|
drv->associated = 1;
|
|
if (params->mode == IEEE80211_MODE_IBSS) {
|
|
os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
|
|
dbss->ssid_len = params->ssid_len;
|
|
if (params->bssid)
|
|
os_memcpy(dbss->bssid, params->bssid,
|
|
ETH_ALEN);
|
|
else {
|
|
os_get_random(dbss->bssid, ETH_ALEN);
|
|
dbss->bssid[0] &= ~0x01;
|
|
dbss->bssid[0] |= 0x02;
|
|
}
|
|
}
|
|
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_get_bssid(void *priv, u8 *bssid)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
os_memcpy(bssid, dbss->bssid, ETH_ALEN);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_get_ssid(void *priv, u8 *ssid)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
os_memcpy(ssid, dbss->ssid, 32);
|
|
return dbss->ssid_len;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv)
|
|
{
|
|
#ifdef DRIVER_TEST_UNIX
|
|
if (drv->test_socket >= 0 &&
|
|
sendto(drv->test_socket, "DISASSOC", 8, 0,
|
|
(struct sockaddr *) &drv->hostapd_addr,
|
|
sizeof(drv->hostapd_addr)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
return -1;
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
|
|
sendto(drv->test_socket, "DISASSOC", 8, 0,
|
|
(struct sockaddr *) &drv->hostapd_addr_udp,
|
|
sizeof(drv->hostapd_addr_udp)) < 0) {
|
|
perror("sendto(test_socket)");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr,
|
|
int reason_code)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
|
|
__func__, MAC2STR(addr), reason_code);
|
|
os_memset(dbss->bssid, 0, ETH_ALEN);
|
|
drv->associated = 0;
|
|
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
|
|
return wpa_driver_test_send_disassoc(drv);
|
|
}
|
|
|
|
|
|
static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
|
|
{
|
|
const u8 *end, *pos;
|
|
|
|
pos = (const u8 *) (res + 1);
|
|
end = pos + res->ie_len;
|
|
|
|
while (pos + 1 < end) {
|
|
if (pos + 2 + pos[1] > end)
|
|
break;
|
|
if (pos[0] == ie)
|
|
return pos;
|
|
pos += 2 + pos[1];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen,
|
|
const char *data)
|
|
{
|
|
struct wpa_scan_res *res;
|
|
const char *pos, *pos2;
|
|
size_t len;
|
|
u8 *ie_pos, *ie_start, *ie_end;
|
|
#define MAX_IE_LEN 1000
|
|
const u8 *ds_params;
|
|
|
|
wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data);
|
|
if (drv->num_scanres >= MAX_SCAN_RESULTS) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan "
|
|
"result");
|
|
return;
|
|
}
|
|
|
|
/* SCANRESP BSSID SSID IEs */
|
|
|
|
res = os_zalloc(sizeof(*res) + MAX_IE_LEN);
|
|
if (res == NULL)
|
|
return;
|
|
ie_start = ie_pos = (u8 *) (res + 1);
|
|
ie_end = ie_pos + MAX_IE_LEN;
|
|
|
|
if (hwaddr_aton(data, res->bssid)) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres");
|
|
os_free(res);
|
|
return;
|
|
}
|
|
|
|
pos = data + 17;
|
|
while (*pos == ' ')
|
|
pos++;
|
|
pos2 = os_strchr(pos, ' ');
|
|
if (pos2 == NULL) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination "
|
|
"in scanres");
|
|
os_free(res);
|
|
return;
|
|
}
|
|
len = (pos2 - pos) / 2;
|
|
if (len > 32)
|
|
len = 32;
|
|
/*
|
|
* Generate SSID IE from the SSID field since this IE is not included
|
|
* in the main IE field.
|
|
*/
|
|
*ie_pos++ = WLAN_EID_SSID;
|
|
*ie_pos++ = len;
|
|
if (hexstr2bin(pos, ie_pos, len) < 0) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres");
|
|
os_free(res);
|
|
return;
|
|
}
|
|
ie_pos += len;
|
|
|
|
pos = pos2 + 1;
|
|
pos2 = os_strchr(pos, ' ');
|
|
if (pos2 == NULL)
|
|
len = os_strlen(pos) / 2;
|
|
else
|
|
len = (pos2 - pos) / 2;
|
|
if ((int) len > ie_end - ie_pos)
|
|
len = ie_end - ie_pos;
|
|
if (hexstr2bin(pos, ie_pos, len) < 0) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres");
|
|
os_free(res);
|
|
return;
|
|
}
|
|
ie_pos += len;
|
|
res->ie_len = ie_pos - ie_start;
|
|
|
|
if (pos2) {
|
|
pos = pos2 + 1;
|
|
while (*pos == ' ')
|
|
pos++;
|
|
if (os_strstr(pos, "PRIVACY"))
|
|
res->caps |= IEEE80211_CAP_PRIVACY;
|
|
if (os_strstr(pos, "IBSS"))
|
|
res->caps |= IEEE80211_CAP_IBSS;
|
|
}
|
|
|
|
ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS);
|
|
if (ds_params && ds_params[1] > 0) {
|
|
if (ds_params[2] >= 1 && ds_params[2] <= 13)
|
|
res->freq = 2407 + ds_params[2] * 5;
|
|
}
|
|
|
|
os_free(drv->scanres[drv->num_scanres]);
|
|
drv->scanres[drv->num_scanres++] = res;
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen,
|
|
const char *data)
|
|
{
|
|
struct test_driver_bss *bss;
|
|
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
|
|
/* ASSOCRESP BSSID <res> */
|
|
if (hwaddr_aton(data, bss->bssid)) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in "
|
|
"assocresp");
|
|
}
|
|
if (drv->use_associnfo) {
|
|
union wpa_event_data event;
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.assoc_info.req_ies = drv->assoc_wpa_ie;
|
|
event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len;
|
|
wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event);
|
|
}
|
|
drv->associated = 1;
|
|
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen)
|
|
{
|
|
drv->associated = 0;
|
|
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen,
|
|
const u8 *data, size_t data_len)
|
|
{
|
|
const u8 *src;
|
|
struct test_driver_bss *bss;
|
|
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
|
|
if (data_len > 14) {
|
|
/* Skip Ethernet header */
|
|
src = data + ETH_ALEN;
|
|
data += 14;
|
|
data_len -= 14;
|
|
} else
|
|
src = bss->bssid;
|
|
|
|
drv_event_eapol_rx(drv->ctx, src, data, data_len);
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen,
|
|
const u8 *data, size_t data_len)
|
|
{
|
|
int freq = 0, own_freq;
|
|
union wpa_event_data event;
|
|
const struct ieee80211_mgmt *mgmt;
|
|
u16 fc;
|
|
struct test_driver_bss *bss;
|
|
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) {
|
|
size_t pos;
|
|
for (pos = 5; pos < data_len; pos++) {
|
|
if (data[pos] == ' ')
|
|
break;
|
|
}
|
|
if (pos < data_len) {
|
|
freq = atoi((const char *) &data[5]);
|
|
wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
|
|
"freq %d MHz", bss->ifname, freq);
|
|
pos++;
|
|
data += pos;
|
|
data_len -= pos;
|
|
}
|
|
}
|
|
|
|
if (drv->remain_on_channel_freq)
|
|
own_freq = drv->remain_on_channel_freq;
|
|
else
|
|
own_freq = drv->current_freq;
|
|
|
|
if (freq && own_freq && freq != own_freq) {
|
|
wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
|
|
"another frequency %d MHz (own %d MHz)",
|
|
bss->ifname, freq, own_freq);
|
|
return;
|
|
}
|
|
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.mlme_rx.buf = data;
|
|
event.mlme_rx.len = data_len;
|
|
event.mlme_rx.freq = freq;
|
|
wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
fc = le_to_host16(mgmt->frame_control);
|
|
|
|
if (drv->probe_req_report && data_len >= 24) {
|
|
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
|
|
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
|
|
os_memset(&event, 0, sizeof(event));
|
|
event.rx_probe_req.sa = mgmt->sa;
|
|
event.rx_probe_req.da = mgmt->da;
|
|
event.rx_probe_req.bssid = mgmt->bssid;
|
|
event.rx_probe_req.ie = mgmt->u.probe_req.variable;
|
|
event.rx_probe_req.ie_len =
|
|
data_len - (mgmt->u.probe_req.variable - data);
|
|
wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
|
|
&event);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv,
|
|
struct sockaddr *from,
|
|
socklen_t fromlen,
|
|
const u8 *data, size_t data_len)
|
|
{
|
|
char buf[512], *pos, *end;
|
|
int ret;
|
|
struct test_driver_bss *bss;
|
|
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
|
|
/* data: optional [ STA-addr | ' ' | IEs(hex) ] */
|
|
|
|
if (!drv->ibss)
|
|
return;
|
|
|
|
pos = buf;
|
|
end = buf + sizeof(buf);
|
|
|
|
/* reply: SCANRESP BSSID SSID IEs */
|
|
ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
|
|
MAC2STR(bss->bssid));
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos,
|
|
bss->ssid, bss->ssid_len);
|
|
ret = snprintf(pos, end - pos, " ");
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie,
|
|
drv->assoc_wpa_ie_len);
|
|
|
|
if (bss->privacy) {
|
|
ret = snprintf(pos, end - pos, " PRIVACY");
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
}
|
|
|
|
ret = snprintf(pos, end - pos, " IBSS");
|
|
if (ret < 0 || ret >= end - pos)
|
|
return;
|
|
pos += ret;
|
|
|
|
sendto(drv->test_socket, buf, pos - buf, 0,
|
|
(struct sockaddr *) from, fromlen);
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx,
|
|
void *sock_ctx)
|
|
{
|
|
struct wpa_driver_test_data *drv = eloop_ctx;
|
|
char *buf;
|
|
int res;
|
|
struct sockaddr_storage from;
|
|
socklen_t fromlen = sizeof(from);
|
|
const size_t buflen = 2000;
|
|
|
|
if (drv->ap) {
|
|
test_driver_receive_unix(sock, eloop_ctx, sock_ctx);
|
|
return;
|
|
}
|
|
|
|
buf = os_malloc(buflen);
|
|
if (buf == NULL)
|
|
return;
|
|
res = recvfrom(sock, buf, buflen - 1, 0,
|
|
(struct sockaddr *) &from, &fromlen);
|
|
if (res < 0) {
|
|
perror("recvfrom(test_socket)");
|
|
os_free(buf);
|
|
return;
|
|
}
|
|
buf[res] = '\0';
|
|
|
|
wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
|
|
|
|
if (os_strncmp(buf, "SCANRESP ", 9) == 0) {
|
|
wpa_driver_test_scanresp(drv, (struct sockaddr *) &from,
|
|
fromlen, buf + 9);
|
|
} else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) {
|
|
wpa_driver_test_assocresp(drv, (struct sockaddr *) &from,
|
|
fromlen, buf + 10);
|
|
} else if (os_strcmp(buf, "DISASSOC") == 0) {
|
|
wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
|
|
fromlen);
|
|
} else if (os_strcmp(buf, "DEAUTH") == 0) {
|
|
wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
|
|
fromlen);
|
|
} else if (os_strncmp(buf, "EAPOL ", 6) == 0) {
|
|
wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen,
|
|
(const u8 *) buf + 6, res - 6);
|
|
} else if (os_strncmp(buf, "MLME ", 5) == 0) {
|
|
wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen,
|
|
(const u8 *) buf + 5, res - 5);
|
|
} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
|
|
wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from,
|
|
fromlen,
|
|
(const u8 *) buf + 5, res - 5);
|
|
} else {
|
|
wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
|
|
(u8 *) buf, res);
|
|
}
|
|
os_free(buf);
|
|
}
|
|
|
|
|
|
static void * wpa_driver_test_init2(void *ctx, const char *ifname,
|
|
void *global_priv)
|
|
{
|
|
struct wpa_driver_test_data *drv;
|
|
struct wpa_driver_test_global *global = global_priv;
|
|
struct test_driver_bss *bss;
|
|
|
|
drv = test_alloc_data(ctx, ifname);
|
|
if (drv == NULL)
|
|
return NULL;
|
|
bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
|
|
drv->global = global_priv;
|
|
drv->test_socket = -1;
|
|
|
|
/* Set dummy BSSID and SSID for testing. */
|
|
bss->bssid[0] = 0x02;
|
|
bss->bssid[1] = 0x00;
|
|
bss->bssid[2] = 0x00;
|
|
bss->bssid[3] = 0x00;
|
|
bss->bssid[4] = 0x00;
|
|
bss->bssid[5] = 0x01;
|
|
os_memcpy(bss->ssid, "test", 5);
|
|
bss->ssid_len = 4;
|
|
|
|
if (global->bss_add_used) {
|
|
os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN);
|
|
global->bss_add_used = 0;
|
|
}
|
|
|
|
eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
|
|
|
|
return bss;
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv)
|
|
{
|
|
if (drv->test_socket >= 0) {
|
|
eloop_unregister_read_sock(drv->test_socket);
|
|
close(drv->test_socket);
|
|
drv->test_socket = -1;
|
|
}
|
|
|
|
if (drv->own_socket_path) {
|
|
unlink(drv->own_socket_path);
|
|
os_free(drv->own_socket_path);
|
|
drv->own_socket_path = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_deinit(void *priv)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
struct test_client_socket *cli, *prev;
|
|
int i;
|
|
|
|
cli = drv->cli;
|
|
while (cli) {
|
|
prev = cli;
|
|
cli = cli->next;
|
|
os_free(prev);
|
|
}
|
|
|
|
#ifdef HOSTAPD
|
|
/* There should be only one BSS remaining at this point. */
|
|
if (dl_list_len(&drv->bss) != 1)
|
|
wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries",
|
|
__func__, dl_list_len(&drv->bss));
|
|
#endif /* HOSTAPD */
|
|
|
|
test_driver_free_bsses(drv);
|
|
|
|
wpa_driver_test_close_test_socket(drv);
|
|
eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
|
|
eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL);
|
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
|
os_free(drv->test_dir);
|
|
for (i = 0; i < MAX_SCAN_RESULTS; i++)
|
|
os_free(drv->scanres[i]);
|
|
os_free(drv->probe_req_ie);
|
|
wpa_trace_remove_ref(drv, ctx, drv->ctx);
|
|
os_free(drv);
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
|
|
const char *dir, int ap)
|
|
{
|
|
#ifdef DRIVER_TEST_UNIX
|
|
static unsigned int counter = 0;
|
|
struct sockaddr_un addr;
|
|
size_t len;
|
|
|
|
os_free(drv->own_socket_path);
|
|
if (dir) {
|
|
len = os_strlen(dir) + 30;
|
|
drv->own_socket_path = os_malloc(len);
|
|
if (drv->own_socket_path == NULL)
|
|
return -1;
|
|
os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR,
|
|
dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr));
|
|
} else {
|
|
drv->own_socket_path = os_malloc(100);
|
|
if (drv->own_socket_path == NULL)
|
|
return -1;
|
|
os_snprintf(drv->own_socket_path, 100,
|
|
"/tmp/wpa_supplicant_test-%d-%d",
|
|
getpid(), counter++);
|
|
}
|
|
|
|
drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
if (drv->test_socket < 0) {
|
|
perror("socket(PF_UNIX)");
|
|
os_free(drv->own_socket_path);
|
|
drv->own_socket_path = NULL;
|
|
return -1;
|
|
}
|
|
|
|
os_memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
|
|
if (bind(drv->test_socket, (struct sockaddr *) &addr,
|
|
sizeof(addr)) < 0) {
|
|
perror("test-driver-attach: bind(PF_UNIX)");
|
|
close(drv->test_socket);
|
|
unlink(drv->own_socket_path);
|
|
os_free(drv->own_socket_path);
|
|
drv->own_socket_path = NULL;
|
|
return -1;
|
|
}
|
|
|
|
eloop_register_read_sock(drv->test_socket,
|
|
wpa_driver_test_receive_unix, drv, NULL);
|
|
|
|
return 0;
|
|
#else /* DRIVER_TEST_UNIX */
|
|
return -1;
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv,
|
|
char *dst)
|
|
{
|
|
char *pos;
|
|
|
|
pos = os_strchr(dst, ':');
|
|
if (pos == NULL)
|
|
return -1;
|
|
*pos++ = '\0';
|
|
wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos);
|
|
|
|
drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0);
|
|
if (drv->test_socket < 0) {
|
|
perror("socket(PF_INET)");
|
|
return -1;
|
|
}
|
|
|
|
os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp));
|
|
drv->hostapd_addr_udp.sin_family = AF_INET;
|
|
#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
|
|
{
|
|
int a[4];
|
|
u8 *pos;
|
|
sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
|
|
pos = (u8 *) &drv->hostapd_addr_udp.sin_addr;
|
|
*pos++ = a[0];
|
|
*pos++ = a[1];
|
|
*pos++ = a[2];
|
|
*pos++ = a[3];
|
|
}
|
|
#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
|
|
inet_aton(dst, &drv->hostapd_addr_udp.sin_addr);
|
|
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
|
|
drv->hostapd_addr_udp.sin_port = htons(atoi(pos));
|
|
|
|
drv->hostapd_addr_udp_set = 1;
|
|
|
|
eloop_register_read_sock(drv->test_socket,
|
|
wpa_driver_test_receive_unix, drv, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_set_param(void *priv, const char *param)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
const char *pos;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
|
|
if (param == NULL)
|
|
return 0;
|
|
|
|
wpa_driver_test_close_test_socket(drv);
|
|
|
|
#ifdef DRIVER_TEST_UNIX
|
|
pos = os_strstr(param, "test_socket=");
|
|
if (pos) {
|
|
const char *pos2;
|
|
size_t len;
|
|
|
|
pos += 12;
|
|
pos2 = os_strchr(pos, ' ');
|
|
if (pos2)
|
|
len = pos2 - pos;
|
|
else
|
|
len = os_strlen(pos);
|
|
if (len > sizeof(drv->hostapd_addr.sun_path))
|
|
return -1;
|
|
os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
|
|
drv->hostapd_addr.sun_family = AF_UNIX;
|
|
os_memcpy(drv->hostapd_addr.sun_path, pos, len);
|
|
drv->hostapd_addr_set = 1;
|
|
}
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
pos = os_strstr(param, "test_dir=");
|
|
if (pos) {
|
|
char *end;
|
|
os_free(drv->test_dir);
|
|
drv->test_dir = os_strdup(pos + 9);
|
|
if (drv->test_dir == NULL)
|
|
return -1;
|
|
end = os_strchr(drv->test_dir, ' ');
|
|
if (end)
|
|
*end = '\0';
|
|
if (wpa_driver_test_attach(drv, drv->test_dir, 0))
|
|
return -1;
|
|
} else {
|
|
pos = os_strstr(param, "test_udp=");
|
|
if (pos) {
|
|
char *dst, *epos;
|
|
dst = os_strdup(pos + 9);
|
|
if (dst == NULL)
|
|
return -1;
|
|
epos = os_strchr(dst, ' ');
|
|
if (epos)
|
|
*epos = '\0';
|
|
if (wpa_driver_test_attach_udp(drv, dst))
|
|
return -1;
|
|
os_free(dst);
|
|
} else if (wpa_driver_test_attach(drv, NULL, 0))
|
|
return -1;
|
|
}
|
|
|
|
if (os_strstr(param, "use_associnfo=1")) {
|
|
wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events");
|
|
drv->use_associnfo = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const u8 * wpa_driver_test_get_mac_addr(void *priv)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "%s", __func__);
|
|
return drv->own_addr;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto,
|
|
const u8 *data, size_t data_len)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
char *msg;
|
|
size_t msg_len;
|
|
struct l2_ethhdr eth;
|
|
struct sockaddr *addr;
|
|
socklen_t alen;
|
|
#ifdef DRIVER_TEST_UNIX
|
|
struct sockaddr_un addr_un;
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
|
|
wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len);
|
|
|
|
os_memset(ð, 0, sizeof(eth));
|
|
os_memcpy(eth.h_dest, dest, ETH_ALEN);
|
|
os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN);
|
|
eth.h_proto = host_to_be16(proto);
|
|
|
|
msg_len = 6 + sizeof(eth) + data_len;
|
|
msg = os_malloc(msg_len);
|
|
if (msg == NULL)
|
|
return -1;
|
|
os_memcpy(msg, "EAPOL ", 6);
|
|
os_memcpy(msg + 6, ð, sizeof(eth));
|
|
os_memcpy(msg + 6 + sizeof(eth), data, data_len);
|
|
|
|
if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
|
|
drv->test_dir == NULL) {
|
|
if (drv->hostapd_addr_udp_set) {
|
|
addr = (struct sockaddr *) &drv->hostapd_addr_udp;
|
|
alen = sizeof(drv->hostapd_addr_udp);
|
|
} else {
|
|
#ifdef DRIVER_TEST_UNIX
|
|
addr = (struct sockaddr *) &drv->hostapd_addr;
|
|
alen = sizeof(drv->hostapd_addr);
|
|
#else /* DRIVER_TEST_UNIX */
|
|
os_free(msg);
|
|
return -1;
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
}
|
|
} else {
|
|
#ifdef DRIVER_TEST_UNIX
|
|
struct stat st;
|
|
os_memset(&addr_un, 0, sizeof(addr_un));
|
|
addr_un.sun_family = AF_UNIX;
|
|
os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
|
|
"%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest));
|
|
if (stat(addr_un.sun_path, &st) < 0) {
|
|
os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
|
|
"%s/AP-" MACSTR,
|
|
drv->test_dir, MAC2STR(dest));
|
|
}
|
|
addr = (struct sockaddr *) &addr_un;
|
|
alen = sizeof(addr_un);
|
|
#else /* DRIVER_TEST_UNIX */
|
|
os_free(msg);
|
|
return -1;
|
|
#endif /* DRIVER_TEST_UNIX */
|
|
}
|
|
|
|
if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) {
|
|
perror("sendmsg(test_socket)");
|
|
os_free(msg);
|
|
return -1;
|
|
}
|
|
|
|
os_free(msg);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
|
|
{
|
|
os_memset(capa, 0, sizeof(*capa));
|
|
capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_FT |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
|
|
capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 |
|
|
WPA_DRIVER_CAPA_ENC_WEP104 |
|
|
WPA_DRIVER_CAPA_ENC_TKIP |
|
|
WPA_DRIVER_CAPA_ENC_CCMP;
|
|
capa->auth = WPA_DRIVER_AUTH_OPEN |
|
|
WPA_DRIVER_AUTH_SHARED |
|
|
WPA_DRIVER_AUTH_LEAP;
|
|
capa->flags |= WPA_DRIVER_FLAGS_AP;
|
|
capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
|
|
capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
|
|
capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
|
|
capa->max_scan_ssids = 2;
|
|
capa->max_remain_on_chan = 60000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr,
|
|
int protect_type,
|
|
int key_type)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d",
|
|
__func__, protect_type, key_type);
|
|
|
|
if (addr) {
|
|
wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR,
|
|
__func__, MAC2STR(addr));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void * wpa_driver_test_global_init(void)
|
|
{
|
|
struct wpa_driver_test_global *global;
|
|
|
|
global = os_zalloc(sizeof(*global));
|
|
return global;
|
|
}
|
|
|
|
|
|
static void wpa_driver_test_global_deinit(void *priv)
|
|
{
|
|
struct wpa_driver_test_global *global = priv;
|
|
os_free(global);
|
|
}
|
|
|
|
|
|
static struct wpa_interface_info *
|
|
wpa_driver_test_get_interfaces(void *global_priv)
|
|
{
|
|
/* struct wpa_driver_test_global *global = priv; */
|
|
struct wpa_interface_info *iface;
|
|
|
|
iface = os_zalloc(sizeof(*iface));
|
|
if (iface == NULL)
|
|
return iface;
|
|
iface->ifname = os_strdup("sta0");
|
|
iface->desc = os_strdup("test interface 0");
|
|
iface->drv_name = "test";
|
|
iface->next = os_zalloc(sizeof(*iface));
|
|
if (iface->next) {
|
|
iface->next->ifname = os_strdup("sta1");
|
|
iface->next->desc = os_strdup("test interface 1");
|
|
iface->next->drv_name = "test";
|
|
}
|
|
|
|
return iface;
|
|
}
|
|
|
|
|
|
static struct hostapd_hw_modes *
|
|
wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
|
|
{
|
|
struct hostapd_hw_modes *modes;
|
|
size_t i;
|
|
|
|
*num_modes = 3;
|
|
*flags = 0;
|
|
modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes));
|
|
if (modes == NULL)
|
|
return NULL;
|
|
modes[0].mode = HOSTAPD_MODE_IEEE80211G;
|
|
modes[0].num_channels = 11;
|
|
modes[0].num_rates = 12;
|
|
modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
|
|
modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int));
|
|
if (modes[0].channels == NULL || modes[0].rates == NULL)
|
|
goto fail;
|
|
for (i = 0; i < 11; i++) {
|
|
modes[0].channels[i].chan = i + 1;
|
|
modes[0].channels[i].freq = 2412 + 5 * i;
|
|
modes[0].channels[i].flag = 0;
|
|
}
|
|
modes[0].rates[0] = 10;
|
|
modes[0].rates[1] = 20;
|
|
modes[0].rates[2] = 55;
|
|
modes[0].rates[3] = 110;
|
|
modes[0].rates[4] = 60;
|
|
modes[0].rates[5] = 90;
|
|
modes[0].rates[6] = 120;
|
|
modes[0].rates[7] = 180;
|
|
modes[0].rates[8] = 240;
|
|
modes[0].rates[9] = 360;
|
|
modes[0].rates[10] = 480;
|
|
modes[0].rates[11] = 540;
|
|
|
|
modes[1].mode = HOSTAPD_MODE_IEEE80211B;
|
|
modes[1].num_channels = 11;
|
|
modes[1].num_rates = 4;
|
|
modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
|
|
modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int));
|
|
if (modes[1].channels == NULL || modes[1].rates == NULL)
|
|
goto fail;
|
|
for (i = 0; i < 11; i++) {
|
|
modes[1].channels[i].chan = i + 1;
|
|
modes[1].channels[i].freq = 2412 + 5 * i;
|
|
modes[1].channels[i].flag = 0;
|
|
}
|
|
modes[1].rates[0] = 10;
|
|
modes[1].rates[1] = 20;
|
|
modes[1].rates[2] = 55;
|
|
modes[1].rates[3] = 110;
|
|
|
|
modes[2].mode = HOSTAPD_MODE_IEEE80211A;
|
|
modes[2].num_channels = 1;
|
|
modes[2].num_rates = 8;
|
|
modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data));
|
|
modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int));
|
|
if (modes[2].channels == NULL || modes[2].rates == NULL)
|
|
goto fail;
|
|
modes[2].channels[0].chan = 60;
|
|
modes[2].channels[0].freq = 5300;
|
|
modes[2].channels[0].flag = 0;
|
|
modes[2].rates[0] = 60;
|
|
modes[2].rates[1] = 90;
|
|
modes[2].rates[2] = 120;
|
|
modes[2].rates[3] = 180;
|
|
modes[2].rates[4] = 240;
|
|
modes[2].rates[5] = 360;
|
|
modes[2].rates[6] = 480;
|
|
modes[2].rates[7] = 540;
|
|
|
|
return modes;
|
|
|
|
fail:
|
|
if (modes) {
|
|
for (i = 0; i < *num_modes; i++) {
|
|
os_free(modes[i].channels);
|
|
os_free(modes[i].rates);
|
|
}
|
|
os_free(modes);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_set_freq(void *priv,
|
|
struct hostapd_freq_params *freq)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq);
|
|
drv->current_freq = freq->freq;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_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)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
int ret = -1;
|
|
u8 *buf;
|
|
struct ieee80211_hdr *hdr;
|
|
|
|
wpa_printf(MSG_DEBUG, "test: Send Action frame");
|
|
|
|
if ((drv->remain_on_channel_freq &&
|
|
freq != drv->remain_on_channel_freq) ||
|
|
(drv->remain_on_channel_freq == 0 &&
|
|
freq != (unsigned int) drv->current_freq)) {
|
|
wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on "
|
|
"unexpected channel: freq=%u MHz (current_freq=%u "
|
|
"MHz, remain-on-channel freq=%u MHz)",
|
|
freq, drv->current_freq,
|
|
drv->remain_on_channel_freq);
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
|
|
ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0);
|
|
os_free(buf);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_driver_test_data *drv = eloop_ctx;
|
|
union wpa_event_data data;
|
|
|
|
wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout");
|
|
|
|
os_memset(&data, 0, sizeof(data));
|
|
data.remain_on_channel.freq = drv->remain_on_channel_freq;
|
|
data.remain_on_channel.duration = drv->remain_on_channel_duration;
|
|
|
|
drv->remain_on_channel_freq = 0;
|
|
|
|
wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
|
|
unsigned int duration)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
union wpa_event_data data;
|
|
|
|
wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)",
|
|
__func__, freq, duration);
|
|
if (drv->remain_on_channel_freq &&
|
|
drv->remain_on_channel_freq != freq) {
|
|
wpa_printf(MSG_DEBUG, "test: Refuse concurrent "
|
|
"remain_on_channel request");
|
|
return -1;
|
|
}
|
|
|
|
drv->remain_on_channel_freq = freq;
|
|
drv->remain_on_channel_duration = duration;
|
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
|
eloop_register_timeout(duration / 1000, (duration % 1000) * 1000,
|
|
test_remain_on_channel_timeout, drv, NULL);
|
|
|
|
os_memset(&data, 0, sizeof(data));
|
|
data.remain_on_channel.freq = freq;
|
|
data.remain_on_channel.duration = duration;
|
|
wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_cancel_remain_on_channel(void *priv)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "%s", __func__);
|
|
if (!drv->remain_on_channel_freq)
|
|
return -1;
|
|
drv->remain_on_channel_freq = 0;
|
|
eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_test_probe_req_report(void *priv, int report)
|
|
{
|
|
struct test_driver_bss *dbss = priv;
|
|
struct wpa_driver_test_data *drv = dbss->drv;
|
|
wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report);
|
|
drv->probe_req_report = report;
|
|
return 0;
|
|
}
|
|
|
|
|
|
const struct wpa_driver_ops wpa_driver_test_ops = {
|
|
"test",
|
|
"wpa_supplicant test driver",
|
|
.hapd_init = test_driver_init,
|
|
.hapd_deinit = wpa_driver_test_deinit,
|
|
.hapd_send_eapol = test_driver_send_eapol,
|
|
.send_mlme = wpa_driver_test_send_mlme,
|
|
.set_generic_elem = test_driver_set_generic_elem,
|
|
.sta_deauth = test_driver_sta_deauth,
|
|
.sta_disassoc = test_driver_sta_disassoc,
|
|
.get_hw_feature_data = wpa_driver_test_get_hw_feature_data,
|
|
.if_add = test_driver_if_add,
|
|
.if_remove = test_driver_if_remove,
|
|
.hapd_set_ssid = test_driver_set_ssid,
|
|
.set_privacy = test_driver_set_privacy,
|
|
.set_sta_vlan = test_driver_set_sta_vlan,
|
|
.sta_add = test_driver_sta_add,
|
|
.send_ether = test_driver_send_ether,
|
|
.set_ap_wps_ie = test_driver_set_ap_wps_ie,
|
|
.get_bssid = wpa_driver_test_get_bssid,
|
|
.get_ssid = wpa_driver_test_get_ssid,
|
|
.set_key = wpa_driver_test_set_key,
|
|
.deinit = wpa_driver_test_deinit,
|
|
.set_param = wpa_driver_test_set_param,
|
|
.deauthenticate = wpa_driver_test_deauthenticate,
|
|
.associate = wpa_driver_test_associate,
|
|
.get_capa = wpa_driver_test_get_capa,
|
|
.get_mac_addr = wpa_driver_test_get_mac_addr,
|
|
.send_eapol = wpa_driver_test_send_eapol,
|
|
.mlme_setprotection = wpa_driver_test_mlme_setprotection,
|
|
.get_scan_results2 = wpa_driver_test_get_scan_results2,
|
|
.global_init = wpa_driver_test_global_init,
|
|
.global_deinit = wpa_driver_test_global_deinit,
|
|
.init2 = wpa_driver_test_init2,
|
|
.get_interfaces = wpa_driver_test_get_interfaces,
|
|
.scan2 = wpa_driver_test_scan,
|
|
.set_freq = wpa_driver_test_set_freq,
|
|
.send_action = wpa_driver_test_send_action,
|
|
.remain_on_channel = wpa_driver_test_remain_on_channel,
|
|
.cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
|
|
.probe_req_report = wpa_driver_test_probe_req_report,
|
|
};
|