aboutsummaryrefslogtreecommitdiffstats
path: root/wlantest
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2010-11-06 14:20:45 (GMT)
committerJouni Malinen <j@w1.fi>2010-11-07 21:29:00 (GMT)
commita149fcc77d3322bf23d1f53c2cd9fc84a1097c31 (patch)
tree50869affc977d98c872265f0493fa28bb10cb0bb /wlantest
parent6fc58a89e1dc41a315b15c8e067dc50f30dd741e (diff)
downloadhostap-a149fcc77d3322bf23d1f53c2cd9fc84a1097c31.zip
hostap-a149fcc77d3322bf23d1f53c2cd9fc84a1097c31.tar.gz
hostap-a149fcc77d3322bf23d1f53c2cd9fc84a1097c31.tar.bz2
wlantest: Add preliminary version of IEEE 802.11 protocol testing tool
This tool can be used to capture IEEE 802.11 frames either from a monitor interface for realtime capturing or from pcap files for offline analysis. This version is only adding basic infrastructure for going through the frames and parsing their headers.
Diffstat (limited to 'wlantest')
-rw-r--r--wlantest/Makefile81
-rw-r--r--wlantest/crc32.c90
-rw-r--r--wlantest/monitor.c87
-rw-r--r--wlantest/process.c289
-rw-r--r--wlantest/readpcap.c73
-rw-r--r--wlantest/wlantest.c108
-rw-r--r--wlantest/wlantest.h33
7 files changed, 761 insertions, 0 deletions
diff --git a/wlantest/Makefile b/wlantest/Makefile
new file mode 100644
index 0000000..b09816a
--- /dev/null
+++ b/wlantest/Makefile
@@ -0,0 +1,81 @@
+ALL=wlantest
+
+all: $(ALL)
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+
+
+OBJS_lib += ../src/utils/libutils.a
+OBJS_lib += ../src/crypto/libcrypto.a
+
+OBJS += wlantest.o
+OBJS += readpcap.o
+OBJS += monitor.o
+OBJS += process.o
+OBJS += crc32.o
+
+LIBS += -lpcap
+
+
+../src/utils/libutils.a:
+ $(MAKE) -C ../src/utils
+
+../src/crypto/libcrypto.a:
+ $(MAKE) -C ../src/crypto
+
+
+ifneq ($(CONFIG_SOLIB), yes)
+LIBWLANTEST = libwlantest.a
+libwlantest.a: $(OBJS_lib)
+ $(AR) crT libwlantest.a $(OBJS_lib)
+ $(RANLIB) libwlantest.a
+
+else
+CFLAGS += -fPIC -DPIC
+LDFLAGS += -shared
+
+LIBWLANTEST = libwlantest.so
+libwlantest.so: $(OBJS_lib)
+ $(LDO) $(LDFLAGS) $(OBJS_lib) -o $(LIBWLANTEST)
+
+endif
+
+wlantest: $(OBJS) $(LIBWLANTEST)
+ $(LDO) $(LDFLAGS) -o wlantest $(OBJS) -L. -lwlantest $(LIBS)
+
+clean:
+ $(MAKE) -C ../src clean
+ rm -f core *~ *.o *.d libwlantest.a libwlantest.so $(ALL)
+
+-include $(OBJS:%.o=%.d)
diff --git a/wlantest/crc32.c b/wlantest/crc32.c
new file mode 100644
index 0000000..c1747d8
--- /dev/null
+++ b/wlantest/crc32.c
@@ -0,0 +1,90 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * 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"
+
+/*
+ * IEEE 802.11 FCS CRC32
+ * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 +
+ * x^5 + x^4 + x^2 + x + 1
+ */
+static const u32 crc32_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+ 0x2d02ef8d
+};
+
+
+u32 crc32(const u8 *frame, size_t frame_len)
+{
+ size_t i;
+ u32 crc;
+
+ crc = 0xFFFFFFFF;
+ for (i = 0; i < frame_len; i++)
+ crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8);
+
+ return ~crc;
+}
diff --git a/wlantest/monitor.c b/wlantest/monitor.c
new file mode 100644
index 0000000..d01d8b3
--- /dev/null
+++ b/wlantest/monitor.c
@@ -0,0 +1,87 @@
+/*
+ * Linux packet socket monitor
+ * 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 <net/if.h>
+#include <netpacket/packet.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wlantest *wt = eloop_ctx;
+ u8 buf[3000];
+ int len;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+ return;
+ }
+
+ wlantest_process(wt, buf, len);
+}
+
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+ struct sockaddr_ll ll;
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = if_nametoindex(ifname);
+ if (ll.sll_ifindex == 0) {
+ wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+ ifname);
+ return -1;
+ }
+
+ wt->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (wt->monitor_sock < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (bind(wt->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ return -1;
+ }
+
+ if (eloop_register_read_sock(wt->monitor_sock, monitor_read, wt, NULL))
+ {
+ wpa_printf(MSG_ERROR, "Could not register monitor read "
+ "socket");
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+ if (wt->monitor_sock >= 0) {
+ eloop_unregister_read_sock(wt->monitor_sock);
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ }
+}
diff --git a/wlantest/process.c b/wlantest/process.c
new file mode 100644
index 0000000..93a0267
--- /dev/null
+++ b/wlantest/process.c
@@ -0,0 +1,289 @@
+/*
+ * Received frame processing
+ * 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 "utils/radiotap.h"
+#include "utils/radiotap_iter.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static const char * mgmt_stype(u16 stype)
+{
+ switch (stype) {
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ return "ASSOC-REQ";
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ return "ASSOC-RESP";
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ return "REASSOC-REQ";
+ case WLAN_FC_STYPE_REASSOC_RESP:
+ return "REASSOC-RESP";
+ case WLAN_FC_STYPE_PROBE_REQ:
+ return "PROBE-REQ";
+ case WLAN_FC_STYPE_PROBE_RESP:
+ return "PROBE-RESP";
+ case WLAN_FC_STYPE_BEACON:
+ return "BEACON";
+ case WLAN_FC_STYPE_ATIM:
+ return "ATIM";
+ case WLAN_FC_STYPE_DISASSOC:
+ return "DISASSOC";
+ case WLAN_FC_STYPE_AUTH:
+ return "AUTH";
+ case WLAN_FC_STYPE_DEAUTH:
+ return "DEAUTH";
+ case WLAN_FC_STYPE_ACTION:
+ return "ACTION";
+ }
+ return "??";
+}
+
+
+static void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc, stype;
+
+ if (len < 24)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ wt->rx_mgmt++;
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
+ stype == WLAN_FC_STYPE_PROBE_RESP ||
+ stype == WLAN_FC_STYPE_PROBE_REQ) ?
+ MSG_EXCESSIVE : MSG_MSGDUMP,
+ "MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
+ mgmt_stype(stype),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+}
+
+
+static const char * data_stype(u16 stype)
+{
+ switch (stype) {
+ case WLAN_FC_STYPE_DATA:
+ return "DATA";
+ case WLAN_FC_STYPE_DATA_CFACK:
+ return "DATA-CFACK";
+ case WLAN_FC_STYPE_DATA_CFPOLL:
+ return "DATA-CFPOLL";
+ case WLAN_FC_STYPE_DATA_CFACKPOLL:
+ return "DATA-CFACKPOLL";
+ case WLAN_FC_STYPE_NULLFUNC:
+ return "NULLFUNC";
+ case WLAN_FC_STYPE_CFACK:
+ return "CFACK";
+ case WLAN_FC_STYPE_CFPOLL:
+ return "CFPOLL";
+ case WLAN_FC_STYPE_CFACKPOLL:
+ return "CFACKPOLL";
+ case WLAN_FC_STYPE_QOS_DATA:
+ return "QOSDATA";
+ case WLAN_FC_STYPE_QOS_DATA_CFACK:
+ return "QOSDATA-CFACK";
+ case WLAN_FC_STYPE_QOS_DATA_CFPOLL:
+ return "QOSDATA-CFPOLL";
+ case WLAN_FC_STYPE_QOS_DATA_CFACKPOLL:
+ return "QOSDATA-CFACKPOLL";
+ case WLAN_FC_STYPE_QOS_NULL:
+ return "QOS-NULL";
+ case WLAN_FC_STYPE_QOS_CFPOLL:
+ return "QOS-CFPOLL";
+ case WLAN_FC_STYPE_QOS_CFACKPOLL:
+ return "QOS-CFACKPOLL";
+ }
+ return "??";
+}
+
+
+static void rx_data(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ if (len < 24)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ wt->rx_data++;
+
+ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ case 0:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s IBSS DA=" MACSTR " SA="
+ MACSTR " BSSID=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ break;
+ case WLAN_FC_FROMDS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s FromDS DA=" MACSTR
+ " BSSID=" MACSTR " SA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ break;
+ case WLAN_FC_TODS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s ToDS BSSID=" MACSTR
+ " SA=" MACSTR " DA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ break;
+ case WLAN_FC_TODS | WLAN_FC_FROMDS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s WDS RA=" MACSTR " TA="
+ MACSTR " DA=" MACSTR " SA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ MAC2STR((const u8 *) (hdr + 1)));
+ break;
+ }
+}
+
+
+static void rx_frame(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len);
+ if (len < 2)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ if (fc & WLAN_FC_PVER) {
+ wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d",
+ fc & WLAN_FC_PVER);
+ return;
+ }
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ rx_mgmt(wt, data, len);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ if (len < 10)
+ return;
+ wt->rx_ctrl++;
+ break;
+ case WLAN_FC_TYPE_DATA:
+ rx_data(wt, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d",
+ WLAN_FC_GET_TYPE(fc));
+ break;
+ }
+}
+
+
+static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack)
+{
+ wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack);
+ wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len);
+}
+
+
+static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs)
+{
+ if (WPA_GET_LE32(fcs) != crc32(frame, frame_len))
+ return -1;
+ return 0;
+}
+
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
+{
+ struct ieee80211_radiotap_iterator iter;
+ int ret;
+ int rxflags = 0, txflags = 0, failed = 0, fcs = 0;
+ const u8 *frame, *fcspos;
+ size_t frame_len;
+
+ wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+ if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len)) {
+ wpa_printf(MSG_INFO, "Invalid radiotap frame");
+ return;
+ }
+
+ for (;;) {
+ ret = ieee80211_radiotap_iterator_next(&iter);
+ wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d "
+ "this_arg_index=%d", ret, iter.this_arg_index);
+ if (ret == -ENOENT)
+ break;
+ if (ret) {
+ wpa_printf(MSG_INFO, "Invalid radiotap header: %d",
+ ret);
+ return;
+ }
+ switch (iter.this_arg_index) {
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+ fcs = 1;
+ break;
+ case IEEE80211_RADIOTAP_RX_FLAGS:
+ rxflags = 1;
+ break;
+ case IEEE80211_RADIOTAP_TX_FLAGS:
+ txflags = 1;
+ failed = le_to_host16((*(u16 *) iter.this_arg)) &
+ IEEE80211_RADIOTAP_F_TX_FAIL;
+ break;
+
+ }
+ }
+
+ frame = data + iter.max_length;
+ frame_len = len - iter.max_length;
+
+ if (fcs && frame_len >= 4) {
+ frame_len -= 4;
+ fcspos = frame + frame_len;
+ if (check_fcs(frame, frame_len, fcspos) < 0) {
+ wpa_printf(MSG_EXCESSIVE, "Drop RX frame with invalid "
+ "FCS");
+ wt->fcs_error++;
+ return;
+ }
+ }
+
+ if (rxflags && txflags)
+ return;
+ if (!txflags)
+ rx_frame(wt, frame, frame_len);
+ else
+ tx_status(wt, frame, frame_len, !failed);
+}
diff --git a/wlantest/readpcap.c b/wlantest/readpcap.c
new file mode 100644
index 0000000..d96345c
--- /dev/null
+++ b/wlantest/readpcap.c
@@ -0,0 +1,73 @@
+/*
+ * PCAP capture file reader
+ * 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 <pcap/pcap.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+int read_cap_file(struct wlantest *wt, const char *fname)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *pcap;
+ unsigned int count = 0;
+ struct pcap_pkthdr *hdr;
+ const u_char *data;
+ int res;
+
+ pcap = pcap_open_offline(fname, errbuf);
+ if (pcap == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+ fname, errbuf);
+ return -1;
+ }
+
+ for (;;) {
+ res = pcap_next_ex(pcap, &hdr, &data);
+ if (res == -2)
+ break; /* No more packets */
+ if (res == -1) {
+ wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+ pcap_geterr(pcap));
+ break;
+ }
+ if (res != 1) {
+ wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+ "value %d", res);
+ break;
+ }
+
+ /* Packet was read without problems */
+ wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+ "len=%u/%u",
+ (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+ hdr->caplen, hdr->len);
+ if (hdr->caplen < hdr->len) {
+ wpa_printf(MSG_DEBUG, "pcap: Dropped incomplete frame "
+ "(%u/%u captured)",
+ hdr->caplen, hdr->len);
+ continue;
+ }
+ count++;
+ wlantest_process(wt, data, hdr->caplen);
+ }
+
+ pcap_close(pcap);
+
+ wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+ return 0;
+}
diff --git a/wlantest/wlantest.c b/wlantest/wlantest.c
new file mode 100644
index 0000000..9a2f70d
--- /dev/null
+++ b/wlantest/wlantest.c
@@ -0,0 +1,108 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * 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 "utils/eloop.h"
+#include "wlantest.h"
+
+
+extern int wpa_debug_level;
+
+
+static void wlantest_terminate(int sig, void *signal_ctx)
+{
+ eloop_terminate();
+}
+
+
+static void usage(void)
+{
+ printf("wlantest [-ddhqq] [-i<ifname>] [-r<pcap file>]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c;
+ const char *read_file = NULL;
+ const char *ifname = NULL;
+ struct wlantest wt;
+
+ wpa_debug_level = MSG_INFO;
+
+ if (os_program_init())
+ return -1;
+
+ os_memset(&wt, 0, sizeof(wt));
+ wt.monitor_sock = -1;
+
+ for (;;) {
+ c = getopt(argc, argv, "dhi:r:q");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'd':
+ if (wpa_debug_level > 0)
+ wpa_debug_level--;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ case 'i':
+ ifname = optarg;
+ break;
+ case 'q':
+ wpa_debug_level++;
+ break;
+ case 'r':
+ read_file = optarg;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+
+ if (ifname == NULL && read_file == NULL) {
+ usage();
+ return 0;
+ }
+
+ if (eloop_init())
+ return -1;
+
+ if (read_file && read_cap_file(&wt, read_file) < 0)
+ return -1;
+
+ if (ifname && monitor_init(&wt, ifname) < 0)
+ return -1;
+
+ eloop_register_signal_terminate(wlantest_terminate, &wt);
+
+ eloop_run();
+
+ wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u "
+ "fcs_error=%u",
+ wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error);
+
+ if (ifname)
+ monitor_deinit(&wt);
+
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h
new file mode 100644
index 0000000..2af12f1
--- /dev/null
+++ b/wlantest/wlantest.h
@@ -0,0 +1,33 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * 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.
+ */
+
+#ifndef WLANTEST_H
+#define WLANTEST_H
+
+struct wlantest {
+ int monitor_sock;
+
+ unsigned int rx_mgmt;
+ unsigned int rx_ctrl;
+ unsigned int rx_data;
+ unsigned int fcs_error;
+};
+
+int read_cap_file(struct wlantest *wt, const char *fname);
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len);
+u32 crc32(const u8 *frame, size_t frame_len);
+int monitor_init(struct wlantest *wt, const char *ifname);
+void monitor_deinit(struct wlantest *wt);
+
+#endif /* WLANTEST_H */