802.11 packet injection test tool

Submitted by:	Andrea Bittau <a.bittau@cs.ucl.ac.uk>
This commit is contained in:
Sam Leffler 2006-08-05 05:13:31 +00:00
parent b86de580a1
commit 7c37a38d03
3 changed files with 879 additions and 0 deletions

View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG= wlaninject
BINDIR= /usr/local/bin
CFLAGS=-g
NO_MAN=
.include <bsd.prog.mk>

View File

@ -0,0 +1,82 @@
$FreeBSD$
This tool generates raw 802.11 frames. The resulting frame will
depend on the capabilities of the hardware. Some hardware mangles
the 802.11 header [e.g. wi] other hardware does not [e.g. ath].
This tool does not deal with layers above 802.11. A payload from
a file must be supplied if, for example, IP data needs to be sent.
Refer to the usage for a complete list of options. Here, the most
relevant will be described. There are three types of switches:
physical layer Physical parameters for transmission
[such as iface and chan].
802.11 header Fields in the 802.11 header.
802.11 body The payload of the 802.11 frame. This includes
management information elements.
Interesting physical layer options.
-----------------------------------
-i The network interface to use to TX; defaults to ath0.
-c The channel to TX on; defaults to 1.
-N Mark transmitted frames such that no ACK is expected. In practice,
this will disable any retransmission done by the card. This
way, you are sure that a single copy of your packet will be TXed.
-V Verify that the packet you TXed did indeed fly in the air as
expected. This is done by sniffing on a second network interface
and comparing the received frame to what was sent. This option
is especially useful because some cards mangle 802.11 header
fields such as duration and fragment numbers.
-W The WME AC to use for transmission. Symbolic names are used:
ac_be, be best effort
ac_bk, bk background
ac_vi, vi video
ac_vo, vo voice
-X The rate to use for transmission; defaults to 1Mbps.
-P The txpower to use; defaults to the maximum permitted by the driver.
Interesting 802.11 header options.
----------------------------------
-t The 802.11 frame type. Symbolic names are:
mgt Management frame
ctl Control frame
data Data frame
Otherwise the numerical type must be supplied.
-s The 802.11 frame subtype. At this point, the length of the 802.11 header
will be calculated. Symbolic names are:
preq, probereq Probe Request
auth Authenticate
areq, assocreq Assocation Request
data Data
Otherwise the numerical subtype must be supplied.
-4 The 4th MAC addr used for WDS. Make sure you specify this before
-s so the header length is calculated correctly.
-l Override the length of the packet. This is useful for sending
truncated packets.
-b Specify a file which will be used as a payload inside the 802.11
frame. The length should be calculated automatically correctly.
Interesting 802.11 payload options.
-----------------------------------
This is mainly used for management frames. For data frames, -b
would typically be used.
-e Add an information element. You can supply multiple -e options,
but make sure you add them in the expected order. The format
of the information element is a list of 2 digit hex numbers.
That is, 010203... The first hexdigit is the type of the IE.
Therefore, 00616161 should correspond to the SSID IE of 'aaa'.
The length is calculated automatically---you do not need to
supply it.
-S Add an SSID IE with the data specified in ascii; e.g. -S 'aaa'
is the equivalent of -e 0061616161.
-R Add an IE of 11b supported rates.
Examples:
---------
wlaninject -t data -s data -i ral0 -V ath0 -m -n 1 -f 1
Transmit a data frame on ral0 and verify the transmission on ath0.
The frame is sent with sequence number 1, fragment number 1, and
the MoreFrag bit marked in the 802.11 header.

View File

@ -0,0 +1,789 @@
/*-
* Copyright (c) 2006, Andrea Bittau <a.bittau@cs.ucl.ac.uk>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <net/bpf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <string.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#include <net80211/ieee80211_freebsd.h>
#include <net80211/ieee80211_radiotap.h>
#include <sys/endian.h>
#include <assert.h>
void setup_if(char *dev, int chan) {
int s;
struct ifreq ifr;
unsigned int flags;
struct ifmediareq ifmr;
int *mwords;
struct ieee80211req ireq;
if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
err(1, "socket()");
/* UP & PROMISC */
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", dev);
if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
err(1, "ioctl(SIOCGIFFLAGS)");
flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
flags |= IFF_UP | IFF_PPROMISC;
ifr.ifr_flags = flags & 0xffff;
ifr.ifr_flagshigh = flags >> 16;
if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
err(1, "ioctl(SIOCSIFFLAGS)");
/* set monitor mode */
memset(&ifmr, 0, sizeof(ifmr));
snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name), "%s", dev);
if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1)
err(1, "ioctl(SIOCGIFMEDIA)");
if (ifmr.ifm_count == 0) {
printf("0 media thinggies...\n");
exit(1);
}
mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
if (!mwords)
err(1, "malloc()");
ifmr.ifm_ulist = mwords;
if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1)
err(1, "ioctl(SIOCGIFMEDIA)");
free(mwords);
ifr.ifr_media = ifmr.ifm_current | IFM_IEEE80211_MONITOR;
if (ioctl(s, SIOCSIFMEDIA, &ifr) == -1)
err(1, "ioctl(SIOCSIFMEDIA)");
/* chan */
memset(&ireq, 0, sizeof(ireq));
snprintf(ireq.i_name, sizeof(ireq.i_name), "%s", dev);
ireq.i_type = IEEE80211_IOC_CHANNEL;
ireq.i_val = chan;
if (ioctl(s, SIOCS80211, &ireq) == -1)
err(1, "ioctl(SIOCS80211)");
close(s);
}
int open_bpf(char *dev)
{
char buf[64];
int i;
int fd;
struct ifreq ifr;
unsigned int dlt = DLT_IEEE802_11_RADIO;
for (i = 0; i < 64; i++) {
sprintf(buf, "/dev/bpf%d", i);
fd = open(buf, O_RDWR);
if (fd != -1)
break;
else if (errno != EBUSY)
err(1, "open()");
}
if (fd == -1) {
printf("Can't find bpf\n");
exit(1);
}
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", dev);
if (ioctl(fd, BIOCSETIF, &ifr) == -1)
err(1, "ioctl(BIOCSETIF)");
if (ioctl(fd, BIOCSDLT, &dlt) == -1)
err(1, "ioctl(BIOCSDLT)");
i = 1;
if (ioctl(fd, BIOCIMMEDIATE, &i) == -1)
err(1, "ioctl(BIOCIMMEDIATE)");
return fd;
}
void inject(int fd, void *buf, int buflen, struct ieee80211_bpf_params *p)
{
struct iovec iov[2];
int totlen;
int rc;
iov[0].iov_base = p;
iov[0].iov_len = sizeof(*p);
iov[1].iov_base = buf;
iov[1].iov_len = buflen;
totlen = iov[0].iov_len + iov[1].iov_len;
rc = writev(fd, iov, sizeof(iov)/sizeof(struct iovec));
if (rc == -1)
err(1, "writev()");
if (rc != totlen) {
printf("Wrote only %d/%d\n", rc, totlen);
exit(1);
}
}
void usage(char *progname)
{
printf("Usage: %s <opts>\n"
"Physical:\n"
"\t-i\t<iface>\n"
"\t-c\t<chan>\n"
"\t-N\tno ack\n"
"\t-V\t<iface> [verify via iface whether packet was mangled]\n"
"\t-W\tWME AC\n"
"\t-X\ttransmit rate (Mbps)\n"
"\t-P\ttransmit power (device units)\n"
"802.11:\n"
"\t-h\tthis lame message\n"
"\t-v\t<version>\n"
"\t-t\t<type>\n"
"\t-s\t<subtype>\n"
"\t-T\tto ds\n"
"\t-F\tfrom ds\n"
"\t-m\tmore frags\n"
"\t-r\tretry\n"
"\t-p\tpower\n"
"\t-d\tmore data\n"
"\t-w\twep\n"
"\t-o\torder\n"
"\t-u\t<duration>\n"
"\t-1\t<addr 1>\n"
"\t-2\t<addr 2>\n"
"\t-3\t<addr 3>\n"
"\t-n\t<seqno>\n"
"\t-f\t<fragno>\n"
"\t-4\t<addr 4>\n"
"\t-b\t<payload file>\n"
"\t-l\t<len>\n"
"Management:\n"
"\t-e\t<info element [hex digits 010203... first is type]>\n"
"\t-S\t<SSID>\n"
"\t-a\t<algo no>\n"
"\t-A\t<transaction>\n"
"\t-C\t<status code>\n"
"\t-R\tstandard rates\n"
, progname);
exit(1);
}
int str2type(const char *type)
{
#define equal(a,b) (strcasecmp(a,b) == 0)
if (equal(type, "mgt"))
return IEEE80211_FC0_TYPE_MGT >> IEEE80211_FC0_TYPE_SHIFT;
else if (equal(type, "ctl"))
return IEEE80211_FC0_TYPE_CTL >> IEEE80211_FC0_TYPE_SHIFT;
else if (equal(type, "data"))
return IEEE80211_FC0_TYPE_DATA >> IEEE80211_FC0_TYPE_SHIFT;
return atoi(type) & 3;
#undef equal
}
int str2subtype(const char *subtype)
{
#define equal(a,b) (strcasecmp(a,b) == 0)
if (equal(subtype, "preq") || equal(subtype, "probereq"))
return IEEE80211_FC0_SUBTYPE_PROBE_REQ >>
IEEE80211_FC0_SUBTYPE_SHIFT;
else if (equal(subtype, "auth"))
return IEEE80211_FC0_SUBTYPE_AUTH >>
IEEE80211_FC0_SUBTYPE_SHIFT;
else if (equal(subtype, "areq") || equal(subtype, "assocreq"))
return IEEE80211_FC0_SUBTYPE_ASSOC_REQ >>
IEEE80211_FC0_SUBTYPE_SHIFT;
else if (equal(subtype, "data"))
return IEEE80211_FC0_SUBTYPE_DATA >>
IEEE80211_FC0_SUBTYPE_SHIFT;
return atoi(subtype) & 0xf;
#undef equal
}
void str2mac(unsigned char *mac, char *str)
{
unsigned int macf[6];
int i;
if (sscanf(str, "%x:%x:%x:%x:%x:%x",
&macf[0], &macf[1], &macf[2],
&macf[3], &macf[4], &macf[5]) != 6) {
printf("can't parse mac %s\n", str);
exit(1);
}
for (i = 0; i < 6; i++)
*mac++ = (unsigned char) macf[i];
}
int str2wmeac(const char *ac)
{
#define equal(a,b) (strcasecmp(a,b) == 0)
if (equal(ac, "ac_be") || equal(ac, "be"))
return WME_AC_BE;
if (equal(ac, "ac_bk") || equal(ac, "bk"))
return WME_AC_BK;
if (equal(ac, "ac_vi") || equal(ac, "vi"))
return WME_AC_VI;
if (equal(ac, "ac_vo") || equal(ac, "vo"))
return WME_AC_VO;
errx(1, "unknown wme access class %s", ac);
#undef equal
}
int str2rate(const char *rate)
{
switch (atoi(rate)) {
case 54: return 54*2;
case 48: return 48*2;
case 36: return 36*2;
case 24: return 24*2;
case 18: return 18*2;
case 12: return 12*2;
case 9: return 9*2;
case 6: return 6*2;
case 11: return 11*2;
case 5: return 11;
case 2: return 2*2;
case 1: return 1*2;
}
errx(1, "unknown transmit rate %s", rate);
}
const char *rate2str(int rate)
{
static char buf[30];
if (rate == 11)
return "5.5";
snprintf(buf, sizeof(buf), "%u", rate/2);
return buf;
}
int load_payload(char *fname, void *buf, int len)
{
int fd;
int rc;
if ((fd = open(fname, O_RDONLY)) == -1)
err(1, "open()");
if ((rc = read(fd, buf, len)) == -1)
err(1, "read()");
close(fd);
printf("Read %d bytes from %s\n", rc, fname);
return rc;
}
int header_len(struct ieee80211_frame *wh)
{
int len = sizeof(*wh);
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_MGT:
switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
len += 2 + 2; /* capa & listen */
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
len += 2 + 2 + 2; /* capa & status & assoc */
break;
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
len += 2 + 2 + 6; /* capa & listen & AP */
break;
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
len += 2 + 2 + 2; /* capa & status & assoc */
break;
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
case IEEE80211_FC0_SUBTYPE_ATIM:
break;
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON:
len += 8 + 2 + 2; /* time & bint & capa */
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
len += 2; /* reason */
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
len += 2 + 2 + 2; /* algo & seq & status */
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
len += 2; /* reason */
break;
default:
errx(1, "Unknown MGT subtype 0x%x",
wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
}
break;
case IEEE80211_FC0_TYPE_CTL:
switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
case IEEE80211_FC0_SUBTYPE_PS_POLL:
len = sizeof(struct ieee80211_frame_pspoll);
break;
case IEEE80211_FC0_SUBTYPE_RTS:
len = sizeof(struct ieee80211_frame_rts);
break;
case IEEE80211_FC0_SUBTYPE_CTS:
len = sizeof(struct ieee80211_frame_cts);
break;
case IEEE80211_FC0_SUBTYPE_ACK:
len = sizeof(struct ieee80211_frame_ack);
break;
case IEEE80211_FC0_SUBTYPE_CF_END_ACK:
case IEEE80211_FC0_SUBTYPE_CF_END:
len = sizeof(struct ieee80211_frame_cfend);
break;
default:
errx(1, "Unknown CTL subtype 0x%x",
wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
}
break;
case IEEE80211_FC0_TYPE_DATA:
if (wh->i_fc[1] & IEEE80211_FC1_DIR_DSTODS)
len += sizeof(wh->i_addr1);
break;
default:
errx(1, "Unknown type 0x%x",
wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
exit(1);
}
return len;
}
int parse_ie(char *str, unsigned char *ie, int len)
{
int digits = 0;
char num[3];
int conv = 0;
int ielen;
ielen = strlen(str)/2;
if (ielen < 1 || (strlen(str) % 2)) {
printf("Invalid IE %s\n", str);
exit(1);
}
num[2] = 0;
while (ielen) {
num[digits++] = *str;
str++;
if (digits == 2) {
unsigned int x;
sscanf(num, "%x", &x);
if (len <= 0) {
printf("No space for IE\n");
exit(1);
}
*ie++ = (unsigned char) x;
len--;
ielen--;
/* first char */
if (conv == 0) {
if (len == 0) {
printf("No space for IE\n");
exit(1);
}
*ie++ = (unsigned char) ielen;
len--;
conv++;
}
conv++;
digits = 0;
}
}
return conv;
}
int possible_match(struct ieee80211_frame *sent, int slen,
struct ieee80211_frame *got, int glen)
{
if (slen != glen)
return 0;
if (memcmp(sent->i_addr1, got->i_addr1, 6) != 0)
printf("Addr1 doesn't match\n");
if ((sent->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
(got->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
return 0;
if ((sent->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) !=
(got->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
return 0;
/* Good enough for CTL frames I guess */
if ((got->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
return 1;
if (memcmp(sent->i_addr2, got->i_addr2, 6) == 0 &&
memcmp(sent->i_addr3, got->i_addr3, 6) == 0)
return 1;
return 0;
}
int do_verify(struct ieee80211_frame *sent, int slen, void *got, int glen)
{
struct bpf_hdr *bpfh = got;
struct ieee80211_frame *wh;
struct ieee80211_radiotap_header *rth;
int i;
unsigned char *ptr, *ptr2;
/* get the 802.11 header */
glen -= bpfh->bh_hdrlen;
assert(glen > 0);
if (bpfh->bh_caplen != glen) {
abort();
}
rth = (struct ieee80211_radiotap_header*)
((char*) bpfh + bpfh->bh_hdrlen);
glen -= rth->it_len;
assert(glen > 0);
wh = (struct ieee80211_frame*) ((char*)rth + rth->it_len);
glen -= 4; /* 802.11 CRC */
assert(glen > 0);
/* did we receive the packet we sent? */
if (!possible_match(sent, slen, wh, glen))
return 0;
/* check if it got mangled */
if (memcmp(sent, wh, slen) == 0) {
printf("No mangling---got it perfect\n");
return 1;
}
/* print differences */
printf("Got mangled:\n");
ptr = (unsigned char*) sent;
ptr2 = (unsigned char *) wh;
for (i = 0; i < slen; i++, ptr++, ptr2++) {
if (*ptr != *ptr2)
printf("Position: %d Was: %.2X Got: %.2X\n",
i, *ptr, *ptr2);
}
return -1;
}
int main(int argc, char *argv[])
{
int fd, fd2;
char *iface = "ath0";
char *verify = NULL;
int chan = 1;
union {
struct ieee80211_frame w;
unsigned char buf[2048];
} u;
int len = 0;
int ch;
struct ieee80211_bpf_params params;
struct ieee80211_frame *wh = &u.w;
unsigned char *body = u.buf;
memset(&u, 0, sizeof(u));
memset(&params, 0, sizeof(params));
params.ibp_rate0 = 2; /* 1 MB/s XXX */
params.ibp_try0 = 1; /* no retransmits */
params.ibp_power = 100; /* nominal max */
params.ibp_pri = WME_AC_VO; /* high priority */
while ((ch = getopt(argc, argv,
"hv:t:s:TFmpdwou:1:2:3:4:b:i:c:l:n:f:e:S:a:A:C:NRV:W:X:P:")) != -1) {
switch (ch) {
case 'i':
iface = optarg;
break;
case 'c':
chan = atoi(optarg);
break;
case 'v':
wh->i_fc[0] |= atoi(optarg)& IEEE80211_FC0_VERSION_MASK;
break;
case 't':
wh->i_fc[0] |= str2type(optarg) <<
IEEE80211_FC0_TYPE_SHIFT;
break;
case 's':
wh->i_fc[0] |= str2subtype(optarg) <<
IEEE80211_FC0_SUBTYPE_SHIFT;
len = header_len(wh);
body += len;
break;
case 'T':
wh->i_fc[1] |= IEEE80211_FC1_DIR_TODS;
break;
case 'F':
wh->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
break;
case 'm':
wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG;
break;
case 'r':
wh->i_fc[1] |= IEEE80211_FC1_RETRY;
break;
case 'p':
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
break;
case 'd':
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
break;
case 'w':
wh->i_fc[1] |= IEEE80211_FC1_WEP;
break;
case 'o':
wh->i_fc[1] |= IEEE80211_FC1_ORDER;
break;
case 'u':
*(uint16_t*)wh->i_dur = htole16(atoi(optarg));
break;
case '1':
str2mac(wh->i_addr1, optarg);
break;
case '2':
str2mac(wh->i_addr2, optarg);
break;
case '3':
str2mac(wh->i_addr3, optarg);
break;
case '4':
str2mac(body, optarg);
break;
case 'n':
*(uint16_t*)wh->i_seq |= htole16((atoi(optarg) & 0xfff)
<< IEEE80211_SEQ_SEQ_SHIFT);
break;
case 'f':
wh->i_seq[0] |= atoi(optarg) & 0xf;
break;
case 'b':
len += load_payload(optarg, body,
u.buf + sizeof(u.buf) - body);
break;
case 'l':
len = atoi(optarg);
break;
case 'e':
do {
int ln;
ln = parse_ie(optarg, body,
u.buf + sizeof(u.buf) - body);
len += ln;
body += ln;
} while(0);
break;
case 'S':
do {
int ln;
int left = u.buf + sizeof(u.buf) - body;
ln = strlen(optarg) & 0xff;
if ((ln + 2) > left) {
printf("No space for SSID\n");
exit(1);
}
*body++ = 0;
*body++ = ln;
memcpy(body, optarg, ln);
body += ln;
len += ln + 2;
} while(0);
break;
case 'R':
do {
unsigned char rates[] = "\x1\x4\x82\x84\xb\x16";
int left = u.buf + sizeof(u.buf) - body;
if ((sizeof(rates) - 1) > left) {
printf("No space for rates\n");
exit(1);
}
memcpy(body, rates, sizeof(rates) - 1);
body += sizeof(rates) - 1;
len += sizeof(rates) - 1;
} while(0);
break;
case 'a':
do {
uint16_t *x = (uint16_t*) (wh+1);
*x = htole16(atoi(optarg));
} while(0);
break;
case 'A':
do {
uint16_t *x = (uint16_t*) (wh+1);
x += 1;
*x = htole16(atoi(optarg));
} while(0);
break;
case 'C':
do {
uint16_t *x = (uint16_t*) (wh+1);
if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
== IEEE80211_FC0_SUBTYPE_AUTH)
x += 1;
x += 1;
*x = htole16(atoi(optarg));
} while(0);
break;
case 'N':
params.ibp_flags |= IEEE80211_BPF_NOACK;
break;
case 'V':
verify = optarg;
break;
case 'W':
params.ibp_pri = str2wmeac(optarg);
break;
case 'X':
params.ibp_rate0 = str2rate(optarg);
break;
case 'P':
params.ibp_power = atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
break;
}
}
if (!len) {
usage(argv[0]);
exit(1);
}
printf("Using interface %s on chan %d, transmit at %s Mbp/s\n",
iface, chan, rate2str(params.ibp_rate0));
setup_if(iface, chan);
fd = open_bpf(iface);
printf("Dose: %db\n", len);
if (verify) {
setup_if(verify, chan);
fd2 = open_bpf(verify);
}
inject(fd, u.buf, len, &params);
close(fd);
if (verify) {
char buf2[4096];
int rc;
int max = 10;
printf("Verifying via %s\n", verify);
while (max--) {
if ((rc = read(fd2, buf2, sizeof(buf2))) == -1)
err(1, "read()");
if (do_verify(wh, len, buf2, rc)) {
max = 666;
break;
}
}
if (max != 666)
printf("No luck\n");
close(fd2);
}
exit(0);
}