From 1049edd1d1cacb85e8b4fc9daa91a434f2372f0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Nov 2003 00:22:24 +0000 Subject: adhoc mode git-svn-id: file:///home/lennart/svn/public/waproamd/trunk@44 022f378f-78c4-0310-b860-d162c87e6274 --- bootstrap.sh | 19 ++++-- src/interface.c | 43 -------------- src/iwapi.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++--- src/iwapi.h | 22 +++++-- src/util.c | 28 ++++++++- src/util.h | 7 ++- src/waproamd.c | 180 ++++++++++++++++++++++++++++++++------------------------ 7 files changed, 314 insertions(+), 144 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 19c60c1..497134f 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -17,22 +17,29 @@ # along with waproamd; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +run_versioned() { + local P + type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1" + + shift 2 + "$P" "$@" +} + if [ "x$1" = "xam" ] ; then set -ex - automake -a -c + run_versioned automake 1.7 -a -c ./config.status else set -ex - - make maintainer-clean || true - rm -rf autom4te.cache rm -f config.cache - aclocal + run_versioned aclocal 1.7 autoheader - automake -a -c + run_versioned automake 1.7 -a -c autoconf -Wall ./configure --sysconfdir=/etc "$@" + + make clean fi diff --git a/src/interface.c b/src/interface.c index e835ab3..78602b0 100644 --- a/src/interface.c +++ b/src/interface.c @@ -65,46 +65,3 @@ void interface_close(struct interface *i) { 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) { - daemon_log(LOG_ERR, "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) { - daemon_log(LOG_ERR, "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/iwapi.c b/src/iwapi.c index ce320c1..2640b06 100644 --- a/src/iwapi.c +++ b/src/iwapi.c @@ -30,6 +30,7 @@ int iw_set_essid(struct interface *i, const char* essid) { struct iwreq req; char e[IW_ESSID_MAX_SIZE + 1]; + assert(i && essid); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); @@ -53,6 +54,7 @@ int iw_set_essid(struct interface *i, const char* essid) { int iw_set_mode(struct interface *i, int m) { struct iwreq req; + assert(i); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); @@ -66,8 +68,9 @@ int iw_set_mode(struct interface *i, int m) { return 0; } -int iw_set_freq(struct interface *i, struct iw_freq *f) { +int iw_set_freq(struct interface *i, const struct iw_freq *f) { struct iwreq req; + assert(i && f); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); @@ -81,8 +84,9 @@ int iw_set_freq(struct interface *i, struct iw_freq *f) { return 0; } -int iw_set_ap(struct interface *i, struct hw_addr *ap) { +int iw_set_ap(struct interface *i, const struct hw_addr *ap) { struct iwreq req; + assert(i && ap); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); @@ -100,6 +104,7 @@ int iw_set_ap(struct interface *i, struct hw_addr *ap) { int iw_scan(struct interface *i) { struct iwreq req; + assert(i); memset(&req, 0, sizeof(req)); strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); @@ -189,10 +194,14 @@ int iw_scan_result(struct interface *i, int (*callback)(struct ap_info* ap)) { return -1; } - if ((e->u.mode != IW_MODE_INFRA) && (e->u.mode != IW_MODE_MASTER)) - f = 0; // Ignore non-APs - else + if ((e->u.mode != IW_MODE_INFRA) && + (e->u.mode != IW_MODE_MASTER) && + (e->u.mode != IW_MODE_ADHOC)) + f = 0; // Ignore non-APs, non-adhocs + else { f |= 4; + ap.adhoc = e->u.mode == IW_MODE_ADHOC; + } break; @@ -226,10 +235,146 @@ int iw_tune(struct interface *i, struct ap_info *ap) { assert(i && ap); /* We are not interested in the return values of these functions due to driver issues */ - iw_set_mode(i, IW_MODE_INFRA); + iw_set_mode(i, ap->adhoc ? IW_MODE_ADHOC : IW_MODE_INFRA); iw_set_essid(i, ap->essid); iw_set_freq(i, &ap->freq); - iw_set_ap(i, &ap->ap); + if (!ap->adhoc) + iw_set_ap(i, &ap->ap); + + return 0; +} + +int iw_get_ap(struct interface *i, struct hw_addr *ap) { + struct iwreq req; + assert(i && ap); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); + + if (ioctl(i->fd, SIOCGIWAP, &req) < 0) { + daemon_log(LOG_ERR, "ioctl(SIOCGIWAP): %s\n", strerror(errno)); + return -1; + } + + memcpy(ap->addr, &(req.u.ap_addr.sa_data), ETH_ALEN); + return 0; +} + +int iw_get_essid(struct interface *i, char *essid) { + struct iwreq req; + assert(i && essid); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); + + memset(essid, 0, IW_ESSID_MAX_SIZE); + + req.u.essid.pointer = essid; + req.u.essid.length = IW_ESSID_MAX_SIZE+1; + req.u.essid.flags = 1; + + if (ioctl(i->fd, SIOCGIWESSID, &req) < 0) { + daemon_log(LOG_ERR, "ioctl(SIOCGSIWESSID): %s\n", strerror(errno)); + return -1; + } + + essid[IW_ESSID_MAX_SIZE] = 0; + return 0; +} + +int iw_get_mode(struct interface *i, int *m) { + struct iwreq req; + assert(i && m); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); + req.u.mode = 0; + + if (ioctl(i->fd, SIOCGIWMODE, &req) < 0) { + daemon_log(LOG_ERR, "ioctl(SIOCGIWMODE): %s\n", strerror(errno)); + return -1; + } + + *m = req.u.mode; + return 0; +} + +int iw_get_freq(struct interface *i, struct iw_freq *f) { + struct iwreq req; + assert(i && f); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_ifrn.ifrn_name, i->name, IFNAMSIZ); + + if (ioctl(i->fd, SIOCGIWFREQ, &req) < 0) { + daemon_log(LOG_ERR, "ioctl(SIOCGIWFREQ): %s\n", strerror(errno)); + return -1; + } + + *f = req.u.freq; return 0; } + +int iw_assoc(struct interface *i, struct ap_info *ap) { + struct hw_addr hw; + struct iwreq req; + struct iw_statistics q; + + assert(i); + + if (iw_get_ap(i, &hw) < 0) + return -1; + + if (!is_assoc_ap(&hw)) + 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) { + daemon_log(LOG_ERR, "Failed to get interface quality\n"); + return -1; + } + + if (q.qual.qual <= 0) + return 0; + + if (ap) { + int m; + + memset(ap, 0, sizeof(struct ap_info)); + memcpy(ap->ap.addr, hw.addr, ETH_ALEN); + + if (iw_get_essid(i, ap->essid) < 0) + return -1; + + if (iw_get_freq(i, &ap->freq) < 0) + return -1; + + if (iw_get_mode(i, &m) < 0) + return -1; + + if (m != IW_MODE_INFRA && m != IW_MODE_ADHOC) { + daemon_log(LOG_ERR, "Bad interface mode\n"); + return -1; + } + + ap->adhoc = m == IW_MODE_ADHOC; + } + + return 1; +} + +int iw_ap_info_equal(const struct ap_info *a, const struct ap_info *b) { + assert(a && b); + + return !strcmp(a->essid, b->essid) && + !!a->adhoc == !!b->adhoc && + !memcmp(&a->freq, &b->freq, sizeof(a->freq)) && + (a->adhoc || hw_addr_equal(&a->ap, &b->ap)); +} diff --git a/src/iwapi.h b/src/iwapi.h index 9a15387..06c46a1 100644 --- a/src/iwapi.h +++ b/src/iwapi.h @@ -26,6 +26,7 @@ #include "util.h" struct ap_info { + int adhoc; /* 0: master; 1: ad hoc */ struct hw_addr ap; struct iw_freq freq; char essid[IW_ESSID_MAX_SIZE + 1]; @@ -33,10 +34,23 @@ struct ap_info { 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_set_freq(struct interface *i, const struct iw_freq *f); +int iw_set_ap(struct interface *i, const struct hw_addr *ap); + +/* Size of string essid must be at least IW_ESSID_MAX_SIZE + 1 characters long */ +int iw_get_essid(struct interface *i, char *essid); +int iw_get_mode(struct interface *i, int *m); +int iw_get_freq(struct interface *i, struct iw_freq *f); +int iw_get_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*)); - + +int iw_tune(struct interface *i, struct ap_info *ap); + +/* Check whether the card is associated and fill in info record for associated AP */ +int iw_assoc(struct interface *i, struct ap_info *ap); + +int iw_ap_info_equal(const struct ap_info *a, const struct ap_info *b); + #endif diff --git a/src/util.c b/src/util.c index 9b6061f..b6d4e14 100644 --- a/src/util.c +++ b/src/util.c @@ -28,6 +28,7 @@ #include #include "util.h" +#include "iwapi.h" struct hw_addr null_ap = { { 0, 0, 0, 0, 0, 0 } }; @@ -38,11 +39,11 @@ void print_hex(FILE *f, uint8_t *w, int l) { } } -int hw_addr_equal(struct hw_addr *a, struct hw_addr *b) { +int hw_addr_equal(const struct hw_addr *a, const struct hw_addr *b) { return memcmp(a->addr, b->addr, ETH_ALEN) == 0; } -int is_assoc_ap(struct hw_addr *ap) { +int is_assoc_ap(const struct hw_addr *ap) { int b, j; b = 1; assert(ap); @@ -134,7 +135,7 @@ int get_ifname(int idx, char *p, int l) { return 0; } -int is_iface_available(char *p) { +int is_iface_available(const char *p) { struct ifreq req; int s, r; @@ -154,3 +155,24 @@ int is_iface_available(char *p) { close(s); return r >= 0 && req.ifr_ifindex >= 0; } + +const char* escape_essid(const char *s) { + static const char hextab[] = "0123456789ABCDEF"; + static char output[3*IW_ESSID_MAX_SIZE + 1]; + const char *i; + char *o; + + for (i = s, o = output; i-s < IW_ESSID_MAX_SIZE && *i; i++) { + + if (*i >= 32 && *i < 126 && *i != '/' && *i != '%') { + *(o++) = '%'; + *(o++) = hextab[*i >> 4]; + *(o++) = hextab[*i & 0xF]; + } else + *(o++) = *i; + } + + *(o++) = 0; + + return output; +} diff --git a/src/util.h b/src/util.h index fc7805b..13a13a0 100644 --- a/src/util.h +++ b/src/util.h @@ -42,9 +42,10 @@ 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 hw_addr_equal(const struct hw_addr *a, const struct hw_addr *b); +int is_assoc_ap(const struct hw_addr *ap); int get_ifname(int idx, char *p, int l); -int is_iface_available(char *p); +int is_iface_available(const char *p); +const char* escape_essid(const char *s); #endif diff --git a/src/waproamd.c b/src/waproamd.c index 0e2c719..23884bf 100644 --- a/src/waproamd.c +++ b/src/waproamd.c @@ -50,10 +50,13 @@ char *interface_name = NULL; int interface_index = -1; int disabled = 0, - associated = 0; + associated = 0, + do_status_check = 0; -struct hw_addr associated_ap; -struct hw_addr current_ap; +struct ap_info associated_ap; + +struct ap_info current_ap; +int current_ap_valid = 0; int use_assocwatch = 1, use_ifmonitor = 0, @@ -85,51 +88,70 @@ int issue_scan(struct interface *i) { return 0; } -void get_script_path(char *path, int l, struct hw_addr *ap, int uc) { +int get_script_path(char *path, int l, struct ap_info *ai) { assert(path && l); - if (!ap) { + if (!ai) { snprintf(path, l, "%s/default", SCRIPTDIR); - return; + return 0; } - snprintf(path, l, uc ? "%s/%02X:%02X:%02X:%02X:%02X:%02X": "%s/%02x:%02x:%02x:%02x:%02x:%02x", + snprintf(path, l, "%s/%02X:%02X:%02X:%02X:%02X:%02X", + SCRIPTDIR, + ai->ap.addr[0], ai->ap.addr[1], ai->ap.addr[2], + ai->ap.addr[3], ai->ap.addr[4], ai->ap.addr[5]); + + if (!access(path, X_OK)) + return 0; + + 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]); + ai->ap.addr[0], ai->ap.addr[1], ai->ap.addr[2], + ai->ap.addr[3], ai->ap.addr[4], ai->ap.addr[5]); + + if (!access(path, X_OK)) + return 0; + + if (ai->essid[0]) { + snprintf(path, l, "%s/essid:%s", + SCRIPTDIR, + escape_essid(ai->essid)); + + if (!access(path, X_OK)) + return 0; + } + + return -1; } -struct ap_info selected_ap; -int selected_ap_has_script; -int selected_ap_valid; +struct ap_info scan_ap; +int scan_ap_has_script; +int scan_ap_valid; -static int scan_result_cb(struct ap_info*ap) { - int b; +static int scan_result_cb(struct ap_info*ai) { + int f; char path[PATH_MAX]; - assert(ap); - get_script_path(path, sizeof(path), &ap->ap, 0); - if (!(b = !access(path, X_OK))) { - get_script_path(path, sizeof(path), &ap->ap, 1); - b = !access(path, X_OK); - } - - if (selected_ap_valid) { + assert(ai); - 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; + /* Found something suitable earlier */ + if (scan_ap_valid && scan_ap_has_script) + return 0; + + f = get_script_path(path, sizeof(path), ai); + if (scan_ap_valid && !f) + return 0; + + memcpy(&scan_ap, ai, sizeof(struct ap_info)); + scan_ap_valid = 1; + scan_ap_has_script = !f; return 0; } int read_scan(struct interface *i, struct ap_info **ap) { int r; - selected_ap_valid = 0; - selected_ap_has_script = 0; + scan_ap_valid = 0; + scan_ap_has_script = 0; if ((r = iw_scan_result(i, scan_result_cb)) < 0) return -1; @@ -137,48 +159,55 @@ int read_scan(struct interface *i, struct ap_info **ap) { if (r == 1) return 1; - *ap = selected_ap_valid ? &selected_ap : NULL; + *ap = scan_ap_valid ? &scan_ap : NULL; return 0; } -int run_script(struct hw_addr *ap, const char *arg) { +int run_script(struct ap_info *ai, const char *arg) { + char t[32]; char path[PATH_MAX]; - get_script_path(path, sizeof(path), ap, 0); - - if (access(path, X_OK) < 0) { - get_script_path(path, sizeof(path), ap, 1); - - if (access(path, X_OK) < 0) - get_script_path(path, sizeof(path), NULL, 0); - } + if (get_script_path(path, sizeof(path), ai) < 0) + get_script_path(path, sizeof(path), NULL); + setenv("IFACE", interface_name, 1); + if (!ai->adhoc) { + snprint_hw_addr(t, sizeof(t), &ai->ap); + setenv("AP", t, 1); + } else + setenv("AP", "n.a.", 1); + + setenv("ESSID", ai->essid, 1); + setenv("ESSID_ESCAPED", escape_essid(ai->essid), 1); + setenv("ADHOC", ai->adhoc ? "YES" : "NO", 1); + return log_exec(SCRIPTDIR, path, arg); }; -int set_current_ap(struct hw_addr *a) { - char t[32]; +int set_current_ap(struct ap_info *a) { - if (!a) - a = &null_ap; - - if (!hw_addr_equal(a, ¤t_ap)) { + if ((a && current_ap_valid && !iw_ap_info_equal(a, ¤t_ap)) || + (!!a != !!current_ap_valid)) { - if (!hw_addr_equal(¤t_ap, &null_ap)) { + if (current_ap_valid) { if (run_script(¤t_ap, "stop") < 0) return -1; } - - memcpy(¤t_ap, a, sizeof(struct hw_addr)); - snprint_hw_addr(t, sizeof(t), ¤t_ap); - setenv("AP", t, 1); - setenv("IFACE", interface_name, 1); + if ((current_ap_valid = !!a)) + memcpy(¤t_ap, a, sizeof(struct ap_info)); - if (!hw_addr_equal(¤t_ap, &null_ap)) { - daemon_log(LOG_INFO, "Selected new AP %s", t); + if (current_ap_valid) { + + if (current_ap.adhoc) + daemon_log(LOG_INFO, "Selected new ad hoc network with ESSID '%s'", escape_essid(a->essid)); + else { + char t[20]; + snprint_hw_addr(t, sizeof(t), ¤t_ap.ap); + daemon_log(LOG_INFO, "Selected new AP %s with ESSID", t, escape_essid(a->essid)); + } if (run_script(¤t_ap, "start") < 0) return -1; @@ -205,13 +234,11 @@ int assocwatch_cb(int index, struct hw_addr *a) { 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)); - } + if (strcmp(name, interface_name)) + return 0; + do_status_check = 1; + return 0; } @@ -221,7 +248,6 @@ int go(struct interface *i) { int send_retval = 1; int r = -1, sigfd; int paused = 0; - int force_assoc_check = 0; fd_set fds; daemon_log(LOG_INFO, "waproamd "VERSION" initializing%s%s.", use_ifmonitor ? ", using NETLINK device monitoring" : "", use_assocwatch ? ", using wireless event notifications" : ""); @@ -250,8 +276,9 @@ int go(struct interface *i) { disabled = 0; memset(¤t_ap, 0, sizeof(current_ap)); + current_ap_valid = 0; - if ((associated = interface_is_assoc(i, &associated_ap)) < 0) { + if ((associated = iw_assoc(i, &associated_ap)) < 0) { if (!use_ifmonitor) goto finish; @@ -270,7 +297,7 @@ int go(struct interface *i) { daemon_log(LOG_INFO, "Initialization complete."); - set_current_ap(&associated_ap); + set_current_ap(associated ? &associated_ap : NULL); next_scan = associated || disabled ? (time_t) -1 : 0; if (daemonize && wait_on_fork) { @@ -298,7 +325,7 @@ int go(struct interface *i) { next_scan = (time_t) -1; } - if (force_assoc_check) { + if (do_status_check) { tv.tv_sec = 0; tv.tv_usec = 0; ptv = &tv; @@ -342,19 +369,20 @@ int go(struct interface *i) { if (nlapi_work(0) < 0) goto finish; - if (force_assoc_check || (!disabled && !use_assocwatch && !paused)) { + if (do_status_check || (!use_assocwatch && !disabled && !paused)) { daemon_log(LOG_INFO, "Querying association status"); - if ((associated = interface_is_assoc(i, &associated_ap)) < 0) { + if ((associated = iw_assoc(i, &associated_ap)) < 0) { if (!use_ifmonitor) goto finish; associated = 0; } + } - force_assoc_check = 0; + do_status_check = 0; if (paused) { @@ -413,16 +441,11 @@ int go(struct interface *i) { if (!associated) { - if (ai) { - if (iw_tune(i, ai) < 0) - goto finish; + if (ai && iw_tune(i, ai) < 0) + goto finish; - if (set_current_ap(&ai->ap) < 0) - goto finish; - } else { - if (set_current_ap(NULL) < 0) - goto finish; - } + if (set_current_ap(ai) < 0) + goto finish; } } } @@ -465,7 +488,8 @@ int go(struct interface *i) { case SIGUSR2: if (paused > 0) { daemon_log(LOG_INFO, "SIGUSR2: Daemon resumed (#%i)", paused--); - force_assoc_check = !paused; + if (!paused) + do_status_check = 1; } break; -- cgit