]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: Add preliminary infrastructure for injecting frames
authorJouni Malinen <j@w1.fi>
Thu, 18 Nov 2010 22:35:13 +0000 (00:35 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 18 Nov 2010 22:35:13 +0000 (00:35 +0200)
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.

src/utils/common.h
wlantest/Makefile
wlantest/ctrl.c
wlantest/inject.c [new file with mode: 0644]
wlantest/wlantest.h
wlantest/wlantest_cli.c
wlantest/wlantest_ctrl.h

index f17bf69ff5471f0d1a144fa5b66ea713211d88ce..babfc7b2aad63307d083e78bc71642ecd0a158e1 100644 (file)
@@ -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"
 
 
index 5855150a953d7d08d668095aa291f31b0aef23f1..451025f8642ad0f1c3c1e4fc60fe9fbf73838271 100644 (file)
@@ -62,6 +62,7 @@ OBJS += crc32.o
 OBJS += ccmp.o
 OBJS += tkip.o
 OBJS += ctrl.o
+OBJS += inject.o
 
 LIBS += -lpcap
 
index 0ca720b9c9472d2088b8ff802baec5eeb539b9b1..b525e4753afebcaec1e2ec8674f2340061293fce 100644 (file)
@@ -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 (file)
index 0000000..f8ee026
--- /dev/null
@@ -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;
+}
index 49e468b43fc79dee0063c8a1abe283e31024d08b..44574d025efa012438d71305bc24257530f5a797 100644 (file)
@@ -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 */
index cfd48bafa118729e6becaada9303778eddda4545..21f50c5edef13c47ee4f24d51c1423ab2c3a6eb2 100644 (file)
@@ -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 }
 };
 
index 6a9c99a113799c0279ab028fc1c1b52b31560703..c5ded1e541d22ef1dbd63e5aa43124384ecf9ba5 100644 (file)
@@ -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 */