wlantest: Add control interface and wlantest_cli
authorJouni Malinen <j@w1.fi>
Sat, 13 Nov 2010 16:38:19 +0000 (18:38 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 13 Nov 2010 16:38:19 +0000 (18:38 +0200)
This can be used to manage wlantest operation during run time.

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

index 666791e..9f36803 100644 (file)
@@ -1,4 +1,4 @@
-ALL=wlantest
+ALL=wlantest wlantest_cli
 
 all: $(ALL)
 
@@ -61,6 +61,7 @@ OBJS += sta.o
 OBJS += crc32.o
 OBJS += ccmp.o
 OBJS += tkip.o
+OBJS += ctrl.o
 
 LIBS += -lpcap
 
@@ -88,9 +89,16 @@ libwlantest.so: $(OBJS_lib)
 
 endif
 
+
+OBJS_cli = wlantest_cli.o
+
+
 wlantest: $(OBJS) $(LIBWLANTEST)
        $(LDO) $(LDFLAGS) -o wlantest $(OBJS) -L. -lwlantest $(LIBS)
 
+wlantest_cli: $(OBJS_cli)
+       $(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) -L. -lwlantest
+
 clean:
        $(MAKE) -C ../src clean
        rm -f core *~ *.o *.d libwlantest.a libwlantest.so $(ALL)
diff --git a/wlantest/ctrl.c b/wlantest/ctrl.c
new file mode 100644 (file)
index 0000000..3a292d6
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * wlantest control interface
+ * 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 <sys/un.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+#include "wlantest_ctrl.h"
+
+
+static void ctrl_disconnect(struct wlantest *wt, int sock)
+{
+       int i;
+       wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d",
+                  sock);
+       for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+               if (wt->ctrl_socks[i] == sock) {
+                       close(wt->ctrl_socks[i]);
+                       eloop_unregister_read_sock(wt->ctrl_socks[i]);
+                       wt->ctrl_socks[i] = -1;
+                       break;
+               }
+       }
+}
+
+
+static void ctrl_send_simple(struct wlantest *wt, int sock,
+                            enum wlantest_ctrl_cmd cmd)
+{
+       u8 buf[4];
+       WPA_PUT_BE32(buf, cmd);
+       if (send(sock, buf, sizeof(buf), 0) < 0) {
+               wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno));
+               ctrl_disconnect(wt, sock);
+       }
+}
+
+
+static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wlantest *wt = eloop_ctx;
+       u8 buf[WLANTEST_CTRL_MAX_CMD_LEN];
+       int len;
+       enum wlantest_ctrl_cmd cmd;
+
+       wpa_printf(MSG_EXCESSIVE, "New control interface message from %d",
+                  sock);
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno));
+               ctrl_disconnect(wt, sock);
+               return;
+       }
+       if (len == 0) {
+               ctrl_disconnect(wt, sock);
+               return;
+       }
+
+       if (len < 4) {
+               wpa_printf(MSG_INFO, "Too short control interface command "
+                          "from %d", sock);
+               ctrl_disconnect(wt, sock);
+               return;
+       }
+       cmd = WPA_GET_BE32(buf);
+       wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d",
+                  cmd, sock);
+
+       switch (cmd) {
+       case WLANTEST_CTRL_PING:
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+               break;
+       case WLANTEST_CTRL_TERMINATE:
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+               eloop_terminate();
+               break;
+       default:
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
+               break;
+       }
+}
+
+
+static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wlantest *wt = eloop_ctx;
+       int conn, i;
+
+       conn = accept(sock, NULL, NULL);
+       if (conn < 0) {
+               wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno));
+               return;
+       }
+       wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn);
+
+       for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+               if (wt->ctrl_socks[i] < 0)
+                       break;
+       }
+
+       if (i == MAX_CTRL_CONNECTIONS) {
+               wpa_printf(MSG_INFO, "No room for new control connection");
+               close(conn);
+               return;
+       }
+
+       wt->ctrl_socks[i] = conn;
+       eloop_register_read_sock(conn, ctrl_read, wt, NULL);
+}
+
+
+int ctrl_init(struct wlantest *wt)
+{
+       struct sockaddr_un addr;
+
+       wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+       if (wt->ctrl_sock < 0) {
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+                  sizeof(addr.sun_path) - 1);
+       if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+               close(wt->ctrl_sock);
+               wt->ctrl_sock = -1;
+               return -1;
+       }
+
+       if (listen(wt->ctrl_sock, 5) < 0) {
+               wpa_printf(MSG_ERROR, "listen: %s", strerror(errno));
+               close(wt->ctrl_sock);
+               wt->ctrl_sock = -1;
+               return -1;
+       }
+
+       if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) {
+               close(wt->ctrl_sock);
+               wt->ctrl_sock = -1;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void ctrl_deinit(struct wlantest *wt)
+{
+       int i;
+
+       if (wt->ctrl_sock < 0)
+               return;
+
+       for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+               if (wt->ctrl_socks[i] >= 0) {
+                       close(wt->ctrl_socks[i]);
+                       eloop_unregister_read_sock(wt->ctrl_socks[i]);
+                       wt->ctrl_socks[i] = -1;
+               }
+       }
+
+       eloop_unregister_read_sock(wt->ctrl_sock);
+       close(wt->ctrl_sock);
+       wt->ctrl_sock = -1;
+}
index 8f73581..d43379f 100644 (file)
@@ -31,7 +31,7 @@ static void wlantest_terminate(int sig, void *signal_ctx)
 
 static void usage(void)
 {
-       printf("wlantest [-ddhqq] [-i<ifname>] [-r<pcap file>] "
+       printf("wlantest [-cddhqq] [-i<ifname>] [-r<pcap file>] "
               "[-p<passphrase>]\n"
                "         [-I<wired ifname>] [-R<wired pcap file>] "
               "[-P<RADIUS shared secret>]\n"
@@ -55,8 +55,12 @@ static void secret_deinit(struct wlantest_radius_secret *r)
 
 static void wlantest_init(struct wlantest *wt)
 {
+       int i;
        os_memset(wt, 0, sizeof(*wt));
        wt->monitor_sock = -1;
+       wt->ctrl_sock = -1;
+       for (i = 0; i < MAX_CTRL_CONNECTIONS; i++)
+               wt->ctrl_socks[i] = -1;
        dl_list_init(&wt->passphrase);
        dl_list_init(&wt->bss);
        dl_list_init(&wt->secret);
@@ -80,6 +84,8 @@ static void wlantest_deinit(struct wlantest *wt)
        struct wlantest_radius *r, *rn;
        struct wlantest_pmk *pmk, *np;
 
+       if (wt->ctrl_sock >= 0)
+               ctrl_deinit(wt);
        if (wt->monitor_sock >= 0)
                monitor_deinit(wt);
        dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
@@ -137,6 +143,7 @@ int main(int argc, char *argv[])
        const char *ifname = NULL;
        const char *ifname_wired = NULL;
        struct wlantest wt;
+       int ctrl_iface = 0;
 
        wpa_debug_level = MSG_INFO;
        wpa_debug_show_keys = 1;
@@ -147,10 +154,13 @@ int main(int argc, char *argv[])
        wlantest_init(&wt);
 
        for (;;) {
-               c = getopt(argc, argv, "dhi:I:p:P:qr:R:w:");
+               c = getopt(argc, argv, "cdhi:I:p:P:qr:R:w:");
                if (c < 0)
                        break;
                switch (c) {
+               case 'c':
+                       ctrl_iface = 1;
+                       break;
                case 'd':
                        if (wpa_debug_level > 0)
                                wpa_debug_level--;
@@ -212,6 +222,9 @@ int main(int argc, char *argv[])
        if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0)
                return -1;
 
+       if (ctrl_iface && ctrl_init(&wt) < 0)
+               return -1;
+
        eloop_register_signal_terminate(wlantest_terminate, &wt);
 
        eloop_run();
index c76d22f..aed4fb8 100644 (file)
@@ -102,10 +102,16 @@ struct wlantest_radius {
        struct radius_msg *last_req;
 };
 
+
+#define MAX_CTRL_CONNECTIONS 10
+
 struct wlantest {
        int monitor_sock;
        int monitor_wired;
 
+       int ctrl_sock;
+       int ctrl_socks[MAX_CTRL_CONNECTIONS];
+
        struct dl_list passphrase; /* struct wlantest_passphrase */
        struct dl_list bss; /* struct wlantest_bss */
        struct dl_list secret; /* struct wlantest_radius_secret */
@@ -157,4 +163,7 @@ u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
                  const u8 *data, size_t data_len, size_t *decrypted_len);
 void tkip_get_pn(u8 *pn, const u8 *data);
 
+int ctrl_init(struct wlantest *wt);
+void ctrl_deinit(struct wlantest *wt);
+
 #endif /* WLANTEST_H */
diff --git a/wlantest/wlantest_cli.c b/wlantest/wlantest_cli.c
new file mode 100644 (file)
index 0000000..024626c
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * wlantest controller
+ * 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 <sys/un.h>
+
+#include "utils/common.h"
+#include "wlantest_ctrl.h"
+
+
+static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
+{
+       char buf[4];
+       int res;
+       enum wlantest_ctrl_cmd resp;
+
+       WPA_PUT_BE32(buf, cmd);
+       if (send(s, buf, 4, 0) < 0)
+               return -1;
+       res = recv(s, buf, sizeof(buf), 0);
+       if (res < 4)
+               return -1;
+
+       resp = WPA_GET_BE32(buf);
+       if (resp == WLANTEST_CTRL_SUCCESS)
+               printf("OK\n");
+       else if (resp == WLANTEST_CTRL_FAILURE)
+               printf("FAIL\n");
+       else if (resp == WLANTEST_CTRL_UNKNOWN_CMD)
+               printf("Unknown command\n");
+
+       return resp == WLANTEST_CTRL_SUCCESS ? 0 : -1;
+}
+
+
+static int cmd_ping(int s, int argc, char *argv[])
+{
+       return cmd_simple(s, WLANTEST_CTRL_PING) == 0;
+}
+
+
+static int cmd_terminate(int s, int argc, char *argv[])
+{
+       return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
+}
+
+
+struct wlantest_cli_cmd {
+       const char *cmd;
+       int (*handler)(int s, int argc, char *argv[]);
+       const char *usage;
+};
+
+static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
+       { "ping", cmd_ping, "= test connection to wlantest" },
+       { "terminate", cmd_terminate, "= terminate wlantest" },
+       { NULL, NULL, NULL }
+};
+
+
+static int ctrl_command(int s, int argc, char *argv[])
+{
+       const struct wlantest_cli_cmd *cmd, *match = NULL;
+       int count = 0;
+       int ret = 0;
+
+       for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+               if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+               {
+                       match = cmd;
+                       if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+                               /* exact match */
+                               count = 1;
+                               break;
+                       }
+                       count++;
+               }
+       }
+
+       if (count > 1) {
+               printf("Ambiguous command '%s'; possible commands:", argv[0]);
+               for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+                       if (os_strncasecmp(cmd->cmd, argv[0],
+                                          os_strlen(argv[0])) == 0) {
+                               printf(" %s", cmd->cmd);
+                       }
+                       cmd++;
+               }
+               printf("\n");
+               ret = 1;
+       } else if (count == 0) {
+               printf("Unknown command '%s'\n", argv[0]);
+               ret = 1;
+       } else {
+               ret = match->handler(s, argc - 1, &argv[1]);
+       }
+
+       return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+       int s;
+       struct sockaddr_un addr;
+       int ret = 0;
+
+       s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+                  sizeof(addr.sun_path) - 1);
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("connect");
+               close(s);
+               return -1;
+       }
+
+       if (argc > 1) {
+               ret = ctrl_command(s, argc - 1, &argv[1]);
+               if (ret < 0)
+                       printf("FAIL\n");
+       } else {
+               /* TODO: interactive */
+       }
+
+       close(s);
+       return ret;
+}
diff --git a/wlantest/wlantest_ctrl.h b/wlantest/wlantest_ctrl.h
new file mode 100644 (file)
index 0000000..d34bc88
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * wlantest control interface
+ * 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_CTRL_H
+#define WLANTEST_CTRL_H
+
+#define WLANTEST_SOCK_NAME "w1.fi.wlantest"
+#define WLANTEST_CTRL_MAX_CMD_LEN 1000
+#define WLANTEST_CTRL_MAX_RESP_LEN 1000
+
+enum wlantest_ctrl_cmd {
+       WLANTEST_CTRL_SUCCESS,
+       WLANTEST_CTRL_FAILURE,
+       WLANTEST_CTRL_UNKNOWN_CMD,
+       WLANTEST_CTRL_PING,
+       WLANTEST_CTRL_TERMINATE,
+};
+
+#endif /* WLANTEST_CTRL_H */