diff --git a/wlantest/Makefile b/wlantest/Makefile index 53278c133..87a1dd9da 100644 --- a/wlantest/Makefile +++ b/wlantest/Makefile @@ -41,11 +41,13 @@ OBJS_lib += ../src/crypto/libcrypto.a OBJS += ../src/common/ieee802_11_common.o OBJS += ../src/common/wpa_common.o +OBJS += ../src/radius/radius.o OBJS += wlantest.o OBJS += readpcap.o OBJS += monitor.o OBJS += process.o +OBJS += wired.o OBJS += rx_mgmt.o OBJS += rx_data.o OBJS += bss.o diff --git a/wlantest/monitor.c b/wlantest/monitor.c index d01d8b3b7..057073ee7 100644 --- a/wlantest/monitor.c +++ b/wlantest/monitor.c @@ -37,6 +37,22 @@ static void monitor_read(int sock, void *eloop_ctx, void *sock_ctx) } +static void monitor_read_wired(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_wired(wt, buf, len); +} + + int monitor_init(struct wlantest *wt, const char *ifname) { struct sockaddr_ll ll; @@ -77,6 +93,46 @@ int monitor_init(struct wlantest *wt, const char *ifname) } +int monitor_init_wired(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_wired = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (wt->monitor_wired < 0) { + wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s", + strerror(errno)); + return -1; + } + + if (bind(wt->monitor_wired, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno)); + close(wt->monitor_wired); + wt->monitor_wired = -1; + return -1; + } + + if (eloop_register_read_sock(wt->monitor_wired, monitor_read_wired, + wt, NULL)) { + wpa_printf(MSG_ERROR, "Could not register monitor read " + "socket"); + close(wt->monitor_wired); + wt->monitor_wired = -1; + return -1; + } + + return 0; +} + + void monitor_deinit(struct wlantest *wt) { if (wt->monitor_sock >= 0) { @@ -84,4 +140,10 @@ void monitor_deinit(struct wlantest *wt) close(wt->monitor_sock); wt->monitor_sock = -1; } + + if (wt->monitor_wired >= 0) { + eloop_unregister_read_sock(wt->monitor_wired); + close(wt->monitor_wired); + wt->monitor_wired = -1; + } } diff --git a/wlantest/readpcap.c b/wlantest/readpcap.c index d96345cf2..dfb7ac070 100644 --- a/wlantest/readpcap.c +++ b/wlantest/readpcap.c @@ -71,3 +71,57 @@ int read_cap_file(struct wlantest *wt, const char *fname) return 0; } + + +int read_wired_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_wired(wt, data, hdr->caplen); + } + + pcap_close(pcap); + + wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count); + + return 0; +} diff --git a/wlantest/wired.c b/wlantest/wired.c new file mode 100644 index 000000000..fe64d4522 --- /dev/null +++ b/wlantest/wired.c @@ -0,0 +1,209 @@ +/* + * Received frame processing for wired interface + * Copyright (c) 2010, Jouni Malinen + * + * 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 +#include +#include + +#include "utils/common.h" +#include "radius/radius.h" +#include "wlantest.h" + + +static const char * radius_code_string(u8 code) +{ + switch (code) { + case RADIUS_CODE_ACCESS_REQUEST: + return "Access-Request"; + case RADIUS_CODE_ACCESS_ACCEPT: + return "Access-Accept"; + case RADIUS_CODE_ACCESS_REJECT: + return "Access-Reject"; + case RADIUS_CODE_ACCOUNTING_REQUEST: + return "Accounting-Request"; + case RADIUS_CODE_ACCOUNTING_RESPONSE: + return "Accounting-Response"; + case RADIUS_CODE_ACCESS_CHALLENGE: + return "Access-Challenge"; + case RADIUS_CODE_STATUS_SERVER: + return "Status-Server"; + case RADIUS_CODE_STATUS_CLIENT: + return "Status-Client"; + case RADIUS_CODE_RESERVED: + return "Reserved"; + default: + return "?Unknown?"; + } +} + + +static void process_radius_access_request(struct wlantest *wt, u32 dst, + u32 src, const u8 *data, size_t len) +{ + struct radius_msg *msg; + + msg = radius_msg_parse(data, len); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Request"); + return; + } + + radius_msg_free(msg); +} + + +static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src, + const u8 *data, size_t len) +{ + struct radius_msg *msg; + + msg = radius_msg_parse(data, len); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept"); + return; + } + + radius_msg_free(msg); +} + + +static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src, + u16 sport, const u8 *data, size_t len) +{ + struct in_addr addr; + char buf[20]; + const struct radius_hdr *hdr; + u16 rlen; + + if (len < sizeof(*hdr)) + return; + hdr = (const struct radius_hdr *) data; + rlen = be_to_host16(hdr->length); + if (len < rlen) + return; + if (len > rlen) + len = rlen; + + addr.s_addr = dst; + snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr)); + + addr.s_addr = src; + wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s", + inet_ntoa(addr), sport, buf, dport, hdr->identifier, + radius_code_string(hdr->code)); + + switch (hdr->code) { + case RADIUS_CODE_ACCESS_REQUEST: + process_radius_access_request(wt, dst, src, data, len); + break; + case RADIUS_CODE_ACCESS_ACCEPT: + process_radius_access_accept(wt, dst, src, data, len); + break; + } +} + + +static void process_udp(struct wlantest *wt, u32 dst, u32 src, + const u8 *data, size_t len) +{ + const struct udphdr *udp; + u16 sport, dport, ulen; + const u8 *payload; + size_t plen; + + if (len < sizeof(*udp)) + return; + udp = (const struct udphdr *) data; + /* TODO: check UDP checksum */ + sport = be_to_host16(udp->source); + dport = be_to_host16(udp->dest); + ulen = be_to_host16(udp->len); + + if (ulen > len) + return; + if (len < ulen) + len = ulen; + + payload = (const u8 *) (udp + 1); + plen = len - sizeof(*udp); + + if (sport == 1812 || dport == 1812) + process_radius(wt, dst, dport, src, sport, payload, plen); +} + + +static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len) +{ + const struct iphdr *ip; + const u8 *payload; + size_t plen; + u16 frag_off, tot_len; + + if (len < sizeof(*ip)) + return; + + ip = (const struct iphdr *) data; + if (ip->version != 4) + return; + if (ip->ihl < 5) + return; + + /* TODO: check header checksum in ip->check */ + + frag_off = be_to_host16(ip->frag_off); + if (frag_off & 0x1fff) { + wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet " + "supported"); + return; + } + + tot_len = be_to_host16(ip->tot_len); + if (tot_len > len) + return; + if (tot_len < len) + len = tot_len; + + payload = data + 4 * ip->ihl; + plen = len - 4 * ip->ihl; + if (payload + plen > data + len) + return; + + switch (ip->protocol) { + case IPPROTO_UDP: + process_udp(wt, ip->daddr, ip->saddr, payload, plen); + break; + } +} + + +void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len) +{ + const struct ether_header *eth; + u16 ethertype; + + wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len); + + if (len < sizeof(*eth)) + return; + + eth = (const struct ether_header *) data; + ethertype = be_to_host16(eth->ether_type); + + switch (ethertype) { + case ETHERTYPE_IP: + process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth)); + break; + } +} diff --git a/wlantest/wlantest.c b/wlantest/wlantest.c index 14cef1ab0..b28f43d32 100644 --- a/wlantest/wlantest.c +++ b/wlantest/wlantest.c @@ -32,7 +32,8 @@ static void wlantest_terminate(int sig, void *signal_ctx) static void usage(void) { printf("wlantest [-ddhqq] [-i] [-r] " - "[-p]\n"); + "[-p]\n" + " [-I] [-R]\n"); } @@ -85,7 +86,9 @@ int main(int argc, char *argv[]) { int c; const char *read_file = NULL; + const char *read_wired_file = NULL; const char *ifname = NULL; + const char *ifname_wired = NULL; struct wlantest wt; wpa_debug_level = MSG_INFO; @@ -97,7 +100,7 @@ int main(int argc, char *argv[]) wlantest_init(&wt); for (;;) { - c = getopt(argc, argv, "dhi:p:qr:"); + c = getopt(argc, argv, "dhi:I:p:qr:R:"); if (c < 0) break; switch (c) { @@ -111,6 +114,9 @@ int main(int argc, char *argv[]) case 'i': ifname = optarg; break; + case 'I': + ifname_wired = optarg; + break; case 'p': add_passphrase(&wt, optarg); break; @@ -120,13 +126,17 @@ int main(int argc, char *argv[]) case 'r': read_file = optarg; break; + case 'R': + read_wired_file = optarg; + break; default: usage(); return -1; } } - if (ifname == NULL && read_file == NULL) { + if (ifname == NULL && ifname_wired == NULL && + read_file == NULL && read_wired_file == NULL) { usage(); return 0; } @@ -134,12 +144,18 @@ int main(int argc, char *argv[]) if (eloop_init()) return -1; + if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0) + return -1; + if (read_file && read_cap_file(&wt, read_file) < 0) return -1; if (ifname && monitor_init(&wt, ifname) < 0) return -1; + if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0) + return -1; + eloop_register_signal_terminate(wlantest_terminate, &wt); eloop_run(); diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 31d903ee2..203044af5 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -66,6 +66,7 @@ struct wlantest_bss { struct wlantest { int monitor_sock; + int monitor_wired; struct dl_list passphrase; /* struct wlantest_passphrase */ struct dl_list bss; /* struct wlantest_bss */ @@ -77,9 +78,12 @@ struct wlantest { }; int read_cap_file(struct wlantest *wt, const char *fname); +int read_wired_cap_file(struct wlantest *wt, const char *fname); void wlantest_process(struct wlantest *wt, const u8 *data, size_t len); +void wlantest_process_wired(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); +int monitor_init_wired(struct wlantest *wt, const char *ifname); void monitor_deinit(struct wlantest *wt); void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len); void rx_data(struct wlantest *wt, const u8 *data, size_t len);