wlantest: Add preliminary infrastructure for injecting frames

This adds new commands for wlantest_cli to request wlantest to
inject frames. This version can only send out Authentication
frames and unprotected SA Query Request frames, but there is
now place to add more frames and encryption with future commits.
This commit is contained in:
Jouni Malinen 2010-11-19 00:35:13 +02:00
parent 6d5ce9fc90
commit 7d23e971f0
7 changed files with 390 additions and 0 deletions

View File

@ -459,6 +459,11 @@ static inline int is_zero_ether_addr(const u8 *a)
return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
}
static inline int is_broadcast_ether_addr(const u8 *a)
{
return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
}
#include "wpa_debug.h"

View File

@ -62,6 +62,7 @@ OBJS += crc32.o
OBJS += ccmp.o
OBJS += tkip.o
OBJS += ctrl.o
OBJS += inject.o
LIBS += -lpcap

View File

@ -17,6 +17,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "wlantest.h"
#include "wlantest_ctrl.h"
@ -49,6 +50,29 @@ static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
}
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_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
u32 val)
{
@ -330,6 +354,149 @@ static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
}
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_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;
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_get(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_get(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_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_read(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wlantest *wt = eloop_ctx;
@ -389,6 +556,9 @@ static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
case WLANTEST_CTRL_GET_BSS_COUNTER:
ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
break;
case WLANTEST_CTRL_INJECT:
ctrl_inject(wt, sock, buf + 4, len - 4);
break;
default:
ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
break;

80
wlantest/inject.c Normal file
View File

@ -0,0 +1,80 @@
/*
* wlantest frame injection
* 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 "utils/common.h"
#include "wlantest.h"
static int inject_frame(int s, const void *data, size_t len)
{
#define IEEE80211_RADIOTAP_F_FRAG 0x08
unsigned char rtap_hdr[] = {
0x00, 0x00, /* radiotap version */
0x0e, 0x00, /* radiotap length */
0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
0x00, /* padding */
0x00, 0x00, /* RX and TX flags to indicate that */
0x00, 0x00, /* this is the injected frame directly */
};
struct iovec iov[2] = {
{
.iov_base = &rtap_hdr,
.iov_len = sizeof(rtap_hdr),
},
{
.iov_base = (void *) data,
.iov_len = len,
}
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = iov,
.msg_iovlen = 2,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
int ret;
ret = sendmsg(s, &msg, 0);
if (ret < 0)
perror("sendmsg");
return ret;
}
int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, u8 *frame, size_t len,
enum wlantest_inject_protection prot)
{
int ret;
wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len);
if (wt->monitor_sock < 0) {
wpa_printf(MSG_INFO, "Cannot inject frames when monitor "
"interface is not in use");
return -1;
}
/* TODO: encrypt if needed */
if (prot != WLANTEST_INJECT_UNPROTECTED)
return -1;
ret = inject_frame(wt->monitor_sock, frame, len);
return (ret < 0) ? -1 : 0;
}

View File

@ -170,4 +170,8 @@ void tkip_get_pn(u8 *pn, const u8 *data);
int ctrl_init(struct wlantest *wt);
void ctrl_deinit(struct wlantest *wt);
int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
struct wlantest_sta *sta, u8 *frame, size_t len,
enum wlantest_inject_protection prot);
#endif /* WLANTEST_H */

View File

@ -429,6 +429,107 @@ static int cmd_get_bss_counter(int s, int argc, char *argv[])
}
struct inject_frames {
const char *name;
enum wlantest_inject_frame frame;
};
static const struct inject_frames inject_frames[] = {
{ "auth", WLANTEST_FRAME_AUTH },
{ "assocreq", WLANTEST_FRAME_ASSOCREQ },
{ "reassocreq", WLANTEST_FRAME_REASSOCREQ },
{ "deauth", WLANTEST_FRAME_DEAUTH },
{ "disassoc", WLANTEST_FRAME_DISASSOC },
{ "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
{ NULL, 0 }
};
static int cmd_inject(int s, int argc, char *argv[])
{
u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
u8 buf[100], *end, *pos;
int rlen, i;
enum wlantest_inject_protection prot;
/* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
if (argc < 5) {
printf("inject needs five arguments: frame, protection, "
"sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
return -1;
}
pos = buf;
end = buf + sizeof(buf);
WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
pos += 4;
for (i = 0; inject_frames[i].name; i++) {
if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
break;
}
if (inject_frames[i].name == NULL) {
printf("Unknown inject frame '%s'\n", argv[0]);
printf("Frames:");
for (i = 0; inject_frames[i].name; i++)
printf(" %s", inject_frames[i].name);
printf("\n");
return -1;
}
pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
inject_frames[i].frame);
if (os_strcasecmp(argv[1], "normal") == 0)
prot = WLANTEST_INJECT_NORMAL;
else if (os_strcasecmp(argv[1], "protected") == 0)
prot = WLANTEST_INJECT_PROTECTED;
else if (os_strcasecmp(argv[1], "unprotected") == 0)
prot = WLANTEST_INJECT_UNPROTECTED;
else if (os_strcasecmp(argv[1], "incorrect") == 0)
prot = WLANTEST_INJECT_INCORRECT_KEY;
else {
printf("Unknown protection type '%s'\n", argv[1]);
printf("Protection types: normal protected unprotected "
"incorrect\n");
return -1;
}
pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
if (os_strcasecmp(argv[2], "ap") == 0) {
pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
1);
} else if (os_strcasecmp(argv[2], "sta") == 0) {
pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
0);
} else {
printf("Unknown sender '%s'\n", argv[2]);
printf("Sender types: ap sta\n");
return -1;
}
pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
if (hwaddr_aton(argv[3], pos) < 0) {
printf("Invalid BSSID '%s'\n", argv[3]);
return -1;
}
pos += ETH_ALEN;
pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
if (hwaddr_aton(argv[4], pos) < 0) {
printf("Invalid STA '%s'\n", argv[4]);
return -1;
}
pos += ETH_ALEN;
rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
if (rlen < 0)
return -1;
printf("OK\n");
return 0;
}
struct wlantest_cli_cmd {
const char *cmd;
int (*handler)(int s, int argc, char *argv[]);
@ -449,6 +550,8 @@ static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
"<counter> <BSSID> <STA> = get STA counter value" },
{ "get_bss_counter", cmd_get_bss_counter,
"<counter> <BSSID> = get BSS counter value" },
{ "inject", cmd_inject,
"<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>" },
{ NULL, NULL, NULL }
};

View File

@ -33,6 +33,7 @@ enum wlantest_ctrl_cmd {
WLANTEST_CTRL_CLEAR_BSS_COUNTERS,
WLANTEST_CTRL_GET_STA_COUNTER,
WLANTEST_CTRL_GET_BSS_COUNTER,
WLANTEST_CTRL_INJECT,
};
enum wlantest_ctrl_attr {
@ -41,6 +42,9 @@ enum wlantest_ctrl_attr {
WLANTEST_ATTR_STA_COUNTER,
WLANTEST_ATTR_BSS_COUNTER,
WLANTEST_ATTR_COUNTER,
WLANTEST_ATTR_INJECT_FRAME,
WLANTEST_ATTR_INJECT_SENDER_AP,
WLANTEST_ATTR_INJECT_PROTECTION,
};
enum wlantest_bss_counter {
@ -75,4 +79,27 @@ enum wlantest_sta_counter {
NUM_WLANTEST_STA_COUNTER
};
enum wlantest_inject_frame {
WLANTEST_FRAME_AUTH,
WLANTEST_FRAME_ASSOCREQ,
WLANTEST_FRAME_REASSOCREQ,
WLANTEST_FRAME_DEAUTH,
WLANTEST_FRAME_DISASSOC,
WLANTEST_FRAME_SAQUERYREQ,
};
/**
* enum wlantest_inject_protection - WLANTEST_CTRL_INJECT protection
* @WLANTEST_INJECT_NORMAL: Use normal rules (protect if key is set)
* @WLANTEST_INJECT_PROTECTED: Force protection (fail if not possible)
* @WLANTEST_INJECT_UNPROTECTED: Force unprotected
* @WLANTEST_INJECT_INCORRECT_KEY: Force protection with incorrect key
*/
enum wlantest_inject_protection {
WLANTEST_INJECT_NORMAL,
WLANTEST_INJECT_PROTECTED,
WLANTEST_INJECT_UNPROTECTED,
WLANTEST_INJECT_INCORRECT_KEY,
};
#endif /* WLANTEST_CTRL_H */