diff --git a/src/utils/common.h b/src/utils/common.h index f17bf69ff..babfc7b2a 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -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" diff --git a/wlantest/Makefile b/wlantest/Makefile index 5855150a9..451025f86 100644 --- a/wlantest/Makefile +++ b/wlantest/Makefile @@ -62,6 +62,7 @@ OBJS += crc32.o OBJS += ccmp.o OBJS += tkip.o OBJS += ctrl.o +OBJS += inject.o LIBS += -lpcap diff --git a/wlantest/ctrl.c b/wlantest/ctrl.c index 0ca720b9c..b525e4753 100644 --- a/wlantest/ctrl.c +++ b/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; diff --git a/wlantest/inject.c b/wlantest/inject.c new file mode 100644 index 000000000..f8ee02639 --- /dev/null +++ b/wlantest/inject.c @@ -0,0 +1,80 @@ +/* + * wlantest frame injection + * 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 "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; +} diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 49e468b43..44574d025 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -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 */ diff --git a/wlantest/wlantest_cli.c b/wlantest/wlantest_cli.c index cfd48bafa..21f50c5ed 100644 --- a/wlantest/wlantest_cli.c +++ b/wlantest/wlantest_cli.c @@ -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; + + /* */ + + 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[] = { " = get STA counter value" }, { "get_bss_counter", cmd_get_bss_counter, " = get BSS counter value" }, + { "inject", cmd_inject, + " " }, { NULL, NULL, NULL } }; diff --git a/wlantest/wlantest_ctrl.h b/wlantest/wlantest_ctrl.h index 6a9c99a11..c5ded1e54 100644 --- a/wlantest/wlantest_ctrl.h +++ b/wlantest/wlantest_ctrl.h @@ -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 */