summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-08-09 16:41:59 +0000
committerLennart Poettering <lennart@poettering.net>2003-08-09 16:41:59 +0000
commit3e81b0123b4bbfedbdc1135a6a4305c347f91a3a (patch)
tree0df13d3199a04b9ac8b3c576d9f4881593a87a36
parentba2dc48008777865fec281837ad9869d76cf8e87 (diff)
Initial commit
git-svn-id: file:///home/lennart/svn/public/aeswepd/trunk@3 022f378f-78c4-0310-b860-d162c87e6274
-rw-r--r--Makefile.am35
-rwxr-xr-xbootstrap.sh39
-rwxr-xr-xconf/scripts/default44
-rw-r--r--configure.ac82
-rw-r--r--src/Makefile.am6
-rw-r--r--src/aes.c94
-rw-r--r--src/aes.h9
-rw-r--r--src/aeswepd.c587
-rw-r--r--src/aeswepd.h14
-rw-r--r--src/assocwatch.c88
-rw-r--r--src/assocwatch.h8
-rw-r--r--src/exec.c138
-rw-r--r--src/exec.h6
-rw-r--r--src/ifmonitor.c84
-rw-r--r--src/ifmonitor.h6
-rw-r--r--src/interface.c88
-rw-r--r--src/interface.h17
-rw-r--r--src/iwapi.c225
-rw-r--r--src/iwapi.h22
-rw-r--r--src/iwkey.c87
-rw-r--r--src/iwkey.h11
-rw-r--r--src/nlapi.c111
-rw-r--r--src/nlapi.h19
-rw-r--r--src/util.c136
-rw-r--r--src/util.h31
-rw-r--r--src/waproamd.c667
-rw-r--r--src/wireless.15.h698
27 files changed, 3352 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..17aafcf
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,35 @@
+# $Id: Makefile.am 22 2003-06-15 16:36:33Z lennart $
+
+# This file is part of wepkeyd.
+#
+# wepkeyd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# wepkeyd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with wepkeyd; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+EXTRA_DIST=bootstrap.sh README LICENSE
+SUBDIRS=src #man conf doc patches
+
+MAINTAINERCLEANFILES = README
+noinst_DATA = README
+
+README:
+ rm -f README
+# $(MAKE) -C doc README
+ ln -s doc/README README
+
+homepage:
+ test -d $$HOME/homepage/lennart
+ mkdir -p $$HOME/homepage/lennart/projects/wepkeyd
+ cp *.tar.gz $$HOME/homepage/lennart/projects/wepkeyd
+ cp doc/README.html doc/style.css doc/NEWS $$HOME/homepage/lennart/projects/wepkeyd
+ cp $$HOME/homepage/lennart/projects/wepkeyd/README.html $$HOME/homepage/lennart/projects/wepkeyd/index.html
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..1a9ef00
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# $Id: bootstrap.sh 20 2003-07-10 16:21:55Z lennart $
+
+# This file is part of wepkeyd.
+#
+# wepkeyd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# wepkeyd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with wepkeyd; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+if [ "x$1" = "xam" ] ; then
+ set -ex
+ automake -a -c
+ ./config.status
+else
+ set -ex
+
+ make maintainer-clean || true
+
+ rm -rf autom4te.cache
+ rm -f config.cache
+
+ aclocal
+ autoheader
+ automake -a -c
+ autoconf -Wall
+
+ ./configure --sysconfdir=/etc "$@"
+fi
+
diff --git a/conf/scripts/default b/conf/scripts/default
new file mode 100755
index 0000000..a871c39
--- /dev/null
+++ b/conf/scripts/default
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+KEYDIR=.
+
+if [ -z "$AP" -o -z "$IFACE" ] ; then
+ echo "Corrupt environment" > /dev/stderr
+ exit 1
+fi
+
+if test -r "$KEYDIR/$AP.aes" ; then
+ METHOD=aes
+else if test -r "$KEYDIR/$AP.wep" ; then
+ METHOD=wep
+else
+ METHOD=open
+fi ; fi
+
+echo "Settings policy '$METHOD' for AP $AP"
+
+case "$1.$METHOD" in
+ start.aes)
+ iwconfig $IFACE key on
+ aeswepd -i $IFACE -w -F "$KEYDIR/$AP.aes"
+ ;;
+
+ stop.aes)
+ aeswepd -i $IFACE -k
+ iwconfig $IFACE key off
+ ;;
+
+ start.wep)
+ iwconfig $IFACE key "`cat $KEYDIR/$AP.wep`" [1] key restricted key [1] key on
+ ;;
+
+ *.open|stop.wep)
+ iwconfig $IFACE key off
+ ;;
+ *)
+ echo "Unknown command\n" > /dev/stderr
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..76c0677
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,82 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+# $Id: Makefile.am 22 2003-06-15 16:36:33Z lennart $
+
+# This file is part of waproamd.
+#
+# waproamd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# waproamd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with waproamd; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+AC_PREREQ(2.57)
+AC_INIT([waproamd], [0.1], [mzjrcxrlq@itaparica.org])
+AC_CONFIG_SRCDIR([src/waproamd.c])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([foreign -Wall])
+AM_MAINTAINER_MODE
+
+# Checks for programs.
+AC_PROG_CXX
+AC_PROG_CC
+
+# If using GCC specifiy some additional parameters
+if test "x$GCC" = "xyes" ; then
+ CFLAGS="$CFLAGS -pipe -Wall"
+fi
+
+# Checks for libraries.
+AC_CHECK_LIB([daemon], [daemon_fork],, [AC_MSG_ERROR([*** Sorry, you have to install libdaemon ***])])
+AC_CHECK_LIB([mcrypt], [mcrypt_module_open],, [AC_MSG_ERROR([*** Sorry, you have to install libmcrypt ***])])
+
+# LYNX documentation generation
+AC_ARG_ENABLE(lynx,
+ AC_HELP_STRING([--disable-lynx], [Turn off lynx usage for documentation generation]),
+[case "${enableval}" in
+ yes) lynx=yes ;;
+ no) lynx=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;;
+esac],[lynx=yes])
+
+if test x$lynx = xyes ; then
+ AC_CHECK_PROG(have_lynx, lynx, yes, no)
+
+ if test x$have_lynx = xno ; then
+ AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***])
+ fi
+fi
+
+AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes])
+
+# XMLTOMAN manpage generation
+AC_ARG_ENABLE(xmltoman,
+ AC_HELP_STRING([--disable-xmltoman], [Disable rebuilding of man pages with xmltoman]),
+[case "${enableval}" in
+ yes) xmltoman=yes ;;
+ no) xmltoman=no ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --disable-xmltoman]) ;;
+esac],[xmltoman=yes])
+
+if test x$xmltoman = xyes ; then
+ AC_CHECK_PROG(have_xmltoman, xmltoman, yes, no)
+
+ if test x$have_xmltoman = xno ; then
+ AC_MSG_WARN([*** Not rebuilding man pages as xmltoman is not found ***])
+ xmltoman=no
+ fi
+fi
+
+AM_CONDITIONAL([USE_XMLTOMAN], [test "x$xmltoman" = xyes])
+
+AC_CONFIG_FILES([src/Makefile Makefile]) # man/Makefile conf/Makefile doc/Makefile doc/README.html patches/Makefile waproamd.spec])
+AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..a55c459
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,6 @@
+AM_CFLAGS = -DSYSCONFDIR="\"$(sysconfdir)\""
+
+sbin_PROGRAMS = waproamd aeswepd
+
+aeswepd_SOURCES = aeswepd.c aeswepd.h aes.c aes.h interface.c interface.h iwkey.c iwkey.h util.c util.h
+waproamd_SOURCES = waproamd.c interface.c interface.h iwapi.c iwapi.h util.c util.h exec.c exec.h nlapi.c nlapi.h assocwatch.c assocwatch.h ifmonitor.c ifmonitor.h
diff --git a/src/aes.c b/src/aes.c
new file mode 100644
index 0000000..5165992
--- /dev/null
+++ b/src/aes.c
@@ -0,0 +1,94 @@
+#include <mcrypt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "aes.h"
+#include "util.h"
+#include "aeswepd.h"
+
+#define MAX_CACHE 10
+
+static MCRYPT m = MCRYPT_FAILED;
+
+
+struct cache_entry {
+ uint8_t key[AES_KEY_LEN];
+ uint8_t data[AES_KEY_LEN];
+ uint8_t result[AES_KEY_LEN];
+ time_t timestamp;
+};
+
+static struct cache_entry cache[MAX_CACHE];
+static int n_cache = 0;
+
+int aes_crypt(uint8_t *key, uint8_t *data, uint8_t *result) {
+ int r, j;
+ struct cache_entry *e;
+ time_t now;
+
+ for (j = 0; j < n_cache; j++)
+ if (!memcmp(cache[j].key, key, AES_KEY_LEN) && !memcmp(cache[j].data, data, AES_KEY_LEN)) {
+ //fprintf(stderr, "Cache hit\n");
+ memcpy(result, cache[j].result, AES_KEY_LEN);
+ return 0;
+ }
+
+ //fprintf(stderr, "Cache miss\n");
+
+ if (m == MCRYPT_FAILED) {
+ if ((m = mcrypt_module_open("rijndael-128", NULL, "ecb", NULL)) == MCRYPT_FAILED) {
+ fprintf(stderr, "Failed to open rijndael mcrypt module\n");
+ return -1;
+ }
+ }
+
+ if ((r = mcrypt_generic_init(m, key, AES_KEY_LEN, NULL)) != 0) {
+ fprintf(stderr, "Failed to encrypt: %s\n", mcrypt_strerror(r));
+ return -1;
+ }
+
+ memcpy(result, data, AES_KEY_LEN);
+ if (mcrypt_generic(m, result, AES_KEY_LEN) != 0) {
+ fprintf(stderr, "mdecrypt_generic() failed.\n");
+ return -1;
+ }
+
+ if (mcrypt_generic_deinit(m) != 0) {
+ fprintf(stderr, "mdecrypt_generic() failed.\n");
+ return -1;
+ }
+
+ now = time(NULL);
+
+ if (n_cache < n_max_keys)
+ e = &cache[n_cache++];
+ else {
+ if (n_cache > n_max_keys)
+ n_cache = n_max_keys;
+
+ e = NULL;
+ for (j = 0; j < n_cache; j++)
+ if (!e || cache[j].timestamp < e->timestamp)
+ e = &cache[j];
+ }
+
+ memcpy(e->key, key, AES_KEY_LEN);
+ memcpy(e->data, data, AES_KEY_LEN);
+ memcpy(e->result, result, AES_KEY_LEN);
+ e->timestamp = now;
+
+ return 0;
+}
+
+int aes_done(void) {
+ if (m != MCRYPT_FAILED) {
+ mcrypt_module_close(m);
+ m = MCRYPT_FAILED;
+ }
+
+ n_cache = 0;
+
+ return 0;
+}
diff --git a/src/aes.h b/src/aes.h
new file mode 100644
index 0000000..cb00254
--- /dev/null
+++ b/src/aes.h
@@ -0,0 +1,9 @@
+#ifndef fooaeshfoo
+#define fooaeshfoo
+
+#include <stdint.h>
+
+int aes_crypt(uint8_t *key, uint8_t *data, uint8_t *result);
+int aes_done(void);
+
+#endif
diff --git a/src/aeswepd.c b/src/aeswepd.c
new file mode 100644
index 0000000..9b4c07f
--- /dev/null
+++ b/src/aeswepd.c
@@ -0,0 +1,587 @@
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <sys/select.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <libdaemon/dlog.h>
+#include <libdaemon/dpid.h>
+#include <libdaemon/dsignal.h>
+#include <libdaemon/dfork.h>
+
+#include "aes.h"
+#include "aeswepd.h"
+#include "iwkey.h"
+#include "interface.h"
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef VARLIBAESWEPD
+#define VARLIBAESWEPD "/var/lib/aeswepd"
+#endif
+
+uint8_t aes_key[AES_KEY_LEN];
+int rekey_time = 15*60;
+int n_max_keys = 3;
+int key_map[MAX_WEP_KEYS];
+char *interface_name = NULL;
+int daemonize = 1, use_syslog = 1, wait_on_fork = 0, use_status_file = 1;
+char log_ident[32], pid_ident[32];
+
+FILE *status_file = NULL;
+
+const char *get_status_file_name(void) {
+ static int init = 0;
+ static char path[PATH_MAX];
+
+ if (!init) {
+ snprintf(path, sizeof(path), "%s/status.%s", VARLIBAESWEPD, interface_name);
+ init = 1;
+ }
+
+ return path;
+}
+
+
+int do_list_keys() {
+ FILE *f;
+ char ln[256];
+
+ if (!(f = fopen(get_status_file_name(), "r"))) {
+ daemon_log(LOG_ERR, "Failed to open '%s': %s", get_status_file_name(), strerror(errno));
+ return -1;
+ }
+
+ while (fgets(ln, sizeof(ln), f))
+ fputs(ln, stdout);
+
+ fclose(f);
+ return 0;
+}
+
+int wep_key_calc(uint32_t t, uint8_t w[WEP_KEY_LEN]) {
+ uint8_t data[AES_KEY_LEN], result[AES_KEY_LEN];
+ uint32_t v = t;
+ int i, shift;
+
+ for (i = 0, shift = 0; i < AES_KEY_LEN; i++) {
+ data[i] = (uint8_t) (v >> shift);
+
+ if ((shift += 8) >= sizeof(v)*8)
+ shift = 0;
+ }
+
+ if (aes_crypt(aes_key, data, result) < 0) {
+ daemon_log(LOG_ERR, "aes_crypt() failed!");
+ return -1;
+ }
+
+ memset(w, 0, WEP_KEY_LEN);
+ memcpy(w, result, MIN(WEP_KEY_LEN, AES_KEY_LEN));
+
+ return 0;
+}
+
+int print_wep_key(FILE *f, int t, uint8_t wep[WEP_KEY_LEN]) {
+ time_t rt = t*rekey_time;
+ fprintf(f, "%10i: Starting %s\t WEP: ", t, ctime(&rt));
+ print_hex(f, wep, WEP_KEY_LEN);
+ fprintf(f, "\n");
+ return 0;
+}
+
+
+int rekey(struct interface *i, time_t now, time_t *next_rekey) {
+ uint8_t w[WEP_KEY_LEN];
+ uint32_t t;
+
+ assert(i && n_max_keys >= 1 && next_rekey);
+
+ t = now/rekey_time;
+
+ daemon_log(LOG_ERR, "Rekeying for %i", t);
+
+ if (status_file) {
+ rewind(status_file);
+ ftruncate(fileno(status_file), 0);
+ }
+
+ if (wep_key_calc(t, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t, w);
+
+ if (n_max_keys >= 3) {
+ if (wep_key_calc(t-1, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t-1, w);
+
+ if (wep_key_calc(t+1, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t+1, w);
+ }
+
+ if (status_file)
+ fflush(status_file);
+
+ if (wep_key_finish(i) < 0)
+ return -1;
+
+ *next_rekey = (t+1)*rekey_time;
+
+ return 0;
+}
+
+int go(struct interface *i) {
+ time_t next_rekey = 0;
+ int send_retval = 1;
+ fd_set fds;
+ time_t now = time(NULL);
+ int sigfd, r = -1;
+
+ daemon_log(LOG_INFO, "aeswepd "VERSION" initializing. (rekey_time=%i)", rekey_time);
+
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc());
+ goto finish;
+ }
+
+ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, -1) < 0) {
+ daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno));
+ goto finish;
+ }
+
+ if (use_status_file) {
+ mode_t u;
+
+ u = umask(077);
+ status_file = fopen(get_status_file_name(), "w");
+ umask(u);
+
+ if (!status_file) {
+ daemon_log(LOG_WARNING, "Failed to open status file '%s', doing without: %s", get_status_file_name(), strerror(errno));
+ use_status_file = 0;
+ }
+ }
+
+ if (rekey(i, now, &next_rekey) < 0)
+ goto finish;
+
+ daemon_log(LOG_INFO, "aeswepd successfully initialized.");
+
+ if (daemonize && wait_on_fork) {
+ daemon_retval_send(0);
+ send_retval = 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(sigfd = daemon_signal_fd(), &fds);
+
+ for (;;) {
+ fd_set qfds;
+ struct timeval tv;
+ now = time(NULL);
+
+ if (next_rekey < now) {
+ if (rekey(i, now, &next_rekey) < 0)
+ return -1;
+ }
+
+ qfds = fds;
+
+ now = time(NULL);
+ tv.tv_sec = next_rekey > now ? next_rekey - now : 0;
+ tv.tv_usec = 0;
+
+ if (select(sigfd+1, &qfds, NULL, NULL, &tv) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ daemon_log(LOG_ERR, "select() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+
+ if (FD_ISSET(sigfd, &qfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ goto finish;
+ }
+
+
+ switch (sig) {
+
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ r = 0;
+ goto finish;
+
+ case SIGHUP:
+ next_rekey = 0;
+ break;
+
+ default:
+ daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig));
+ break;
+
+ }
+ }
+ }
+
+ r = 0;
+
+finish:
+
+ if (status_file) {
+ fclose(status_file);
+ unlink(get_status_file_name());
+ }
+
+ if (send_retval && daemonize && wait_on_fork)
+ daemon_retval_send(1);
+
+ daemon_pid_file_remove();
+ daemon_signal_done();
+
+ daemon_log(LOG_INFO, "Exiting.");
+
+ return r;
+}
+
+void usage(char *p) {
+ if (strrchr(p, '/'))
+ p = strchr(p, '/')+1;
+
+ printf("%s -- AES Rekeying Daemon for IEEE 802.11b WEP\n\n"
+ "Usage: %s [options]\n\n"
+ "Options:\n"
+ " -n --no-daemon Do not daemonize (for debugging) (%s)\n"
+ " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n"
+ " -i --iface=IFACE Specify network interface (%s)\n"
+ " -t --rekey-time=SECS Specify rekeying interval in seconds (%i)\n"
+ " -K --key=KEY Specify AES key (don't use this!)\n"
+ " -F --key-file=KEYFILE File to read key from\n"
+ " -E --key-env=KEYENV Specify environment variable to read key from\n"
+ " -m --max-keys=NR Specify the number of keys to use concurrently (%i)\n"
+ " -w --wait-on-fork Wait until daemon fork finished (%s)\n"
+ " -l --list-keys Show keys of running daemon\n"
+ " -h --help Show this help\n"
+ " -k --kill Kill a running daemon\n"
+ " -c --check-running Check if a daemon is currently running\n"
+ " -v --version Show version\n\n"
+ " <KEY>: A %i byte AES key as hexadecimal string\n",
+ p, p,
+ !daemonize ? "on" : "off",
+ !use_syslog ? "on" : "off",
+ interface_name,
+ rekey_time,
+ n_max_keys,
+ wait_on_fork ? "on" : "off",
+ AES_KEY_LEN);
+}
+
+void parse_args(int argc, char *argv[]) {
+ static struct option long_options[] = {
+ {"no-daemon", no_argument, 0, 'n'},
+ {"no-syslog", no_argument, 0, 's'},
+ {"iface", required_argument, 0, 'i'},
+ {"rekey-time", required_argument, 0, 't'},
+ {"max-keys", required_argument, 0, 'm'},
+ {"wait-on-fork", no_argument, 0, 'w'},
+ {"help", no_argument, 0, 'h'},
+ {"kill", no_argument, 0, 'k'},
+ {"check-running", no_argument, 0, 'c'},
+ {"version", no_argument, 0, 'v'},
+ {"list-keys", no_argument, 0, 'l'},
+ {"key", no_argument, 0, 'K'},
+ {"key-file", no_argument, 0, 'F'},
+ {"key-env", no_argument, 0, 'E'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int _help = 0, _kill = 0, _check = 0, _version = 0, r, _list_keys = 0, _key_set = 0;
+ char ln[64];
+ char *b;
+
+ if ((b = strrchr(argv[0], '/')))
+ b++;
+ else
+ b = argv[0];
+
+
+ if (strcmp(b, "aeswepl") == 0)
+ _list_keys = 1;
+
+ memset(aes_key, sizeof(aes_key), 0);
+
+ for (;;) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "nsi:t:m:whkcvlK:F:E:", long_options, &option_index)) < 0)
+ break;
+
+ switch (c) {
+ case 'n' :
+ daemonize = !daemonize;
+ break;
+ case 's' :
+ use_syslog = !use_syslog;
+ break;
+ case 'i' :
+ if (interface_name)
+ free(interface_name);
+
+ interface_name = strdup(optarg);
+ break;
+ case 't':
+ if ((rekey_time = atoi(optarg)) <= 0) {
+ fprintf(stderr, "Rekey time too short.\n");
+ exit(1);
+ }
+ break;
+ case 'm':
+ n_max_keys = atoi(optarg);
+ if (n_max_keys <= 0 || n_max_keys > 4) {
+ fprintf(stderr, "--max-keys has to be between 1 and 4\n");
+ exit(1);
+ }
+ break;
+ case 'w':
+ wait_on_fork = !wait_on_fork;
+ break;
+ case 'h':
+ _help = 1;
+ break;
+ case 'k':
+ _kill = 1;
+ break;
+ case 'c':
+ _check = 1;
+ break;
+ case 'v':
+ _version = 1;
+ break;
+ case 'l':
+ _list_keys = 1;
+ break;
+
+ case 'K':
+ daemon_log(LOG_WARNING, "WARNING: Don't use the --key option, other local users might peek on 'ps'. Proceeding");
+ strncpy(ln, optarg, sizeof(ln)-1);
+ ln[sizeof(ln)-1] = 0;
+ _key_set = 1;
+ memset(optarg, 'X', strlen(optarg));
+ break;
+
+ case 'E':
+ if (!getenv(optarg)) {
+ daemon_log(LOG_ERR, "Environment variable for key '%s' not existent.", optarg);
+ exit(1);
+ }
+
+ strncpy(ln, getenv(optarg), sizeof(ln)-1);
+ ln[sizeof(ln)-1] = 0;
+ _key_set = 1;
+ unsetenv(optarg);
+ break;
+
+ case 'F':{
+ FILE *f;
+ struct stat st;
+
+ if (!(f = fopen(optarg, "r"))) {
+ daemon_log(LOG_ERR, "Could not open key file '%s': %s", optarg, strerror(errno));
+ exit(1);
+ }
+
+ if (fstat(fileno(f), &st) < 0) {
+ daemon_log(LOG_ERR, "Failed to stat file '%s': %s", optarg, strerror(errno));
+ exit(1);
+ }
+
+ if (st.st_mode & 077 || st.st_uid != 0) {
+ daemon_log(LOG_ERR, "Key file '%s' must be owned by root and have an access mode of 0700 or less.", optarg);
+ exit(1);
+ }
+
+ ln[0] = 0;
+ fgets(ln, sizeof(ln), f);
+ fclose(f);
+ ln[strcspn(ln, " \n\r\t")] = 0;
+ _key_set = 1;
+ break;
+ }
+
+ default:
+ fprintf(stderr, "Unknown parameter.\n");
+ exit(1);
+ }
+ }
+
+ if (!interface_name)
+ interface_name = strdup("wlan0");
+
+ snprintf(pid_ident, sizeof(pid_ident), "aeswepd.%s", interface_name);
+ daemon_pid_file_ident = pid_ident;
+ snprintf(log_ident, sizeof(log_ident), "aeswepd(%s)", interface_name);
+ daemon_log_ident = log_ident;
+
+
+ if (_help) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (_kill) {
+ if (daemon_pid_file_kill(SIGINT) < 0) {
+ daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno));
+ exit(6);
+ }
+
+ exit(0);
+ }
+
+ if (_version) {
+ printf("aeswepd "VERSION"\n");
+ exit(0);
+ }
+
+ if (_check) {
+ pid_t pid = daemon_pid_file_is_running();
+
+ if (pid == (pid_t) -1)
+ printf("aeswepd not running.\n");
+ else
+ printf("aeswepd process for device %s running as pid %u.\n", interface_name, pid);
+
+ exit(pid == 0 ? 255 : 0);
+ }
+
+ if (_list_keys) {
+ if (daemon_pid_file_is_running() == (pid_t) -1) {
+ daemon_log(LOG_ERR, "Daemon not running!");
+ exit(1);
+ }
+
+ do_list_keys();
+ exit(0);
+ }
+
+ if (!_key_set) {
+ daemon_log(LOG_ERR, "Not AES key specified!");
+ exit(1);
+ }
+
+ if ((r = parse_hex(ln, aes_key, sizeof(aes_key))) < 0) {
+ daemon_log(LOG_ERR, "Failed to parse AES key at position %i!", -r-1);
+ exit(1);
+ }
+
+ if (r != 16) {
+ daemon_log(LOG_ERR, "AES key to short: 16 bytes required, %i bytes read", r);
+ exit(1);
+ }
+
+ if (!use_syslog)
+ daemon_log_use = DAEMON_LOG_STDERR;
+}
+
+int main(int argc, char *argv[]) {
+ struct interface *i = NULL;
+ int r = 1, j;
+ pid_t pid;
+
+ daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
+
+ parse_args(argc, argv);
+
+ if (geteuid() != 0) {
+ daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary.");
+ goto finish;
+ }
+
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
+ goto finish;
+
+ }
+
+ if (daemonize) {
+ pid_t pid;
+
+ if (wait_on_fork)
+ if (daemon_retval_init() < 0) {
+ daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((pid = daemon_fork()) < 0)
+ goto finish;
+
+ if (pid) {
+ int c = 0;
+
+ // Parent process
+
+ if (wait_on_fork)
+ if ((c = daemon_retval_wait(60)) < 0) {
+ daemon_log(LOG_WARNING, "Killing background process.");
+ kill(pid, SIGTERM);
+ }
+
+ r = c;
+ goto finish;
+ }
+ }
+
+
+ for (j = 0; j < MAX_WEP_KEYS; j++)
+ key_map[j] = j;
+
+ if (!(i = interface_open(interface_name)) < 0)
+ goto finish;
+
+ if (go(i) < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+
+ if (i)
+ interface_close(i);
+
+ if (interface_name)
+ free(interface_name);
+
+ return r;
+}
diff --git a/src/aeswepd.h b/src/aeswepd.h
new file mode 100644
index 0000000..c3be819
--- /dev/null
+++ b/src/aeswepd.h
@@ -0,0 +1,14 @@
+#ifndef fooaeswepdhfoo
+#define fooaeswepdhfoo
+
+#include <stdint.h>
+
+#define WEP_KEY_LEN 13
+#define AES_KEY_LEN 16
+#define MAX_WEP_KEYS 16
+
+extern int n_max_keys;
+extern int key_map[MAX_WEP_KEYS];
+
+
+#endif
diff --git a/src/assocwatch.c b/src/assocwatch.c
new file mode 100644
index 0000000..c3bf5f2
--- /dev/null
+++ b/src/assocwatch.c
@@ -0,0 +1,88 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <assert.h>
+#include <libdaemon/dlog.h>
+#include "wireless.h"
+
+#include "util.h"
+#include "assocwatch.h"
+#include "nlapi.h"
+
+static int handle_wireless_event(int idx, uint8_t *data, int l, int (*cb) (int idx, struct hw_addr *a)) {
+ struct iw_event *e;
+ int hs;
+ struct hw_addr ap;
+
+ e = (struct iw_event*) data;
+ hs = sizeof(struct iw_event)-sizeof(union iwreq_data);
+ while (l >= sizeof(struct iw_event)) {
+
+ if (e->len < hs) {
+ daemon_log(LOG_ERR, "ASSOCWATCH: Recieved corrupt wireless event\n");
+ return -1;
+ }
+
+ switch (e->cmd) {
+
+ case SIOCGIWAP:
+ if (e->len < hs + sizeof(struct sockaddr)) {
+ daemon_log(LOG_ERR, "ASSOCWATCH: Recieved corrupt AP wireless event\n");
+ return -1;
+ }
+
+ memcpy(ap.addr, e->u.ap_addr.sa_data, ETH_ALEN);
+
+ if (cb(idx, is_assoc_ap(&ap) ? &ap : NULL) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ l -= e->len;
+ e = (struct iw_event*) (((uint8_t*) e) + e->len);
+ }
+
+ return 0;
+}
+
+static int callback(struct nlmsghdr *n, void *u) {
+ int (*cb) (int idx, struct hw_addr *a) = u;
+
+ if (n->nlmsg_type == RTM_NEWLINK) {
+ struct ifinfomsg* ifi;
+ struct rtattr *a;
+ int la;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
+ daemon_log(LOG_ERR, "ASSOCWATCH: Corrupt NETLINK message\n");
+ return -1;
+ }
+
+ ifi = NLMSG_DATA(n);
+ a = (void*) ifi + NLMSG_ALIGN(sizeof(struct ifinfomsg));
+ la = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+
+ while (RTA_OK(a, la)) {
+ if(a->rta_type == IFLA_WIRELESS)
+ handle_wireless_event(ifi->ifi_index, RTA_DATA(a), RTA_PAYLOAD(a), cb);
+
+ a = RTA_NEXT(a, la);
+ }
+ }
+
+ return 0;
+}
+
+
+int assocwatch_init(int (*cb) (int idx, struct hw_addr *a)) {
+ return nlapi_register(callback, cb);
+}
diff --git a/src/assocwatch.h b/src/assocwatch.h
new file mode 100644
index 0000000..7a06f87
--- /dev/null
+++ b/src/assocwatch.h
@@ -0,0 +1,8 @@
+#ifndef fooassocwatchfoo
+#define fooassocwatchfoo
+
+#include "util.h"
+
+int assocwatch_init(int (*cb) (int idx, struct hw_addr *a));
+
+#endif
diff --git a/src/exec.c b/src/exec.c
new file mode 100644
index 0000000..2e8b33b
--- /dev/null
+++ b/src/exec.c
@@ -0,0 +1,138 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <libdaemon/dlog.h>
+#include <libdaemon/dsignal.h>
+
+#include "exec.h"
+
+int log_exec(const char *dir, const char *prog, const char *arg) {
+ pid_t pid;
+ int p[2];
+ unsigned n = 0;
+ static char buf[256];
+ int sigfd, r;
+ fd_set fds;
+
+ daemon_log(LOG_INFO, "Running '%s %s'", prog, arg);
+
+ if (pipe(p) < 0) {
+ daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if ((pid = fork()) < 0) {
+ daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno));
+ return -1;
+
+ } else if (pid == 0) {
+ dup2(p[1], 1);
+ dup2(p[1], 2);
+
+ if (p[0] > 2)
+ close(p[0]);
+
+ if (p[1] > 2)
+ close(p[1]);
+
+ close(0);
+ open("/dev/null", O_RDONLY);
+
+ umask(0022); // Set up a sane umask
+
+ if (dir && chdir(dir) < 0) {
+ daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir);
+ chdir("/");
+ }
+
+ execl(prog, prog, arg, 0);
+
+ daemon_log(LOG_ERR, "execl(%s) failed: %s\n", prog, strerror(errno));
+
+ _exit(EXIT_FAILURE);
+ }
+
+ close(p[1]);
+
+ FD_ZERO(&fds);
+ FD_SET(p[0], &fds);
+ FD_SET(sigfd = daemon_signal_fd(), &fds);
+
+ n = 0;
+
+ for (;;) {
+ fd_set qfds = fds;
+
+ if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ daemon_log(LOG_ERR, "select() failed: %s", strerror(errno));
+ return -1;
+ }
+
+
+ if (FD_ISSET(p[0], &qfds)) {
+ char c;
+
+ if (read(p[0], &c, 1) != 1)
+ break;
+
+ buf[n] = c;
+
+ if (c == '\n' || n >= sizeof(buf) - 2) {
+ if (c != '\n') n++;
+ buf[n] = 0;
+
+ if (buf[0])
+ daemon_log(LOG_INFO, "client: %s", buf);
+
+ n = 0;
+ } else
+ n++;
+ }
+
+ if (FD_ISSET(sigfd, &qfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ break;
+ }
+
+ if (sig != SIGCHLD) {
+ daemon_log(LOG_WARNING, "Killing child.");
+ kill(pid, SIGTERM);
+ }
+
+ break;
+ }
+ }
+
+ if (n > 0) {
+ buf[n] = 0;
+ daemon_log(LOG_WARNING, "client: %s", buf);
+ }
+
+ waitpid(pid, &r, 0);
+
+ close(p[0]);
+
+ if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) {
+ daemon_log(LOG_ERR, "Program execution failed, return value is %i.", WEXITSTATUS(r));
+ return -1;
+ } else {
+ daemon_log(LOG_INFO, "Program executed successfully.");
+ return 0;
+ }
+}
diff --git a/src/exec.h b/src/exec.h
new file mode 100644
index 0000000..419096c
--- /dev/null
+++ b/src/exec.h
@@ -0,0 +1,6 @@
+#ifndef fooexehfoo
+#define fooexehfoo
+
+int log_exec(const char *dir, const char *prog, const char *arg);
+
+#endif
diff --git a/src/ifmonitor.c b/src/ifmonitor.c
new file mode 100644
index 0000000..b1124a5
--- /dev/null
+++ b/src/ifmonitor.c
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "util.h"
+#include "nlapi.h"
+
+static int callback(struct nlmsghdr *n, void *u) {
+ int (*cb)(int b, int index, unsigned short type, const char *name) = u;
+
+ if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) {
+ struct rtattr *a;
+ struct ifinfomsg *i;
+ char ifname[IFNAMSIZ+1];
+ int la;
+
+ i = NLMSG_DATA(n);
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
+ fprintf(stderr, "NETLINK: Packet too small or truncated! (2)\n");
+ return -1;
+ }
+
+ memset(&ifname, 0, sizeof(ifname));
+
+ a = (void*) i + NLMSG_ALIGN(sizeof(struct ifinfomsg));
+ la = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+
+ while (RTA_OK(a, la)) {
+
+ if(a->rta_type == IFLA_IFNAME)
+ strncpy(ifname, RTA_DATA(a), MIN(RTA_PAYLOAD(a), IFNAMSIZ));
+
+ a = RTA_NEXT(a, la);
+ }
+
+ if (cb(n->nlmsg_type == RTM_NEWLINK, i->ifi_index, i->ifi_type, ifname[0] ? ifname : NULL) < 0)
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+int ifmonitor_init(int (*cb) (int b, int index, unsigned short type, const char *name)) {
+ return nlapi_register(callback, cb);
+}
+
+#if 0
+
+int test(int b, int index, unsigned short type, const char * name) {
+ printf("CALLBACK %s %s %i %i\n", b ? "new" : "del", name, index, type);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int r = 1;
+
+ if (nlapi_open(RTMGRP_LINK) < 0)
+ goto finish;
+
+ if (ifmonitor_init(test) < 0)
+ goto finish;
+
+ for (;;)
+ if (nlapi_work() < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ nlapi_close();
+
+ return r;
+}
+
+#endif
diff --git a/src/ifmonitor.h b/src/ifmonitor.h
new file mode 100644
index 0000000..cdbb5dd
--- /dev/null
+++ b/src/ifmonitor.h
@@ -0,0 +1,6 @@
+#ifndef fooifmonitorhfoo
+#define fooifmonitorhfoo
+
+int ifmonitor_init(int (*cb) (int b, int index, unsigned short type, const char *name));
+
+#endif
diff --git a/src/interface.c b/src/interface.c
new file mode 100644
index 0000000..5fa7dfb
--- /dev/null
+++ b/src/interface.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "interface.h"
+#include "util.h"
+
+struct interface *interface_open(char *name) {
+ struct interface* i = NULL;
+
+ if (!(i = (struct interface*) malloc(sizeof(struct interface))))
+ return NULL;
+
+ memset(i, 0, sizeof(struct interface));
+ strncpy(i->name, name, IFNAMSIZ);
+
+ if ((i->fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ return i;
+
+fail:
+
+ if (i)
+ interface_close(i);
+
+ return NULL;
+}
+
+void interface_close(struct interface *i) {
+ assert(i);
+
+ if (i->fd >= 0)
+ close(i->fd);
+
+ free(i);
+}
+
+int interface_is_assoc(struct interface *i, struct hw_addr *ap) {
+ struct hw_addr ap2;
+ struct iwreq req;
+ struct iw_statistics q;
+
+ assert(i);
+
+ if (ap)
+ memset(ap->addr, 0, ETH_ALEN);
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ if (ioctl(i->fd, SIOCGIWAP, &req) < 0) {
+ fprintf(stderr, "Failed to get AP address\n");
+ return -1;
+ }
+
+ memcpy(ap2.addr, &(req.u.ap_addr.sa_data), ETH_ALEN);
+
+ if (!is_assoc_ap(&ap2))
+ return 0;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.data.pointer = (caddr_t) &q;
+ req.u.data.length = sizeof(q);
+ req.u.data.flags = 1;
+
+ if (ioctl(i->fd, SIOCGIWSTATS, &req) < 0) {
+ fprintf(stderr, "Failed to get interface quality\n");
+ return -1;
+ }
+
+
+ if (q.qual.qual <= 0)
+ return 0;
+
+ if (ap)
+ memcpy(ap->addr, ap2.addr, ETH_ALEN);
+
+ return 1;
+}
diff --git a/src/interface.h b/src/interface.h
new file mode 100644
index 0000000..b6f3b9d
--- /dev/null
+++ b/src/interface.h
@@ -0,0 +1,17 @@
+#ifndef foointerfacehfoo
+#define foointerfacehfoo
+
+#include <sys/socket.h>
+#include "wireless.h"
+#include "util.h"
+
+struct interface {
+ char name[IFNAMSIZ+1];
+ int fd;
+};
+
+struct interface *interface_open(char *name);
+void interface_close(struct interface *i);
+int interface_is_assoc(struct interface *i, struct hw_addr *a);
+
+#endif
diff --git a/src/iwapi.c b/src/iwapi.c
new file mode 100644
index 0000000..5662548
--- /dev/null
+++ b/src/iwapi.c
@@ -0,0 +1,225 @@
+#include <net/if_arp.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include "iwapi.h"
+
+int iw_set_essid(struct interface *i, const char* essid) {
+ struct iwreq req;
+ char e[IW_ESSID_MAX_SIZE + 1];
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ if (essid && *essid) {
+ memset(&e, 0, sizeof(e));
+ strncpy(e, essid, IW_ESSID_MAX_SIZE);
+
+ req.u.essid.pointer = e;
+ req.u.essid.length = strlen(essid) + 1;
+ req.u.essid.flags = 1;
+ }
+
+ if (ioctl(i->fd, SIOCSIWESSID, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWESSID): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int iw_set_mode(struct interface *i, int m) {
+ struct iwreq req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+ req.u.mode = m;
+
+ if (ioctl(i->fd, SIOCSIWMODE, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWMODE): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int iw_set_freq(struct interface *i, struct iw_freq *f) {
+ struct iwreq req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+ req.u.freq = *f;
+
+ if (ioctl(i->fd, SIOCSIWFREQ, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWFREQ): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int iw_set_ap(struct interface *i, struct hw_addr *ap) {
+ struct iwreq req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(req.u.ap_addr.sa_data, ap->addr, ETH_ALEN);
+
+ if (ioctl(i->fd, SIOCSIWAP, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWAP): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int iw_scan(struct interface *i) {
+ struct iwreq req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.param.flags = IW_SCAN_DEFAULT;
+ req.u.param.value = 0;
+
+ if (ioctl(i->fd, SIOCSIWSCAN, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWSCAN): %s\n", strerror(errno));
+ return -1;
+ }
+
+// fprintf(stderr, "scan!\n");
+
+ return 0;
+}
+
+
+int iw_scan_result(struct interface *i, int (*callback)(struct ap_info* ap)) {
+ struct ap_info ap;
+ int f, l, hs;
+ struct iwreq req;
+ struct iw_event *e;
+ uint8_t buffer[IW_SCAN_MAX_DATA];
+
+ assert(i && callback);
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.data.pointer = buffer;
+ req.u.data.flags = 0;
+ req.u.data.length = sizeof(buffer);
+
+ if (ioctl(i->fd, SIOCGIWSCAN, &req) < 0) {
+ if (errno == EAGAIN)
+ return 1;
+
+ fprintf(stderr, "ioctl(SIOCGIWSCAN): %s\n", strerror(errno));
+ return -1;
+ }
+
+// fprintf(stderr, "scan response!\n");
+
+ e = (struct iw_event*) req.u.data.pointer;
+ l = req.u.data.length;
+ f = 0;
+ hs = sizeof(struct iw_event)-sizeof(union iwreq_data);
+ while (l >= sizeof(struct iw_event)) {
+ if (e->len < hs) {
+ fprintf(stderr, "Recieved bogus wireless event\n");
+ return -1;
+ }
+
+ if (!f)
+ memset(&ap, 0, sizeof(ap));
+
+ switch (e->cmd) {
+
+ case SIOCGIWAP:
+
+ f = 1;
+
+ if (e->len < hs + sizeof(struct sockaddr)) {
+ fprintf(stderr, "Corrupt scan result (1)\n");
+ return -1;
+ }
+
+ memcpy(&ap.ap, e->u.ap_addr.sa_data, ETH_ALEN);
+ break;
+
+
+ case SIOCGIWESSID:
+
+ if (e->len < hs + sizeof(struct iw_point)) {
+ fprintf(stderr, "Corrupt scan result (2)\n");
+ return -1;
+ }
+
+ memset(&ap.essid, 0, sizeof(ap.essid));
+ memcpy(&ap.essid, ((uint8_t*) e)+hs+sizeof(struct iw_point), MIN(sizeof(ap.essid)-1, e->len-hs-sizeof(struct iw_point)));
+
+ f |= 2;
+
+ break;
+
+ case SIOCGIWMODE:
+
+ if (e->len < hs + sizeof(__u32)) {
+ fprintf(stderr, "Corrupt scan result (3)\n");
+ return -1;
+ }
+
+ if (e->u.mode != IW_MODE_MASTER)
+ f = 0; // Ignore non-APs
+ else
+ f |= 4;
+
+ break;
+
+ case SIOCGIWFREQ:
+ if (e->len < hs + sizeof(struct iw_freq)) {
+ fprintf(stderr, "Corrupt scan result (4)\n");
+ return -1;
+ }
+
+ memcpy(&ap.freq, &e->u.freq, sizeof(struct iw_freq));
+ f |= 8;
+ break;
+ }
+
+ if (f == 15) {
+
+ //fprintf(stderr, "Scan successful\n");
+
+ if (callback(&ap) < 0)
+ return -1;
+
+ f = 0;
+ }
+
+
+ l -= e->len;
+ e = (struct iw_event*) (((uint8_t*) e) + e->len);
+ }
+ return 0;
+}
+
+int iw_tune(struct interface *i, struct ap_info *ap) {
+ assert(i && ap);
+
+ if (iw_set_mode(i, IW_MODE_INFRA) < 0)
+ return -1;
+
+ if (iw_set_essid(i, ap->essid) < 0)
+ return -1;
+
+ if (iw_set_freq(i, &ap->freq) < 0)
+ return -1;
+
+ if (iw_set_ap(i, &ap->ap) < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/src/iwapi.h b/src/iwapi.h
new file mode 100644
index 0000000..bf65829
--- /dev/null
+++ b/src/iwapi.h
@@ -0,0 +1,22 @@
+#ifndef fooiwapihfoo
+#define fooiwapihfoo
+
+#include "interface.h"
+#include "wireless.h"
+#include "util.h"
+
+struct ap_info {
+ struct hw_addr ap;
+ struct iw_freq freq;
+ char essid[IW_ESSID_MAX_SIZE + 1];
+};
+
+int iw_set_essid(struct interface *i, const char* essid);
+int iw_set_mode(struct interface *i, int m);
+int iw_set_freq(struct interface *i, struct iw_freq *f);
+int iw_set_ap(struct interface *i, struct hw_addr *ap);
+int iw_scan(struct interface *i);
+int iw_tune(struct interface *i, struct ap_info *ap);
+int iw_scan_result(struct interface *i, int (*callback)(struct ap_info*));
+
+#endif
diff --git a/src/iwkey.c b/src/iwkey.c
new file mode 100644
index 0000000..3875594
--- /dev/null
+++ b/src/iwkey.c
@@ -0,0 +1,87 @@
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "iwkey.h"
+#include "wireless.h"
+#include "util.h"
+
+static int n_used_keys = 0;
+
+int wep_key_add(struct interface *i, uint8_t w[WEP_KEY_LEN]) {
+ struct iwreq req;
+ assert(i);
+
+ if (n_used_keys >= n_max_keys) {
+ fprintf(stderr, "Too many keys added!\n");
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.encoding.pointer = w;
+ req.u.encoding.length = WEP_KEY_LEN;
+ req.u.encoding.flags = key_map[n_used_keys++]+1;
+
+ if (ioctl(i->fd, SIOCSIWENCODE, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWENCODE): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int wep_key_finish(struct interface *i) {
+ struct iwreq req;
+ assert(i);
+
+ if (n_used_keys) {
+ uint8_t tmp[WEP_KEY_LEN];
+ int j;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.encoding.pointer = tmp;
+ req.u.encoding.length = WEP_KEY_LEN;
+ req.u.encoding.flags = (key_map[n_used_keys-1]+1);
+
+ if (ioctl(i->fd, SIOCGIWENCODE, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCGIWENCODE): %s\n", strerror(errno));
+ return -1;
+ }
+
+ for (j = n_used_keys; j < n_max_keys; j++) {
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.encoding.pointer = tmp;
+ req.u.encoding.length = WEP_KEY_LEN;
+ req.u.encoding.flags = (key_map[j]+1);
+
+ if (ioctl(i->fd, SIOCSIWENCODE, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWENCODE): %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ }
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ);
+
+ req.u.encoding.pointer = NULL;
+ req.u.encoding.length = 0;
+ req.u.encoding.flags = IW_ENCODE_RESTRICTED;
+
+ if (ioctl(i->fd, SIOCSIWENCODE, &req) < 0) {
+ fprintf(stderr, "ioctl(SIOCSIWENCODE): %s\n", strerror(errno));
+ return -1;
+ }
+
+ n_used_keys = 0;
+
+ return 0;
+}
diff --git a/src/iwkey.h b/src/iwkey.h
new file mode 100644
index 0000000..8e412d4
--- /dev/null
+++ b/src/iwkey.h
@@ -0,0 +1,11 @@
+#ifndef fooiwkeyhfoo
+#define fooiwkeyhfoo
+
+#include <stdint.h>
+#include "interface.h"
+#include "aeswepd.h"
+
+int wep_key_add(struct interface *i, uint8_t w[WEP_KEY_LEN]);
+int wep_key_finish(struct interface *i);
+
+#endif
diff --git a/src/nlapi.c b/src/nlapi.c
new file mode 100644
index 0000000..1f6e690
--- /dev/null
+++ b/src/nlapi.c
@@ -0,0 +1,111 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <libdaemon/dlog.h>
+
+#include "nlapi.h"
+
+int nlapi_fd = -1;
+
+struct callback_info {
+ nlapi_callback_t callback;
+ void *userdata;
+ struct callback_info * next;
+};
+
+struct callback_info *callbacks = NULL;
+
+int nlapi_open(uint32_t groups) {
+ struct sockaddr_nl addr;
+
+ if ((nlapi_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+ fprintf(stderr, "socket(PF_NETLINK): %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = groups;
+ addr.nl_pid = getpid();
+
+ if (bind(nlapi_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(nlapi_fd);
+ fprintf(stderr, "bind(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int nlapi_work(int block) {
+
+ assert(nlapi_fd >= 0);
+
+ for (;;) {
+ int bytes;
+ char replybuf[1024];
+ struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
+
+ if ((bytes = recv(nlapi_fd, &replybuf, sizeof(replybuf), block ? 0 : MSG_DONTWAIT)) < 0) {
+
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ daemon_log(LOG_ERR, "NLAPI: recv(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+ struct callback_info *c;
+
+ if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) {
+ daemon_log(LOG_ERR, "NLAPI: Packet too small or truncated!\n");
+ return -1;
+ }
+
+ for (c = callbacks; c; c = c->next)
+ if (c->callback(p, c->userdata) < 0)
+ return -1;
+ }
+
+ if (block)
+ break;
+ }
+
+ return 0;
+}
+
+void nlapi_close(void) {
+ if (nlapi_fd >= 0)
+ close(nlapi_fd);
+ nlapi_fd = -1;
+
+
+ while (callbacks) {
+ struct callback_info *c = callbacks;
+ callbacks = callbacks->next;
+ free(c);
+ }
+}
+
+int nlapi_register(int (*callback) (struct nlmsghdr *n, void *u), void *u) {
+ struct callback_info *c;
+
+ assert(callback);
+
+ if (!(c = malloc(sizeof(struct callback_info)))) {
+ daemon_log(LOG_ERR, "NLAPI: Not enough memory.\n");
+ return -1;
+ }
+
+ c->callback = callback;
+ c->userdata = u;
+
+ c->next = callbacks;
+ callbacks = c;
+ return 0;
+}
diff --git a/src/nlapi.h b/src/nlapi.h
new file mode 100644
index 0000000..11ee81d
--- /dev/null
+++ b/src/nlapi.h
@@ -0,0 +1,19 @@
+#ifndef foonlapihfoo
+#define foonlapihfoo
+
+#include <stdint.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+typedef int (*nlapi_callback_t) (struct nlmsghdr *n, void *u);
+
+extern int nlapi_fd;
+
+int nlapi_open(uint32_t groups);
+void nlapi_close(void);
+int nlapi_work(int block);
+int nlapi_register(nlapi_callback_t cb, void *u);
+
+#endif
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..4728752
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,136 @@
+#include <unistd.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <libdaemon/dlog.h>
+
+#include "util.h"
+
+struct hw_addr null_ap = { { 0, 0, 0, 0, 0, 0 } };
+
+void print_hex(FILE *f, uint8_t *w, int l) {
+ while (l > 0) {
+ fprintf(f, "%02x", *(w++));
+ l--;
+ }
+}
+
+int hw_addr_equal(struct hw_addr *a, struct hw_addr *b) {
+ return memcmp(a->addr, b->addr, ETH_ALEN) == 0;
+}
+
+int is_assoc_ap(struct hw_addr *ap) {
+ int b, j;
+ b = 1;
+ assert(ap);
+
+ for (j = 1; j < ETH_ALEN; j++)
+ if (ap->addr[j] != ap->addr[0]) {
+ b = 0;
+ break;
+ }
+
+ return !b || (ap->addr[0] != 0xFF && ap->addr[0] != 0x44 && ap->addr[0] != 0x00);
+}
+
+
+void print_hw_addr(FILE*f, struct hw_addr *a) {
+ fprintf(f, "%02x:%02x:%02x:%02x:%02x:%02x",
+ a->addr[0], a->addr[1], a->addr[2],
+ a->addr[3], a->addr[4], a->addr[5]);
+}
+
+void snprint_hw_addr(char *c, int l, struct hw_addr *a) {
+ snprintf(c, l, "%02x:%02x:%02x:%02x:%02x:%02x",
+ a->addr[0], a->addr[1], a->addr[2],
+ a->addr[3], a->addr[4], a->addr[5]);
+}
+
+
+int parse_hex(char *s, uint8_t *b, int l) {
+ int n = 0;
+ char *p;
+ int nibble = 0;
+
+ for (p = s; *p && l >= 0; p++) {
+ int c;
+
+ if (*p >= '0' && *p <= '9')
+ c = *p - '0';
+ else if (*p >= 'A' && *p <= 'F')
+ c = *p - 'A' + 10;
+ else if (*p >= 'a' && *p <= 'f')
+ c = *p - 'a' + 10;
+ else if (*p == ':' || *p == '.')
+ continue;
+ else
+ return -(p-s)-1;
+
+ if (!nibble)
+ *b = c << 4;
+ else {
+ *b |= c & 15;
+ b++;
+ l--;
+ n++;
+ }
+
+ nibble = !nibble;
+ }
+
+ if (*p)
+ return -(p-s)-1;
+
+ return n;
+}
+
+
+int get_ifname(int idx, char *p, int l) {
+ struct ifreq req;
+ int s;
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ daemon_log(LOG_ERR, "socket() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.ifr_ifindex = idx;
+
+ if(ioctl(s, SIOCGIFNAME, &req) < 0) {
+ close(s);
+ daemon_log(LOG_ERR, "SIOCGIFNAME failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ close(s);
+
+ strncpy(p, req.ifr_name, l-1);
+ p[l-1] = 0;
+
+ return 0;
+}
+
+int is_iface_available(char *p) {
+ struct ifreq req;
+ int s, r;
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ daemon_log(LOG_ERR, "socket() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_name, p, IFNAMSIZ);
+
+ if ((r = ioctl(s, SIOCGIFINDEX, &req)) < 0 && errno != ENODEV) {
+ daemon_log(LOG_ERR, "SIOCGIFINDEX failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ close(s);
+ return r >= 0 && req.ifr_ifindex >= 0;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..8241ddf
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,31 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+struct hw_addr {
+ uint8_t addr[ETH_ALEN];
+};
+
+
+extern struct hw_addr null_ap;
+
+void print_hex(FILE *f, uint8_t *w, int l);
+void print_hw_addr(FILE*f, struct hw_addr *a);
+void snprint_hw_addr(char *c, int l, struct hw_addr *a);
+int parse_hex(char *s, uint8_t *b, int l);
+int hw_addr_equal(struct hw_addr *a, struct hw_addr *b);
+int is_assoc_ap(struct hw_addr *ap);
+int get_ifname(int idx, char *p, int l);
+int is_iface_available(char *p);
+
+#endif
diff --git a/src/waproamd.c b/src/waproamd.c
new file mode 100644
index 0000000..c2af3af
--- /dev/null
+++ b/src/waproamd.c
@@ -0,0 +1,667 @@
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <libdaemon/dpid.h>
+#include <libdaemon/dlog.h>
+#include <libdaemon/dfork.h>
+#include <libdaemon/dsignal.h>
+
+#include "iwapi.h"
+#include "interface.h"
+#include "exec.h"
+#include "nlapi.h"
+#include "ifmonitor.h"
+#include "assocwatch.h"
+
+#ifndef SYSCONFDIR
+#define SYSCONFDIR "/etc/waproamd"
+#endif
+
+#ifndef SCRIPTDIR
+#define SCRIPTDIR SYSCONFDIR"/scripts"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+char *interface_name = NULL;
+int interface_index = -1;
+
+int disabled = 0,
+ associated = 0;
+
+struct hw_addr associated_ap;
+struct hw_addr current_ap;
+
+int use_assocwatch = 1,
+ use_ifmonitor = 0,
+ daemonize = 1,
+ wait_on_fork = 0,
+ use_syslog = 1;
+
+int poll_interval = 5,
+ scan_interval = 10;
+
+char log_ident[32], pid_ident[32];
+
+int issue_scan(struct interface *i) {
+
+ //fprintf(stderr, "Scanning...\n");
+
+ if (iw_set_mode(i, IW_MODE_INFRA) < 0)
+ return -1;
+
+ if (iw_set_ap(i, &null_ap) < 0)
+ return -1;
+
+ if (iw_set_essid(i, NULL) < 0)
+ return -1;
+
+ if (iw_scan(i) < 0)
+ return -1;
+
+ return 0;
+}
+
+void get_script_path(char *path, int l, struct hw_addr *ap) {
+ assert(path && l);
+
+ if (!ap) {
+ snprintf(path, l, "%s/default", SCRIPTDIR);
+ return;
+ }
+
+ snprintf(path, l, "%s/%02x:%02x:%02x:%02x:%02x:%02x",
+ SCRIPTDIR,
+ ap->addr[0], ap->addr[1], ap->addr[2],
+ ap->addr[3], ap->addr[4], ap->addr[5]);
+}
+
+struct ap_info selected_ap;
+int selected_ap_has_script;
+int selected_ap_valid;
+
+static int scan_result_cb(struct ap_info*ap) {
+ int b;
+ char path[PATH_MAX];
+
+ assert(ap);
+ get_script_path(path, sizeof(path), &ap->ap);
+ b = access(path, X_OK) == 0;
+
+ if (selected_ap_valid) {
+
+ if (!b || selected_ap_has_script)
+ return 0;
+ }
+
+ memcpy(&selected_ap, ap, sizeof(struct ap_info));
+ selected_ap_valid = 1;
+ selected_ap_has_script = 1;
+ return 0;
+}
+
+int read_scan(struct interface *i, struct ap_info **ap) {
+ int r;
+ selected_ap_valid = 0;
+ selected_ap_has_script = 0;
+
+ if ((r = iw_scan_result(i, scan_result_cb)) < 0)
+ return -1;
+
+ if (r == 1)
+ return 1;
+
+ *ap = selected_ap_valid ? &selected_ap : NULL;
+
+ return 0;
+}
+
+int run_script(struct hw_addr *ap, const char *arg) {
+ char path[PATH_MAX];
+
+ get_script_path(path, sizeof(path), ap);
+
+ if (access(path, X_OK) < 0)
+ get_script_path(path, sizeof(path), NULL);
+
+ return log_exec(SCRIPTDIR, path, arg);
+};
+
+
+int set_current_ap(struct hw_addr *a) {
+ char t[32];
+
+ if (!a)
+ a = &null_ap;
+
+ if (!hw_addr_equal(a, &current_ap)) {
+
+ if (!hw_addr_equal(&current_ap, &null_ap)) {
+ if (run_script(&current_ap, "stop") < 0)
+ return -1;
+ }
+
+ memcpy(&current_ap, a, sizeof(struct hw_addr));
+
+ snprint_hw_addr(t, sizeof(t), &current_ap);
+ setenv("AP", t, 1);
+ setenv("IFACE", interface_name, 1);
+
+ if (!hw_addr_equal(&current_ap, &null_ap)) {
+ fprintf(stderr, "Selected AP ");
+ print_hw_addr(stderr, &current_ap);
+ fprintf(stderr, "\n");
+
+ if (run_script(&current_ap, "start") < 0)
+ return -1;
+ }
+ }
+
+
+ return 0;
+}
+
+int ifmonitor_cb(int b, int index, unsigned short type, const char *name) {
+ if (!name)
+ return 0;
+
+ if (!strcmp(name, interface_name)) {
+ interface_index = index;
+ disabled = !b;
+ }
+ return 0;
+}
+
+int assocwatch_cb(int index, struct hw_addr *a) {
+ char name[IFNAMSIZ+1];
+
+ if (get_ifname(index, name, sizeof(name)) < 0)
+ return -1;
+
+ if (!strcmp(name, interface_name)) {
+ interface_index = index;
+ disabled = 0;
+ if ((associated = !!a))
+ memcpy(&associated_ap, a, sizeof(struct hw_addr));
+ }
+
+ return 0;
+}
+
+int go(struct interface *i) {
+ time_t next_scan;
+ int scanning = 0;
+ int send_retval = 1;
+ int r = -1, sigfd;
+ fd_set fds;
+
+ daemon_log(LOG_INFO, "waproamd "VERSION" initializing%s%s.", use_ifmonitor ? ", using NETLINK device monitoring" : "", use_assocwatch ? ", using wireless event notifications" : "");
+
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc());
+ goto finish;
+ }
+
+ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, -1) < 0) {
+ daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno));
+ goto finish;
+ }
+
+ if (nlapi_open(RTMGRP_LINK) < 0)
+ goto finish;
+
+ if (use_ifmonitor) {
+ int b;
+
+ if ((b = is_iface_available(interface_name)) < 0)
+ goto finish;
+
+ disabled = !b;
+ } else
+ disabled = 0;
+
+ memset(&current_ap, 0, sizeof(current_ap));
+
+ if ((associated = interface_is_assoc(i, &associated_ap)) < 0) {
+ if (!use_ifmonitor)
+ goto finish;
+
+ associated = 0;
+ }
+
+ daemon_log(LOG_INFO, "Currently %sassociated, interface %s.", associated ? "" : "not ", disabled ? "disabled" : "enabled");
+
+ if (use_assocwatch)
+ if (assocwatch_init(assocwatch_cb) < 0)
+ goto finish;
+
+ if (use_ifmonitor)
+ if (ifmonitor_init(ifmonitor_cb) < 0)
+ goto finish;
+
+ daemon_log(LOG_INFO, "Initialization complete.");
+
+ set_current_ap(&associated_ap);
+ next_scan = associated || disabled ? (time_t) -1 : 0;
+
+ if (daemonize && wait_on_fork) {
+ daemon_retval_send(0);
+ send_retval = 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(sigfd = daemon_signal_fd(), &fds);
+ FD_SET(nlapi_fd, &fds);
+
+ for (;;) {
+ fd_set qfds;
+ struct timeval tv, *ptv;
+ time_t now = time(NULL);
+ int a, d;
+
+ if (next_scan != (time_t) -1 && next_scan <= now) {
+ if (issue_scan(i) < 0) {
+ if (!use_ifmonitor)
+ goto finish;
+ } else
+ scanning = 1;
+
+ next_scan = (time_t) -1;
+ }
+
+ ptv = NULL;
+
+ if (scanning) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ ptv = &tv;
+ } else if (!use_assocwatch) {
+ tv.tv_sec = poll_interval;
+ tv.tv_usec = 0;
+ ptv = &tv;
+ }
+
+ if (next_scan != (time_t) -1) {
+ struct timeval tv2;
+ now = time(NULL);
+ tv2.tv_sec = next_scan > now ? next_scan - now : 0;
+ tv2.tv_usec = 0;
+
+ if (!ptv || tv2.tv_sec < tv.tv_sec) {
+ tv.tv_sec = tv2.tv_sec;
+ tv.tv_usec = tv2.tv_usec;
+ ptv = &tv;
+ }
+ }
+
+ qfds = fds;
+
+ if (select(FD_SETSIZE, &qfds, NULL, NULL, ptv) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ fprintf(stderr, "select() failed: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ a = associated;
+ d = disabled;
+
+ if (FD_ISSET(nlapi_fd, &qfds))
+ if (nlapi_work(0) < 0)
+ goto finish;
+
+ if (!use_assocwatch) {
+ if ((associated = interface_is_assoc(i, &associated_ap)) < 0) {
+ if (!use_ifmonitor)
+ goto finish;
+
+ associated = 0;
+ }
+ }
+
+ /* Changed: enabled -> disabled */
+ if (!d && disabled) {
+ fprintf(stderr, "Interface disabled\n");
+
+ if (associated)
+ if (set_current_ap(&null_ap) < 0)
+ goto finish;
+
+ associated = 0;
+ }
+
+ /* Changed: disabled -> enabled */
+ if (d && !disabled) {
+ fprintf(stderr, "Interface enabled\n");
+ associated = 0;
+ }
+
+ if (!disabled) {
+ /* Changed: associated -> not associated */
+ if (a && !associated)
+ fprintf(stderr, "No longer associated.\n");
+
+ /* Changed: not associated -> associated */
+ if (!a && associated) {
+ if (set_current_ap(&associated_ap) < 0)
+ goto finish;
+
+ fprintf(stderr, "Associated.\n");
+ next_scan = (time_t) -1;
+ }
+
+ if (scanning) {
+ int r;
+ struct ap_info *ai = NULL;
+
+ if ((r = read_scan(i, &ai)) < 0) {
+ if (!use_ifmonitor)
+ goto finish;
+
+ scanning = 0;
+
+ } else if (!r) {
+
+ scanning = 0;
+
+ if (!associated) {
+
+ if (set_current_ap(ai ? &ai->ap : NULL) < 0)
+ goto finish;
+ }
+ }
+ }
+ }
+
+ if (FD_ISSET(sigfd, &qfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ goto finish;
+ }
+
+
+ switch (sig) {
+
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ r = 0;
+ goto finish;
+
+ case SIGHUP:
+ next_scan = 0;
+ break;
+
+ default:
+ daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig));
+ break;
+
+ }
+ }
+
+ if (next_scan == (time_t) -1 && !scanning)
+ if (!disabled && !associated)
+ next_scan = time(NULL) + scan_interval;
+
+ if (disabled || associated) {
+ scanning = 0;
+ next_scan = (time_t) -1;
+ }
+ }
+
+ r = 0;
+
+finish:
+
+ if (send_retval && daemonize && wait_on_fork)
+ daemon_retval_send(1);
+
+ nlapi_close();
+
+ daemon_pid_file_remove();
+ daemon_signal_done();
+
+ daemon_log(LOG_INFO, "Exiting.");
+
+ return r;
+}
+
+void usage(char *p) {
+ if (strrchr(p, '/'))
+ p = strchr(p, '/')+1;
+
+ printf("%s -- Wireless Access Point Roaming Daemon for 802.11b\n\n"
+ "Usage: %s [options]\n\n"
+ "Options:\n"
+ " -n --no-daemon Do not daemonize (for debugging) (%s)\n"
+ " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n"
+ " -i --iface=IFACE Specify network interface (%s)\n"
+ " -w --wait-on-fork Wait until daemon fork finished (%s)\n"
+ " -M --monitor Use interface monitoring (%s)\n"
+ " -e --no-event Don't use wireless event API (%s)\n"
+ " -t --scan-interval Specify scan interval (%i)\n"
+ " -p --poll-interval Specify association poll interval, unless using -e (%i)\n"
+ " -h --help Show this help\n"
+ " -k --kill Kill a running daemon\n"
+ " -c --check-running Check if a daemon is currently running\n"
+ " -v --version Show version\n",
+ p, p,
+ !daemonize ? "on" : "off",
+ !use_syslog ? "on" : "off",
+ interface_name,
+ wait_on_fork ? "on" : "off",
+ use_ifmonitor ? "on" : "off",
+ use_assocwatch ? "off" : "on",
+ scan_interval,
+ poll_interval);
+}
+
+void parse_args(int argc, char *argv[]) {
+ static struct option long_options[] = {
+ {"no-daemon", no_argument, 0, 'n'},
+ {"no-syslog", no_argument, 0, 's'},
+ {"iface", required_argument, 0, 'i'},
+ {"wait-on-fork", no_argument, 0, 'w'},
+ {"monitor", no_argument, 0, 'M'},
+ {"no-event", no_argument, 0, 'e'},
+ {"scan-interval", required_argument, 0, 't'},
+ {"poll-interval", required_argument, 0, 'p'},
+ {"help", no_argument, 0, 'h'},
+ {"kill", no_argument, 0, 'k'},
+ {"check-running", no_argument, 0, 'c'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int _help = 0, _kill = 0, _check = 0, _version = 0;
+
+ for (;;) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "nsi:whkcvMet:p:", long_options, &option_index)) < 0)
+ break;
+
+ switch (c) {
+ case 'n' :
+ daemonize = !daemonize;
+ break;
+
+ case 's' :
+ use_syslog = !use_syslog;
+ break;
+
+ case 'i' :
+ if (interface_name)
+ free(interface_name);
+ interface_name = strdup(optarg);
+ break;
+
+ case 'w':
+ wait_on_fork = !wait_on_fork;
+ break;
+
+ case 'M':
+ use_ifmonitor = !use_ifmonitor;
+ break;
+
+ case 'e':
+ use_assocwatch = !use_assocwatch;
+ break;
+
+ case 't':
+ if ((scan_interval = atoi(optarg)) < 0) {
+ daemon_log(LOG_ERR, "Scan interval must be a positive, nonzero integer.");
+ exit(1);
+ }
+
+ case 'p':
+ if ((poll_interval = atoi(optarg)) < 0) {
+ daemon_log(LOG_ERR, "Poll interval must be a positive, nonzero integer.");
+ exit(1);
+ }
+
+ case 'h':
+ _help = 1;
+ break;
+
+ case 'k':
+ _kill = 1;
+ break;
+
+ case 'c':
+ _check = 1;
+ break;
+
+ case 'v':
+ _version = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown parameter.\n");
+ exit(1);
+ }
+ }
+
+ if (!interface_name)
+ interface_name = strdup("wlan0");
+
+ snprintf(pid_ident, sizeof(pid_ident), "waproamd.%s", interface_name);
+ daemon_pid_file_ident = pid_ident;
+ snprintf(log_ident, sizeof(log_ident), "waproamd(%s)", interface_name);
+ daemon_log_ident = log_ident;
+
+
+ if (_help) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (_kill) {
+ if (daemon_pid_file_kill(SIGINT) < 0) {
+ daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno));
+ exit(6);
+ }
+
+ exit(0);
+ }
+
+ if (_version) {
+ printf("waproamd "VERSION"\n");
+ exit(0);
+ }
+
+ if (_check) {
+ pid_t pid = daemon_pid_file_is_running();
+
+ if (pid == (pid_t) -1)
+ printf("waproamd not running.\n");
+ else
+ printf("waproamd process for device %s running as pid %u.\n", interface_name, pid);
+
+ exit(pid == 0 ? 255 : 0);
+ }
+
+ if (!use_syslog)
+ daemon_log_use = DAEMON_LOG_STDERR;
+
+}
+
+int main(int argc, char *argv[]) {
+ struct interface *i = NULL;
+ int r = 1;
+ pid_t pid;
+
+ daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
+
+ parse_args(argc, argv);
+
+ if (geteuid() != 0) {
+ daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary.");
+ goto finish;
+ }
+
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
+ goto finish;
+
+ }
+
+ if (daemonize) {
+ pid_t pid;
+
+ if (wait_on_fork)
+ if (daemon_retval_init() < 0) {
+ daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((pid = daemon_fork()) < 0)
+ goto finish;
+
+ if (pid) {
+ int c = 0;
+
+ // Parent process
+
+ if (wait_on_fork)
+ if ((c = daemon_retval_wait(60)) < 0) {
+ daemon_log(LOG_WARNING, "Killing background process.");
+ kill(pid, SIGTERM);
+ }
+
+ r = c;
+ goto finish;
+ }
+ }
+
+ if (!(i = interface_open(interface_name)) < 0)
+ goto finish;
+
+ if (go(i) < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+
+ if (i)
+ interface_close(i);
+
+ if (interface_name)
+ free(interface_name);
+
+ return r;
+}
diff --git a/src/wireless.15.h b/src/wireless.15.h
new file mode 100644
index 0000000..bacf44b
--- /dev/null
+++ b/src/wireless.15.h
@@ -0,0 +1,698 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version : 15 12.7.02
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands. This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ * # net/core/dev.c (two place + add include)
+ * # net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ * # net/core/dev.c (two other places)
+ * # include/linux/netdevice.h (one place)
+ * # include/linux/proc_fs.h (one place)
+ *
+ * New driver API (2002 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ * # include/net/iw_handler.h
+ *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ * # include/linux/wireless.c
+ *
+ * Wireless Events (2002 -> onward) :
+ * --------------------------------
+ * Events are defined at the end of this file, and implemented in :
+ * # include/linux/wireless.c
+ *
+ * Other comments :
+ * --------------
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/types.h> /* for "caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+#include <linux/if.h> /* for IFNAMSIZ and co... */
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT 15
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ * Alan Cox start some incompatibles changes. I've integrated a bit more.
+ * - Encryption renamed to Encode to avoid US regulation problems
+ * - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ * - Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ * - Missing encoding definitions in range
+ * - Access points stuff
+ *
+ * V5 to V6
+ * --------
+ * - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ * - define IW_ESSID_MAX_SIZE and IW_MAX_AP
+ *
+ * V7 to V8
+ * --------
+ * - Changed my e-mail address
+ * - More 802.11 support (nickname, rate, rts, frag)
+ * - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ * - Support for 'mode of operation' (ad-hoc, managed...)
+ * - Support for unicast and multicast power saving
+ * - Change encoding to support larger tokens (>64 bits)
+ * - Updated iw_params (disable, flags) and use it for NWID
+ * - Extracted iw_point from iwreq for clarity
+ *
+ * V9 to V10
+ * ---------
+ * - Add PM capability to range structure
+ * - Add PM modifier : MAX/MIN/RELATIVE
+ * - Add encoding option : IW_ENCODE_NOKEY
+ * - Add TxPower ioctls (work like TxRate)
+ *
+ * V10 to V11
+ * ----------
+ * - Add WE version in range (help backward/forward compatibility)
+ * - Add retry ioctls (work like PM)
+ *
+ * V11 to V12
+ * ----------
+ * - Add SIOCSIWSTATS to get /proc/net/wireless programatically
+ * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
+ * - Add new statistics (frag, retry, beacon)
+ * - Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ * - Document creation of new driver API.
+ * - Extract union iwreq_data from struct iwreq (for new driver API).
+ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT
+ *
+ * V13 to V14
+ * ----------
+ * - Wireless Events support : define struct iw_event
+ * - Define additional specific event numbers
+ * - Add "addr" and "param" fields in union iwreq_data
+ * - AP scanning stuff (SIOCSIWSCAN and friends)
+ *
+ * V14 to V15
+ * ----------
+ * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg
+ * - Make struct iw_freq signed (both m & e), add explicit padding
+ * - Add IWEVCUSTOM for driver specific event/scanning token
+ * - Add IW_MAX_GET_SPY for driver returning a lot of addresses
+ * - Add IW_TXPOW_RANGE for range of Tx Powers
+ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+ * - Add IW_MODE_MONITOR for passive monitor
+ */
+
+/**************************** CONSTANTS ****************************/
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Wireless Identification */
+#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
+#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */
+/* SIOCGIWNAME is used to verify the presence of Wireless Extensions.
+ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"...
+ * Don't put the name of your driver there, it's useless. */
+
+/* Basic operations */
+#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */
+#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */
+#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */
+#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */
+#define SIOCSIWMODE 0x8B06 /* set operation mode */
+#define SIOCGIWMODE 0x8B07 /* get operation mode */
+#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */
+#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */
+
+/* Informative stuff */
+#define SIOCSIWRANGE 0x8B0A /* Unused */
+#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */
+#define SIOCSIWPRIV 0x8B0C /* Unused */
+#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */
+#define SIOCSIWSTATS 0x8B0E /* Unused */
+#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */
+/* SIOCGIWSTATS is strictly used between user space and the kernel, and
+ * is never passed to the driver (i.e. the driver will never see it). */
+
+/* Mobile IP support (statistics per MAC address) */
+#define SIOCSIWSPY 0x8B10 /* set spy addresses */
+#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
+
+/* Access Point manipulation */
+#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
+#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */
+#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */
+#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */
+#define SIOCGIWSCAN 0x8B19 /* get scanning results */
+
+/* 802.11 specific support */
+#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */
+#define SIOCGIWESSID 0x8B1B /* get ESSID */
+#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */
+#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */
+/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit
+ * within the 'iwreq' structure, so we need to use the 'data' member to
+ * point to a string in user space, like it is done for RANGE... */
+
+/* Other parameters useful in 802.11 and some other devices */
+#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */
+#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */
+#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */
+#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */
+#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */
+#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */
+#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */
+#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */
+#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */
+#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */
+
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */
+#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */
+#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */
+
+/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
+
+/* These 16 ioctl are wireless device private.
+ * Each driver is free to use them for whatever purpose it chooses,
+ * however the driver *must* export the description of those ioctls
+ * with SIOCGIWPRIV and *must* use arguments as defined below.
+ * If you don't follow those rules, DaveM is going to hate you (reason :
+ * it make mixed 32/64bit operation impossible).
+ */
+#define SIOCIWFIRSTPRIV 0x8BE0
+#define SIOCIWLASTPRIV 0x8BFF
+/* Previously, we were using SIOCDEVPRIVATE, but we now have our
+ * separate range because of collisions with other tools such as
+ * 'mii-tool'.
+ * We now have 32 commands, so a bit more space ;-).
+ * Also, all 'odd' commands are only usable by root and don't return the
+ * content of ifr/iwr to user (but you are not obliged to use the set/get
+ * convention, just use every other two command).
+ * And I repeat : you are not obliged to use them with iwspy, but you
+ * must be compliant with it.
+ */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST 0x8B00
+#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
+
+/* Even : get (world access), odd : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/* Those are *NOT* ioctls, do not issue request on them !!! */
+/* Most events use the same identifier as ioctl requests */
+
+#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */
+#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */
+#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */
+#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */
+#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */
+
+#define IWEVFIRST 0x8C00
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV
+ */
+
+#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */
+#define IW_PRIV_TYPE_NONE 0x0000
+#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */
+#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */
+#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
+#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
+
+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */
+
+#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES 16
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum bit rates in the range struct */
+#define IW_MAX_BITRATES 8
+
+/* Maximum tx powers in the range struct */
+#define IW_MAX_TXPOWER 8
+/* Note : if you more than 8 TXPowers, just set the max and min or
+ * a few of them in the struct iw_range. */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY 8 /* set */
+#define IW_MAX_GET_SPY 64 /* get */
+
+/* Maximum of address that you may get in the
+ list of access points in range */
+#define IW_MAX_AP 8
+
+/* Maximum size of the ESSID and NICKN strings */
+#define IW_ESSID_MAX_SIZE 32
+
+/* Modes of operation */
+#define IW_MODE_AUTO 0 /* Let the driver decides */
+#define IW_MODE_ADHOC 1 /* Single cell network */
+#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
+#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES 8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX 32 /* 256 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */
+#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */
+#define IW_ENCODE_MODE 0xF000 /* Modes defined below */
+#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */
+#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON 0x0000 /* No details... */
+#define IW_POWER_TYPE 0xF000 /* Type of parameter */
+#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */
+#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE 0x0F00 /* Power Management mode */
+#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */
+#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */
+#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */
+#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */
+#define IW_POWER_MIN 0x0001 /* Value is a minimum */
+#define IW_POWER_MAX 0x0002 /* Value is a maximum */
+#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+
+/* Transmit Power flags available */
+#define IW_TXPOW_TYPE 0x00FF /* Type of value */
+#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */
+#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */
+#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */
+
+/* Retry limits and lifetime flags available */
+#define IW_RETRY_ON 0x0000 /* No details... */
+#define IW_RETRY_TYPE 0xF000 /* Type of parameter */
+#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/
+#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */
+#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */
+#define IW_RETRY_MIN 0x0001 /* Value is a minimum */
+#define IW_RETRY_MAX 0x0002 /* Value is a maximum */
+#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+
+/* Scanning request flags */
+#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */
+#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */
+#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */
+#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */
+#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */
+#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */
+#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */
+#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */
+#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */
+/* Maximum size of returned data */
+#define IW_SCAN_MAX_DATA 4096 /* In bytes */
+
+/* Max number of char in custom event - use multiple of them if needed */
+#define IW_CUSTOM_MAX 256 /* In bytes */
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ * Generic format for most parameters that fit in an int
+ */
+struct iw_param
+{
+ __s32 value; /* The value of the parameter itself */
+ __u8 fixed; /* Hardware should not use auto select */
+ __u8 disabled; /* Disable the feature */
+ __u16 flags; /* Various specifc flags (if any) */
+};
+
+/*
+ * For all data larger than 16 octets, we need to use a
+ * pointer to memory allocated in user space.
+ */
+struct iw_point
+{
+ caddr_t pointer; /* Pointer to the data (in user space) */
+ __u16 length; /* number of fields or size in bytes */
+ __u16 flags; /* Optional params */
+};
+
+/*
+ * A frequency
+ * For numbers lower than 10^9, we encode the number in 'm' and
+ * set 'e' to 0
+ * For number greater than 10^9, we divide it by the lowest power
+ * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')...
+ * The power of 10 is in 'e', the result of the division is in 'm'.
+ */
+struct iw_freq
+{
+ __s32 m; /* Mantissa */
+ __s16 e; /* Exponent */
+ __u8 i; /* List index (when in range struct) */
+ __u8 pad; /* Unused - just for alignement */
+};
+
+/*
+ * Quality of the link
+ */
+struct iw_quality
+{
+ __u8 qual; /* link quality (%retries, SNR,
+ %missed beacons or better...) */
+ __u8 level; /* signal level (dBm) */
+ __u8 noise; /* noise level (dBm) */
+ __u8 updated; /* Flags to know if updated */
+};
+
+/*
+ * Packet discarded in the wireless adapter due to
+ * "wireless" specific problems...
+ * Note : the list of counter and statistics in net_device_stats
+ * is already pretty exhaustive, and you should use that first.
+ * This is only additional stats...
+ */
+struct iw_discarded
+{
+ __u32 nwid; /* Rx : Wrong nwid/essid */
+ __u32 code; /* Rx : Unable to code/decode (WEP) */
+ __u32 fragment; /* Rx : Can't perform MAC reassembly */
+ __u32 retries; /* Tx : Max MAC retries num reached */
+ __u32 misc; /* Others cases */
+};
+
+/*
+ * Packet/Time period missed in the wireless adapter due to
+ * "wireless" specific problems...
+ */
+struct iw_missed
+{
+ __u32 beacon; /* Missed beacons/superframe */
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics
+{
+ __u16 status; /* Status
+ * - device dependent for now */
+
+ struct iw_quality qual; /* Quality of the link
+ * (instant/mean/max) */
+ struct iw_discarded discard; /* Packet discarded counts */
+ struct iw_missed miss; /* Packet missed counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * This structure defines the payload of an ioctl, and is used
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union iwreq_data
+{
+ /* Config - generic */
+ char name[IFNAMSIZ];
+ /* Name : used to verify the presence of wireless extensions.
+ * Name of the protocol/provider... */
+
+ struct iw_point essid; /* Extended network name */
+ struct iw_param nwid; /* network id (or domain - the cell) */
+ struct iw_freq freq; /* frequency or channel :
+ * 0-1000 = channel
+ * > 1000 = frequency in Hz */
+
+ struct iw_param sens; /* signal level threshold */
+ struct iw_param bitrate; /* default bit rate */
+ struct iw_param txpower; /* default transmit power */
+ struct iw_param rts; /* RTS threshold threshold */
+ struct iw_param frag; /* Fragmentation threshold */
+ __u32 mode; /* Operation mode */
+ struct iw_param retry; /* Retry limits & lifetime */
+
+ struct iw_point encoding; /* Encoding stuff : tokens */
+ struct iw_param power; /* PM duration/timeout */
+ struct iw_quality qual; /* Quality part of statistics */
+
+ struct sockaddr ap_addr; /* Access point address */
+ struct sockaddr addr; /* Destination address (hw) */
+
+ struct iw_param param; /* Other small parameters */
+ struct iw_point data; /* Other large parameters */
+};
+
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ * Do I need to remind you about structure size (32 octets) ?
+ */
+struct iwreq
+{
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
+ } ifr_ifrn;
+
+ /* Data part (defined just above) */
+ union iwreq_data u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ * For those ioctl which want to exchange mode data that what could
+ * fit in the above structure...
+ */
+
+/*
+ * Range of parameters
+ */
+
+struct iw_range
+{
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Frequency */
+ __u16 num_channels; /* Number of channels [0; num - 1] */
+ __u8 num_frequency; /* Number of entry in the list */
+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
+ /* Note : this frequency list doesn't need to fit channel numbers */
+
+ /* signal level threshold range */
+ __s32 sensitivity;
+
+ /* Quality of link & SNR stuff */
+ struct iw_quality max_qual; /* Quality of the link */
+
+ /* Rates */
+ __u8 num_bitrates; /* Number of entries in the list */
+ __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */
+
+ /* RTS threshold */
+ __s32 min_rts; /* Minimal RTS threshold */
+ __s32 max_rts; /* Maximal RTS threshold */
+
+ /* Frag threshold */
+ __s32 min_frag; /* Minimal frag threshold */
+ __s32 max_frag; /* Maximal frag threshold */
+
+ /* Power Management duration & timeout */
+ __s32 min_pmp; /* Minimal PM period */
+ __s32 max_pmp; /* Maximal PM period */
+ __s32 min_pmt; /* Minimal PM timeout */
+ __s32 max_pmt; /* Maximal PM timeout */
+ __u16 pmp_flags; /* How to decode max/min PM period */
+ __u16 pmt_flags; /* How to decode max/min PM timeout */
+ __u16 pm_capa; /* What PM options are supported */
+
+ /* Encoder stuff */
+ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
+ __u8 num_encoding_sizes; /* Number of entry in the list */
+ __u8 max_encoding_tokens; /* Max number of tokens */
+
+ /* Transmit power */
+ __u16 txpower_capa; /* What options are supported */
+ __u8 num_txpower; /* Number of entries in the list */
+ __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */
+
+ /* Wireless Extension version info */
+ __u8 we_version_compiled; /* Must be WIRELESS_EXT */
+ __u8 we_version_source; /* Last update of source */
+
+ /* Retry limits and lifetime */
+ __u16 retry_capa; /* What retry options are supported */
+ __u16 retry_flags; /* How to decode max/min retry limit */
+ __u16 r_time_flags; /* How to decode max/min retry life */
+ __s32 min_retry; /* Minimal number of retries */
+ __s32 max_retry; /* Maximal number of retries */
+ __s32 min_r_time; /* Minimal retry lifetime */
+ __s32 max_r_time; /* Maximal retry lifetime */
+
+ /* Average quality of link & SNR */
+ struct iw_quality avg_qual; /* Quality of the link */
+ /* This should contain the average/typical values of the quality
+ * indicator. This should be the threshold between a "good" and
+ * a "bad" link (example : monitor going from green to orange).
+ * Currently, user space apps like quality monitors don't have any
+ * way to calibrate the measurement. With this, they can split
+ * the range between 0 and max_qual in different quality level
+ * (using a geometric subdivision centered on the average).
+ * I expect that people doing the user space apps will feedback
+ * us on which value we need to put in each driver...
+ */
+};
+
+/*
+ * Private ioctl interface information
+ */
+
+struct iw_priv_args
+{
+ __u32 cmd; /* Number of the ioctl to issue */
+ __u16 set_args; /* Type and number of args */
+ __u16 get_args; /* Type and number of args */
+ char name[IFNAMSIZ]; /* Name of the extension */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Wireless events are carried through the rtnetlink socket to user
+ * space. They are encapsulated in the IFLA_WIRELESS field of
+ * a RTM_NEWLINK message.
+ */
+
+/*
+ * A Wireless Event. Contains basically the same data as the ioctl...
+ */
+struct iw_event
+{
+ __u16 len; /* Real lenght of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ union iwreq_data u; /* IOCTL fixed payload */
+};
+
+/* Size of the Event prefix (including padding and alignement junk) */
+#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data))
+/* Size of the various events */
+#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ)
+#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32))
+#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point))
+#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality))
+
+/* Note : in the case of iw_point, the extra data will come at the
+ * end of the event */
+
+#endif /* _LINUX_WIRELESS_H */