| /* |
| * 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 "p2p/p2p.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; |
| |
| struct p2p_data *p2p; |
| 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; |
| unsigned int pending_action_no_cck; |
| unsigned int pending_listen_freq; |
| unsigned int pending_listen_duration; |
| int pending_p2p_scan; |
| struct sockaddr *probe_from; |
| socklen_t probe_from_len; |
| }; |
| |
| |
| 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 int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); |
| |
| |
| 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); |
| |
| #ifdef CONFIG_P2P |
| if (drv->p2p && |
| WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && |
| WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { |
| if (drv->pending_action_tx == NULL) { |
| wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " |
| "no pending operation"); |
| return ret; |
| } |
| |
| if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != |
| 0) { |
| wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " |
| "unknown destination address"); |
| return ret; |
| } |
| |
| wpabuf_free(drv->pending_action_tx); |
| drv->pending_action_tx = NULL; |
| |
| p2p_send_action_cb(drv->p2p, drv->pending_action_freq, |
| drv->pending_action_dst, |
| drv->pending_action_src, |
| drv->pending_action_bssid, |
| ret >= 0); |
| } |
| #endif /* CONFIG_P2P */ |
| |
| 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); |
| #ifdef CONFIG_P2P |
| if (drv->p2p) |
| p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); |
| #endif /* CONFIG_P2P */ |
| } |
| |
| 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) |
| { |
| 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) |
| { |
| struct wpa_driver_test_data *drv = eloop_ctx; |
| wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); |
| if (drv->pending_p2p_scan && drv->p2p) { |
| #ifdef CONFIG_P2P |
| size_t i; |
| for (i = 0; i < drv->num_scanres; i++) { |
| struct wpa_scan_res *bss = drv->scanres[i]; |
| if (p2p_scan_res_handler(drv->p2p, bss->bssid, |
| bss->freq, bss->age, |
| bss->level, |
| (const u8 *) (bss + 1), |
| bss->ie_len) > 0) |
| return; |
| } |
| p2p_scan_res_handled(drv->p2p); |
| #endif /* CONFIG_P2P */ |
| return; |
| } |
| 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); |
| #ifdef CONFIG_P2P |
| if (drv->p2p) |
| p2p_probe_req_rx(drv->p2p, mgmt->sa, |
| mgmt->da, mgmt->bssid, |
| event.rx_probe_req.ie, |
| event.rx_probe_req.ie_len); |
| #endif /* CONFIG_P2P */ |
| } |
| } |
| |
| #ifdef CONFIG_P2P |
| if (drv->p2p && |
| WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && |
| WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { |
| size_t hdr_len; |
| hdr_len = (const u8 *) |
| &mgmt->u.action.u.vs_public_action.action - data; |
| p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, |
| mgmt->u.action.category, |
| &mgmt->u.action.u.vs_public_action.action, |
| data_len - hdr_len, freq); |
| } |
| #endif /* CONFIG_P2P */ |
| |
| } |
| |
| |
| 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) ] */ |
| #ifdef CONFIG_P2P |
| if (drv->probe_req_report && drv->p2p && data_len) { |
| const char *d = (const char *) data; |
| u8 sa[ETH_ALEN]; |
| u8 ie[512]; |
| size_t ielen; |
| |
| if (hwaddr_aton(d, sa)) |
| return; |
| d += 18; |
| while (*d == ' ') |
| d++; |
| ielen = os_strlen(d) / 2; |
| if (ielen > sizeof(ie)) |
| ielen = sizeof(ie); |
| if (hexstr2bin(d, ie, ielen) < 0) |
| ielen = 0; |
| drv->probe_from = from; |
| drv->probe_from_len = fromlen; |
| p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); |
| drv->probe_from = NULL; |
| } |
| #endif /* CONFIG_P2P */ |
| |
| 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; |
| |
| #ifdef CONFIG_P2P |
| if (drv->p2p) |
| p2p_deinit(drv->p2p); |
| wpabuf_free(drv->pending_action_tx); |
| #endif /* CONFIG_P2P */ |
| |
| 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; |
| } |
| |
| if (os_strstr(param, "p2p_mgmt=1")) { |
| wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " |
| "management"); |
| if (wpa_driver_test_init_p2p(drv) < 0) |
| return -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) |
| { |
| struct test_driver_bss *dbss = priv; |
| struct wpa_driver_test_data *drv = dbss->drv; |
| 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; |
| if (drv->p2p) |
| capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; |
| 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; |
| } |
| |
| |
| #ifdef CONFIG_P2P |
| static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) |
| { |
| struct wpa_driver_test_data *drv = eloop_ctx; |
| |
| if (drv->pending_action_tx == NULL) |
| return; |
| |
| if (drv->off_channel_freq != drv->pending_action_freq) { |
| wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " |
| "waiting for another freq=%u", |
| drv->pending_action_freq); |
| return; |
| } |
| wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " |
| MACSTR, MAC2STR(drv->pending_action_dst)); |
| wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, |
| drv->pending_action_dst, |
| drv->pending_action_src, |
| drv->pending_action_bssid, |
| wpabuf_head(drv->pending_action_tx), |
| wpabuf_len(drv->pending_action_tx), |
| drv->pending_action_no_cck); |
| } |
| #endif /* CONFIG_P2P */ |
| |
| |
| 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; |
| |
| if (drv->p2p) |
| drv->off_channel_freq = 0; |
| |
| 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); |
| |
| #ifdef CONFIG_P2P |
| if (drv->p2p) { |
| drv->off_channel_freq = drv->remain_on_channel_freq; |
| test_send_action_cb(drv, NULL); |
| if (drv->off_channel_freq == drv->pending_listen_freq) { |
| p2p_listen_cb(drv->p2p, drv->pending_listen_freq, |
| drv->pending_listen_duration); |
| drv->pending_listen_freq = 0; |
| } |
| } |
| #endif /* CONFIG_P2P */ |
| |
| 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_dri
|