hostap/wlantest/ctrl.c
Jouni Malinen 0d2e395d74 wlantest: Maintain only a single entry for an active direct link
The TDLS link itself is bidirectional, but there is explicit
initiator/responder roles. Remove the other direction of the link if it
exists when processing TDLS Setup Confirm to make sure that the link
counters are stored for the current TDLS entery.

This is also changing the control interface search for TDLS counters
to require initiator/responder addresses in the correct order instead
of matching entries regardless of the role.
2011-01-24 14:34:45 +02:00

1331 lines
33 KiB
C

/*
* wlantest control interface
* Copyright (c) 2010, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "utils/includes.h"
#include <sys/un.h>
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/defs.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "wlantest.h"
#include "wlantest_ctrl.h"
static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
size_t *len)
{
u8 *pos = buf;
while (pos + 8 <= buf + buflen) {
enum wlantest_ctrl_attr a;
size_t alen;
a = WPA_GET_BE32(pos);
pos += 4;
alen = WPA_GET_BE32(pos);
pos += 4;
if (pos + alen > buf + buflen) {
wpa_printf(MSG_DEBUG, "Invalid control message "
"attribute");
return NULL;
}
if (a == attr) {
*len = alen;
return pos;
}
pos += alen;
}
return NULL;
}
static u8 * attr_get_macaddr(u8 *buf, size_t buflen,
enum wlantest_ctrl_attr attr)
{
u8 *addr;
size_t addr_len;
addr = attr_get(buf, buflen, attr, &addr_len);
if (addr && addr_len != ETH_ALEN)
addr = NULL;
return addr;
}
static int attr_get_int(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr)
{
u8 *pos;
size_t len;
pos = attr_get(buf, buflen, attr, &len);
if (pos == NULL || len != 4)
return -1;
return WPA_GET_BE32(pos);
}
static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
const char *str)
{
size_t len = os_strlen(str);
if (pos == NULL || end - pos < 8 + len)
return NULL;
WPA_PUT_BE32(pos, attr);
pos += 4;
WPA_PUT_BE32(pos, len);
pos += 4;
os_memcpy(pos, str, len);
pos += len;
return pos;
}
static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
u32 val)
{
if (pos == NULL || end - pos < 12)
return NULL;
WPA_PUT_BE32(pos, attr);
pos += 4;
WPA_PUT_BE32(pos, 4);
pos += 4;
WPA_PUT_BE32(pos, val);
pos += 4;
return pos;
}
static void ctrl_disconnect(struct wlantest *wt, int sock)
{
int i;
wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d",
sock);
for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
if (wt->ctrl_socks[i] == sock) {
close(wt->ctrl_socks[i]);
eloop_unregister_read_sock(wt->ctrl_socks[i]);
wt->ctrl_socks[i] = -1;
break;
}
}
}
static void ctrl_send(struct wlantest *wt, int sock, const u8 *buf,
size_t len)
{
if (send(sock, buf, len, 0) < 0) {
wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno));
ctrl_disconnect(wt, sock);
}
}
static void ctrl_send_simple(struct wlantest *wt, int sock,
enum wlantest_ctrl_cmd cmd)
{
u8 buf[4];
WPA_PUT_BE32(buf, cmd);
ctrl_send(wt, sock, buf, sizeof(buf));
}
static struct wlantest_bss * ctrl_get_bss(struct wlantest *wt, int sock,
u8 *cmd, size_t clen)
{
struct wlantest_bss *bss;
u8 *pos;
size_t len;
pos = attr_get(cmd, clen, WLANTEST_ATTR_BSSID, &len);
if (pos == NULL || len != ETH_ALEN) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return NULL;
}
bss = bss_find(wt, pos);
if (bss == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return NULL;
}
return bss;
}
static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock,
u8 *cmd, size_t clen,
struct wlantest_bss *bss)
{
struct wlantest_sta *sta;
u8 *pos;
size_t len;
if (bss == NULL)
return NULL;
pos = attr_get(cmd, clen, WLANTEST_ATTR_STA_ADDR, &len);
if (pos == NULL || len != ETH_ALEN) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return NULL;
}
sta = sta_find(bss, pos);
if (sta == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return NULL;
}
return sta;
}
static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock,
u8 *cmd, size_t clen,
struct wlantest_bss *bss)
{
struct wlantest_sta *sta;
u8 *pos;
size_t len;
if (bss == NULL)
return NULL;
pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len);
if (pos == NULL || len != ETH_ALEN) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return NULL;
}
sta = sta_find(bss, pos);
if (sta == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return NULL;
}
return sta;
}
static void ctrl_list_bss(struct wlantest *wt, int sock)
{
u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
struct wlantest_bss *bss;
pos = buf;
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
pos += 4;
len = pos; /* to be filled */
pos += 4;
dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
break;
os_memcpy(pos, bss->bssid, ETH_ALEN);
pos += ETH_ALEN;
}
WPA_PUT_BE32(len, pos - len - 4);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_list_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
{
u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
struct wlantest_bss *bss;
struct wlantest_sta *sta;
bss = ctrl_get_bss(wt, sock, cmd, clen);
if (bss == NULL)
return;
pos = buf;
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
pos += 4;
len = pos; /* to be filled */
pos += 4;
dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
break;
os_memcpy(pos, sta->addr, ETH_ALEN);
pos += ETH_ALEN;
}
WPA_PUT_BE32(len, pos - len - 4);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_flush(struct wlantest *wt, int sock)
{
wpa_printf(MSG_DEBUG, "Drop all collected BSS data");
bss_flush(wt);
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
}
static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
struct wlantest_bss *bss;
struct wlantest_sta *sta;
bss = ctrl_get_bss(wt, sock, cmd, clen);
sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
if (sta == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
os_memset(sta->counters, 0, sizeof(sta->counters));
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
}
static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
struct wlantest_bss *bss;
bss = ctrl_get_bss(wt, sock, cmd, clen);
if (bss == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
os_memset(bss->counters, 0, sizeof(bss->counters));
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
}
static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
struct wlantest_bss *bss;
struct wlantest_sta *sta;
struct wlantest_sta *sta2;
struct wlantest_tdls *tdls;
bss = ctrl_get_bss(wt, sock, cmd, clen);
sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
if (sta == NULL || sta2 == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
if ((tdls->init == sta && tdls->resp == sta2) ||
(tdls->init == sta2 && tdls->resp == sta))
os_memset(tdls->counters, 0, sizeof(tdls->counters));
}
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
}
static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
u8 *addr;
size_t addr_len;
struct wlantest_bss *bss;
struct wlantest_sta *sta;
u32 counter;
u8 buf[4 + 12], *end, *pos;
bss = ctrl_get_bss(wt, sock, cmd, clen);
sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
if (sta == NULL)
return;
addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_COUNTER, &addr_len);
if (addr == NULL || addr_len != 4) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
counter = WPA_GET_BE32(addr);
if (counter >= NUM_WLANTEST_STA_COUNTER) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
sta->counters[counter]);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
u8 *addr;
size_t addr_len;
struct wlantest_bss *bss;
u32 counter;
u8 buf[4 + 12], *end, *pos;
bss = ctrl_get_bss(wt, sock, cmd, clen);
if (bss == NULL)
return;
addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_COUNTER, &addr_len);
if (addr == NULL || addr_len != 4) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
counter = WPA_GET_BE32(addr);
if (counter >= NUM_WLANTEST_BSS_COUNTER) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
bss->counters[counter]);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
u8 *addr;
size_t addr_len;
struct wlantest_bss *bss;
struct wlantest_sta *sta;
struct wlantest_sta *sta2;
struct wlantest_tdls *tdls;
u32 counter;
u8 buf[4 + 12], *end, *pos;
int found = 0;
bss = ctrl_get_bss(wt, sock, cmd, clen);
sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
if (sta == NULL || sta2 == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len);
if (addr == NULL || addr_len != 4) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
counter = WPA_GET_BE32(addr);
if (counter >= NUM_WLANTEST_TDLS_COUNTER) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
if (tdls->init == sta && tdls->resp == sta2) {
found = 1;
break;
}
}
if (!found) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
tdls->counters[counter]);
ctrl_send(wt, sock, buf, pos - buf);
}
static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt,
struct wlantest_bss *bss, struct wlantest_sta *sta,
int sender_ap, int stype)
{
os_memset(mgmt, 0, 24);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
if (sender_ap) {
if (sta)
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
else
os_memset(mgmt->da, 0xff, ETH_ALEN);
os_memcpy(mgmt->sa, bss->bssid, ETH_ALEN);
} else {
os_memcpy(mgmt->da, bss->bssid, ETH_ALEN);
os_memcpy(mgmt->sa, sta->addr, ETH_ALEN);
}
os_memcpy(mgmt->bssid, bss->bssid, ETH_ALEN);
}
static int ctrl_inject_auth(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
struct ieee80211_mgmt mgmt;
if (prot != WLANTEST_INJECT_NORMAL &&
prot != WLANTEST_INJECT_UNPROTECTED)
return -1; /* Authentication frame is never protected */
if (sta == NULL)
return -1; /* No broadcast Authentication frames */
if (sender_ap)
wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
MAC2STR(bss->bssid), MAC2STR(sta->addr));
else
wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
MAC2STR(sta->addr), MAC2STR(bss->bssid));
build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_AUTH);
mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
mgmt.u.auth.auth_transaction = host_to_le16(1);
mgmt.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 6,
WLANTEST_INJECT_UNPROTECTED);
}
static int ctrl_inject_assocreq(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
u8 *buf;
struct ieee80211_mgmt *mgmt;
int ret;
if (prot != WLANTEST_INJECT_NORMAL &&
prot != WLANTEST_INJECT_UNPROTECTED)
return -1; /* Association Request frame is never protected */
if (sta == NULL)
return -1; /* No broadcast Association Request frames */
if (sender_ap)
return -1; /* No Association Request frame sent by AP */
if (sta->assocreq_ies == NULL) {
wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
"Request available for " MACSTR,
MAC2STR(sta->addr));
return -1;
}
wpa_printf(MSG_INFO, "INJECT: AssocReq " MACSTR " -> " MACSTR,
MAC2STR(sta->addr), MAC2STR(bss->bssid));
buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
if (buf == NULL)
return -1;
mgmt = (struct ieee80211_mgmt *) buf;
build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = host_to_le16(sta->assocreq_capab_info);
mgmt->u.assoc_req.listen_interval =
host_to_le16(sta->assocreq_listen_int);
os_memcpy(mgmt->u.assoc_req.variable, sta->assocreq_ies,
sta->assocreq_ies_len);
ret = wlantest_inject(wt, bss, sta, buf,
24 + 4 + sta->assocreq_ies_len,
WLANTEST_INJECT_UNPROTECTED);
os_free(buf);
return ret;
}
static int ctrl_inject_reassocreq(struct wlantest *wt,
struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
u8 *buf;
struct ieee80211_mgmt *mgmt;
int ret;
if (prot != WLANTEST_INJECT_NORMAL &&
prot != WLANTEST_INJECT_UNPROTECTED)
return -1; /* Reassociation Request frame is never protected */
if (sta == NULL)
return -1; /* No broadcast Reassociation Request frames */
if (sender_ap)
return -1; /* No Reassociation Request frame sent by AP */
if (sta->assocreq_ies == NULL) {
wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
"Request available for " MACSTR,
MAC2STR(sta->addr));
return -1;
}
wpa_printf(MSG_INFO, "INJECT: ReassocReq " MACSTR " -> " MACSTR,
MAC2STR(sta->addr), MAC2STR(bss->bssid));
buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
if (buf == NULL)
return -1;
mgmt = (struct ieee80211_mgmt *) buf;
build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_REASSOC_REQ);
mgmt->u.reassoc_req.capab_info =
host_to_le16(sta->assocreq_capab_info);
mgmt->u.reassoc_req.listen_interval =
host_to_le16(sta->assocreq_listen_int);
os_memcpy(mgmt->u.reassoc_req.current_ap, bss->bssid, ETH_ALEN);
os_memcpy(mgmt->u.reassoc_req.variable, sta->assocreq_ies,
sta->assocreq_ies_len);
ret = wlantest_inject(wt, bss, sta, buf,
24 + 10 + sta->assocreq_ies_len,
WLANTEST_INJECT_UNPROTECTED);
os_free(buf);
return ret;
}
static int ctrl_inject_deauth(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
struct ieee80211_mgmt mgmt;
if (sender_ap) {
if (sta)
wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> "
MACSTR,
MAC2STR(bss->bssid), MAC2STR(sta->addr));
else
wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR
" -> broadcast", MAC2STR(bss->bssid));
} else
wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR,
MAC2STR(sta->addr), MAC2STR(bss->bssid));
build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DEAUTH);
mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
}
static int ctrl_inject_disassoc(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
struct ieee80211_mgmt mgmt;
if (sender_ap) {
if (sta)
wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> "
MACSTR,
MAC2STR(bss->bssid), MAC2STR(sta->addr));
else
wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR
" -> broadcast", MAC2STR(bss->bssid));
} else
wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR,
MAC2STR(sta->addr), MAC2STR(bss->bssid));
build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DISASSOC);
mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
}
static int ctrl_inject_saqueryreq(struct wlantest *wt,
struct wlantest_bss *bss,
struct wlantest_sta *sta, int sender_ap,
enum wlantest_inject_protection prot)
{
struct ieee80211_mgmt mgmt;
if (sta == NULL)
return -1; /* No broadcast SA Query frames */
if (sender_ap)
wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr));
else
wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid));
build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ACTION);
mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
mgmt.u.action.u.sa_query_req.trans_id[0] = 0x12;
mgmt.u.action.u.sa_query_req.trans_id[1] = 0x34;
os_memcpy(sender_ap ? sta->ap_sa_query_tr : sta->sta_sa_query_tr,
mgmt.u.action.u.sa_query_req.trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 4, prot);
}
static void ctrl_inject(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
{
u8 *bssid, *sta_addr;
struct wlantest_bss *bss;
struct wlantest_sta *sta;
int frame, sender_ap, prot;
int ret = 0;
bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
sta_addr = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_STA_ADDR);
frame = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_FRAME);
sender_ap = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_SENDER_AP);
if (sender_ap < 0)
sender_ap = 0;
prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
if (bssid == NULL || sta_addr == NULL || frame < 0 || prot < 0) {
wpa_printf(MSG_INFO, "Invalid inject command parameters");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
bss = bss_find(wt, bssid);
if (bss == NULL) {
wpa_printf(MSG_INFO, "BSS not found for inject command");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
if (is_broadcast_ether_addr(sta_addr)) {
if (!sender_ap) {
wpa_printf(MSG_INFO, "Invalid broadcast inject "
"command without sender_ap set");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
} sta = NULL;
} else {
sta = sta_find(bss, sta_addr);
if (sta == NULL) {
wpa_printf(MSG_INFO, "Station not found for inject "
"command");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
}
switch (frame) {
case WLANTEST_FRAME_AUTH:
ret = ctrl_inject_auth(wt, bss, sta, sender_ap, prot);
break;
case WLANTEST_FRAME_ASSOCREQ:
ret = ctrl_inject_assocreq(wt, bss, sta, sender_ap, prot);
break;
case WLANTEST_FRAME_REASSOCREQ:
ret = ctrl_inject_reassocreq(wt, bss, sta, sender_ap, prot);
break;
case WLANTEST_FRAME_DEAUTH:
ret = ctrl_inject_deauth(wt, bss, sta, sender_ap, prot);
break;
case WLANTEST_FRAME_DISASSOC:
ret = ctrl_inject_disassoc(wt, bss, sta, sender_ap, prot);
break;
case WLANTEST_FRAME_SAQUERYREQ:
ret = ctrl_inject_saqueryreq(wt, bss, sta, sender_ap, prot);
break;
default:
wpa_printf(MSG_INFO, "Unsupported inject command frame %d",
frame);
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
if (ret)
wpa_printf(MSG_INFO, "Failed to inject frame");
else
wpa_printf(MSG_INFO, "Frame injected successfully");
ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
WLANTEST_CTRL_FAILURE);
}
static void ctrl_version(struct wlantest *wt, int sock)
{
u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos;
pos = buf;
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_str(pos, buf + sizeof(buf), WLANTEST_ATTR_VERSION,
VERSION_STR);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_add_passphrase(struct wlantest *wt, int sock, u8 *cmd,
size_t clen)
{
u8 *passphrase;
size_t len;
struct wlantest_passphrase *p, *pa;
u8 *bssid;
passphrase = attr_get(cmd, clen, WLANTEST_ATTR_PASSPHRASE, &len);
if (passphrase == NULL || len < 8 || len > 63) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
p = os_zalloc(sizeof(*p));
if (p == NULL) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
os_memcpy(p->passphrase, passphrase, len);
wpa_printf(MSG_INFO, "Add passphrase '%s'", p->passphrase);
bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
if (bssid) {
os_memcpy(p->bssid, bssid, ETH_ALEN);
wpa_printf(MSG_INFO, "Limit passphrase for BSSID " MACSTR,
MAC2STR(p->bssid));
}
dl_list_for_each(pa, &wt->passphrase, struct wlantest_passphrase, list)
{
if (os_strcmp(p->passphrase, pa->passphrase) == 0 &&
os_memcmp(p->bssid, pa->bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_INFO, "Passphrase was already known");
os_free(p);
p = NULL;
break;
}
}
if (p) {
struct wlantest_bss *bss;
dl_list_add(&wt->passphrase, &p->list);
dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
if (bssid &&
os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
continue;
bss_add_pmk_from_passphrase(bss, p->passphrase);
}
}
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
}
static void info_print_proto(char *buf, size_t len, int proto)
{
char *pos, *end;
if (proto == 0) {
os_snprintf(buf, len, "OPEN");
return;
}
pos = buf;
end = buf + len;
if (proto & WPA_PROTO_WPA)
pos += os_snprintf(pos, end - pos, "%sWPA",
pos == buf ? "" : " ");
if (proto & WPA_PROTO_RSN)
pos += os_snprintf(pos, end - pos, "%sWPA2",
pos == buf ? "" : " ");
}
static void info_print_cipher(char *buf, size_t len, int cipher)
{
char *pos, *end;
if (cipher == 0) {
os_snprintf(buf, len, "N/A");
return;
}
pos = buf;
end = buf + len;
if (cipher & WPA_CIPHER_NONE)
pos += os_snprintf(pos, end - pos, "%sNONE",
pos == buf ? "" : " ");
if (cipher & WPA_CIPHER_WEP40)
pos += os_snprintf(pos, end - pos, "%sWEP40",
pos == buf ? "" : " ");
if (cipher & WPA_CIPHER_WEP104)
pos += os_snprintf(pos, end - pos, "%sWEP104",
pos == buf ? "" : " ");
if (cipher & WPA_CIPHER_TKIP)
pos += os_snprintf(pos, end - pos, "%sTKIP",
pos == buf ? "" : " ");
if (cipher & WPA_CIPHER_CCMP)
pos += os_snprintf(pos, end - pos, "%sCCMP",
pos == buf ? "" : " ");
if (cipher & WPA_CIPHER_AES_128_CMAC)
pos += os_snprintf(pos, end - pos, "%sBIP",
pos == buf ? "" : " ");
}
static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt)
{
char *pos, *end;
if (key_mgmt == 0) {
os_snprintf(buf, len, "N/A");
return;
}
pos = buf;
end = buf + len;
if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
pos += os_snprintf(pos, end - pos, "%sEAP",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_PSK)
pos += os_snprintf(pos, end - pos, "%sPSK",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_WPA_NONE)
pos += os_snprintf(pos, end - pos, "%sWPA-NONE",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
pos += os_snprintf(pos, end - pos, "%sFT-EAP",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
pos += os_snprintf(pos, end - pos, "%sFT-PSK",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
pos += os_snprintf(pos, end - pos, "%sEAP-SHA256",
pos == buf ? "" : " ");
if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
pos += os_snprintf(pos, end - pos, "%sPSK-SHA256",
pos == buf ? "" : " ");
}
static void info_print_rsn_capab(char *buf, size_t len, int capab)
{
char *pos, *end;
pos = buf;
end = buf + len;
if (capab & WPA_CAPABILITY_PREAUTH)
pos += os_snprintf(pos, end - pos, "%sPREAUTH",
pos == buf ? "" : " ");
if (capab & WPA_CAPABILITY_NO_PAIRWISE)
pos += os_snprintf(pos, end - pos, "%sNO_PAIRWISE",
pos == buf ? "" : " ");
if (capab & WPA_CAPABILITY_MFPR)
pos += os_snprintf(pos, end - pos, "%sMFPR",
pos == buf ? "" : " ");
if (capab & WPA_CAPABILITY_MFPC)
pos += os_snprintf(pos, end - pos, "%sMFPC",
pos == buf ? "" : " ");
if (capab & WPA_CAPABILITY_PEERKEY_ENABLED)
pos += os_snprintf(pos, end - pos, "%sPEERKEY",
pos == buf ? "" : " ");
}
static void info_print_state(char *buf, size_t len, int state)
{
switch (state) {
case STATE1:
os_strlcpy(buf, "NOT-AUTH", len);
break;
case STATE2:
os_strlcpy(buf, "AUTH", len);
break;
case STATE3:
os_strlcpy(buf, "AUTH+ASSOC", len);
break;
}
}
static void ctrl_info_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
{
u8 *addr;
size_t addr_len;
struct wlantest_bss *bss;
struct wlantest_sta *sta;
enum wlantest_sta_info info;
u8 buf[4 + 108], *end, *pos;
char resp[100];
bss = ctrl_get_bss(wt, sock, cmd, clen);
sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
if (sta == NULL)
return;
addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_INFO, &addr_len);
if (addr == NULL || addr_len != 4) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
info = WPA_GET_BE32(addr);
resp[0] = '\0';
switch (info) {
case WLANTEST_STA_INFO_PROTO:
info_print_proto(resp, sizeof(resp), sta->proto);
break;
case WLANTEST_STA_INFO_PAIRWISE:
info_print_cipher(resp, sizeof(resp), sta->pairwise_cipher);
break;
case WLANTEST_STA_INFO_KEY_MGMT:
info_print_key_mgmt(resp, sizeof(resp), sta->key_mgmt);
break;
case WLANTEST_STA_INFO_RSN_CAPAB:
info_print_rsn_capab(resp, sizeof(resp), sta->rsn_capab);
break;
case WLANTEST_STA_INFO_STATE:
info_print_state(resp, sizeof(resp), sta->state);
break;
default:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_info_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
{
u8 *addr;
size_t addr_len;
struct wlantest_bss *bss;
enum wlantest_bss_info info;
u8 buf[4 + 108], *end, *pos;
char resp[100];
bss = ctrl_get_bss(wt, sock, cmd, clen);
if (bss == NULL)
return;
addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_INFO, &addr_len);
if (addr == NULL || addr_len != 4) {
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
info = WPA_GET_BE32(addr);
resp[0] = '\0';
switch (info) {
case WLANTEST_BSS_INFO_PROTO:
info_print_proto(resp, sizeof(resp), bss->proto);
break;
case WLANTEST_BSS_INFO_PAIRWISE:
info_print_cipher(resp, sizeof(resp), bss->pairwise_cipher);
break;
case WLANTEST_BSS_INFO_GROUP:
info_print_cipher(resp, sizeof(resp), bss->group_cipher);
break;
case WLANTEST_BSS_INFO_GROUP_MGMT:
info_print_cipher(resp, sizeof(resp), bss->mgmt_group_cipher);
break;
case WLANTEST_BSS_INFO_KEY_MGMT:
info_print_key_mgmt(resp, sizeof(resp), bss->key_mgmt);
break;
case WLANTEST_BSS_INFO_RSN_CAPAB:
info_print_rsn_capab(resp, sizeof(resp), bss->rsn_capab);
break;
default:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
pos += 4;
pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
ctrl_send(wt, sock, buf, pos - buf);
}
static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
{
struct wlantest_bss *bss;
struct wlantest_sta *sta;
u8 *bssid, *sta_addr;
int prot;
u8 *frame;
size_t frame_len;
int ret = 0;
struct ieee80211_hdr *hdr;
u16 fc;
frame = attr_get(cmd, clen, WLANTEST_ATTR_FRAME, &frame_len);
prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
if (frame == NULL || frame_len < 24 || prot < 0) {
wpa_printf(MSG_INFO, "Invalid send command parameters");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
return;
}
hdr = (struct ieee80211_hdr *) frame;
fc = le_to_host16(hdr->frame_control);
switch (WLAN_FC_GET_TYPE(fc)) {
case WLAN_FC_TYPE_MGMT:
bssid = hdr->addr3;
if (os_memcmp(hdr->addr2, hdr->addr3, ETH_ALEN) == 0)
sta_addr = hdr->addr1;
else
sta_addr = hdr->addr2;
break;
case WLAN_FC_TYPE_DATA:
switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
case 0:
bssid = hdr->addr3;
sta_addr = hdr->addr2;
break;
case WLAN_FC_TODS:
bssid = hdr->addr1;
sta_addr = hdr->addr2;
break;
case WLAN_FC_FROMDS:
bssid = hdr->addr2;
sta_addr = hdr->addr1;
break;
default:
wpa_printf(MSG_INFO, "Unsupported inject frame");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
break;
default:
wpa_printf(MSG_INFO, "Unsupported inject frame");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
bss = bss_find(wt, bssid);
if (bss == NULL) {
wpa_printf(MSG_INFO, "Unknown BSSID");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
sta = sta_find(bss, sta_addr);
if (sta == NULL) {
wpa_printf(MSG_INFO, "Unknown STA address");
ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
return;
}
ret = wlantest_inject(wt, bss, sta, frame, frame_len, prot);
if (ret)
wpa_printf(MSG_INFO, "Failed to inject frame");
else
wpa_printf(MSG_INFO, "Frame injected successfully");
ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
WLANTEST_CTRL_FAILURE);
}
static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wlantest *wt = eloop_ctx;
u8 buf[WLANTEST_CTRL_MAX_CMD_LEN];
int len;
enum wlantest_ctrl_cmd cmd;
wpa_printf(MSG_EXCESSIVE, "New control interface message from %d",
sock);
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) {
wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno));
ctrl_disconnect(wt, sock);
return;
}
if (len == 0) {
ctrl_disconnect(wt, sock);
return;
}
if (len < 4) {
wpa_printf(MSG_INFO, "Too short control interface command "
"from %d", sock);
ctrl_disconnect(wt, sock);
return;
}
cmd = WPA_GET_BE32(buf);
wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d",
cmd, sock);
switch (cmd) {
case WLANTEST_CTRL_PING:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
break;
case WLANTEST_CTRL_TERMINATE:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
eloop_terminate();
break;
case WLANTEST_CTRL_LIST_BSS:
ctrl_list_bss(wt, sock);
break;
case WLANTEST_CTRL_LIST_STA:
ctrl_list_sta(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_FLUSH:
ctrl_flush(wt, sock);
break;
case WLANTEST_CTRL_CLEAR_STA_COUNTERS:
ctrl_clear_sta_counters(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_CLEAR_BSS_COUNTERS:
ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS:
ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_GET_STA_COUNTER:
ctrl_get_sta_counter(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_GET_BSS_COUNTER:
ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_GET_TDLS_COUNTER:
ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_INJECT:
ctrl_inject(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_VERSION:
ctrl_version(wt, sock);
break;
case WLANTEST_CTRL_ADD_PASSPHRASE:
ctrl_add_passphrase(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_INFO_STA:
ctrl_info_sta(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_INFO_BSS:
ctrl_info_bss(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_SEND:
ctrl_send_(wt, sock, buf + 4, len - 4);
break;
default:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
break;
}
}
static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wlantest *wt = eloop_ctx;
int conn, i;
conn = accept(sock, NULL, NULL);
if (conn < 0) {
wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno));
return;
}
wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn);
for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
if (wt->ctrl_socks[i] < 0)
break;
}
if (i == MAX_CTRL_CONNECTIONS) {
wpa_printf(MSG_INFO, "No room for new control connection");
close(conn);
return;
}
wt->ctrl_socks[i] = conn;
eloop_register_read_sock(conn, ctrl_read, wt, NULL);
}
int ctrl_init(struct wlantest *wt)
{
struct sockaddr_un addr;
wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (wt->ctrl_sock < 0) {
wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
return -1;
}
os_memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
sizeof(addr.sun_path) - 1);
if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
close(wt->ctrl_sock);
wt->ctrl_sock = -1;
return -1;
}
if (listen(wt->ctrl_sock, 5) < 0) {
wpa_printf(MSG_ERROR, "listen: %s", strerror(errno));
close(wt->ctrl_sock);
wt->ctrl_sock = -1;
return -1;
}
if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) {
close(wt->ctrl_sock);
wt->ctrl_sock = -1;
return -1;
}
return 0;
}
void ctrl_deinit(struct wlantest *wt)
{
int i;
if (wt->ctrl_sock < 0)
return;
for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
if (wt->ctrl_socks[i] >= 0) {
close(wt->ctrl_socks[i]);
eloop_unregister_read_sock(wt->ctrl_socks[i]);
wt->ctrl_socks[i] = -1;
}
}
eloop_unregister_read_sock(wt->ctrl_sock);
close(wt->ctrl_sock);
wt->ctrl_sock = -1;
}