hostap/wlantest/ctrl.c

1331 lines
33 KiB
C
Raw Normal View History

/*
* 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;
}