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:
parent
6d5ce9fc90
commit
7d23e971f0
7 changed files with 390 additions and 0 deletions
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ OBJS += crc32.o
|
|||
OBJS += ccmp.o
|
||||
OBJS += tkip.o
|
||||
OBJS += ctrl.o
|
||||
OBJS += inject.o
|
||||
|
||||
LIBS += -lpcap
|
||||
|
||||
|
|
170
wlantest/ctrl.c
170
wlantest/ctrl.c
|
@ -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
80
wlantest/inject.c
Normal 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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue