diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:43:13 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:43:13 +0000 |
commit | fb524bd0267c7aef55be1ee0b6a07a425d7ad454 (patch) | |
tree | 0eb6c3954940a31b1cd862a1a468b6bcd05323da | |
parent | a56ef47966af832916ae8cf683710c11bc9532f3 (diff) | |
download | FreeBSD-src-fb524bd0267c7aef55be1ee0b6a07a425d7ad454.zip FreeBSD-src-fb524bd0267c7aef55be1ee0b6a07a425d7ad454.tar.gz |
add dwds test app
Supported by: Hobnob
Obtained from: Atheros (earlier version)
-rw-r--r-- | tools/tools/net80211/Makefile | 2 | ||||
-rw-r--r-- | tools/tools/net80211/wlanwds/Makefile | 7 | ||||
-rw-r--r-- | tools/tools/net80211/wlanwds/wlanwds.c | 351 |
3 files changed, 359 insertions, 1 deletions
diff --git a/tools/tools/net80211/Makefile b/tools/tools/net80211/Makefile index 576dbd0..02edecd 100644 --- a/tools/tools/net80211/Makefile +++ b/tools/tools/net80211/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR= stumbler w00t wesside wlaninject wlanstats wlanwatch +SUBDIR= stumbler w00t wesside wlaninject wlanstats wlanwatch wlanwds .include <bsd.subdir.mk> diff --git a/tools/tools/net80211/wlanwds/Makefile b/tools/tools/net80211/wlanwds/Makefile new file mode 100644 index 0000000..6813f3a --- /dev/null +++ b/tools/tools/net80211/wlanwds/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= wlanwds +BINDIR= /usr/local/bin +NO_MAN= + +.include <bsd.prog.mk> diff --git a/tools/tools/net80211/wlanwds/wlanwds.c b/tools/tools/net80211/wlanwds/wlanwds.c new file mode 100644 index 0000000..c27bf30 --- /dev/null +++ b/tools/tools/net80211/wlanwds/wlanwds.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2006-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +/* + * Test app to demonstrate how to handle dynamic WDS links: + * o monitor 802.11 events for wds discovery events + * o create wds vap's in response to wds discovery events + * and launch a script to handle adding the vap to the + * bridge, etc. + * o destroy wds vap's when station leaves + * + * Note we query only internal state which means if we don't see + * a vap created we won't handle leave/delete properly. Also there + * are several fixed pathnames/strings. Some require fixing + * kernel support (e.g. sysctl to find parent device of a vap). + * + * Code liberaly swiped from wlanwatch; probably should nuke printfs. + */ +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <net/if.h> +#include "net/if_media.h" +#include <net/route.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netatalk/at.h> +#include "net80211/ieee80211_ioctl.h" +#include "net80211/ieee80211_freebsd.h" +#include <arpa/inet.h> +#include <netdb.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <ifaddrs.h> + +#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) +#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) + +struct wds { + struct wds *next; + uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ + char ifname[IFNAMSIZ]; /* vap interface name */ +}; +static struct wds *wds; + +static const char *bridge = "bridge0"; +static const char *parent = "mv0"; /* XXX no sysctl to find this */ +static const char *script = "/usr/local/bin/wdsup"; +static int verbose = 0; + +static void handle_rtmsg(struct rt_msghdr *rtm, int msglen); +static void wds_discovery(const char *ifname, + const uint8_t bssid[IEEE80211_ADDR_LEN]); +static void wds_destroy(const char *ifname); +static void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); +static int wds_vap_create(const char *ifname, struct wds *); +static int wds_vap_destroy(const char *ifname); + +int +main(int argc, char *argv[]) +{ + int n, s, c; + char msg[2048]; + + while ((c = getopt(argc, argv, "b:p:s:vn")) != -1) + switch (c) { + case 'b': + bridge = optarg; + break; + case 'p': + parent = optarg; + break; + case 's': + script = optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + errx(1, "usage: %s [-b <bridgename>] [-p <parentname>] [-s <set_scriptname>]\n" + " [-v (for verbose)]\n", argv[0]); + /*NOTREACHED*/ + } + + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(EX_OSERR, "socket"); + for(;;) { + n = read(s, msg, 2048); + handle_rtmsg((struct rt_msghdr *)msg, n); + } + return 0; +} + +static const char * +ether_sprintf(const uint8_t mac[6]) +{ + static char buf[32]; + + snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return buf; +} + +static void +handle_rtmsg(struct rt_msghdr *rtm, int msglen) +{ + struct if_announcemsghdr *ifan; + time_t now = time(NULL); + char *cnow = ctime(&now); + + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)rtm; + if (!verbose) + break; + printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", + cnow, ifan->ifan_index); + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + printf("arrival"); + break; + case IFAN_DEPARTURE: + printf("departure"); + wds_destroy(ifan->ifan_name); + break; + default: + printf("#%d", ifan->ifan_what); + break; + } + printf("\n"); + break; + case RTM_IEEE80211: +#define V(type) ((struct type *)(&ifan[1])) + ifan = (struct if_announcemsghdr *)rtm; + switch (ifan->ifan_what) { + case RTM_IEEE80211_LEAVE: + if (verbose) + printf("%.19s %s station leave", cnow, + ether_sprintf(V(ieee80211_leave_event)->iev_addr)); + wds_leave(V(ieee80211_leave_event)->iev_addr); + if (verbose) + printf("\n"); + break; + case RTM_IEEE80211_WDS: + if (verbose) + printf("%.19s %s wds discovery", cnow, + ether_sprintf(V(ieee80211_wds_event)->iev_addr)); + /* XXX wlan0 */ + wds_discovery("wlan0", V(ieee80211_wds_event)->iev_addr); + if (verbose) + printf("\n"); + break; + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + case RTM_IEEE80211_DISASSOC: + case RTM_IEEE80211_JOIN: + case RTM_IEEE80211_REJOIN: + case RTM_IEEE80211_SCAN: + case RTM_IEEE80211_REPLAY: + case RTM_IEEE80211_MICHAEL: + break; + default: + if (verbose) + printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow, + ifan->ifan_index, ifan->ifan_what); + break; + } + break; +#undef V + } +} + +static void +wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN]) +{ + struct wds *p; + + for (p = wds; p != NULL; p = p->next) + if (IEEE80211_ADDR_EQ(p->bssid, bssid)) { + if (verbose) + printf(" (already created)"); + return; + } + p = malloc(sizeof(struct wds)); + if (p == NULL) { + warn("%s: malloc", __func__); + return; + } + IEEE80211_ADDR_COPY(p->bssid, bssid); + /* XXX mv0: no sysctl to find parent device */ + if (wds_vap_create(parent, p) >= 0) { + char cmd[1024]; + int status; + + /* + * Add to table. + */ + p->next = wds; + wds = p; + if (verbose) + printf(" (create %s)", p->ifname); + /* + * XXX launch script to setup bridge, etc. + */ + snprintf(cmd, sizeof(cmd), "%s %s %s", + script, p->ifname, bridge); + status = system(cmd); + if (status) + warnx("vap setup script %s exited with status %d\n", + script, status); + } else + free(p); +} + +static void +wds_destroy(const char *ifname) +{ + struct wds *p, **pp; + + for (pp = &wds; (p = *pp) != NULL; pp = &p->next) + if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) + break; + /* XXX check for device directly */ + if (p == NULL) /* not ours/known */ + return; + *pp = p->next; + if (wds_vap_destroy(p->ifname) >= 0) + if (verbose) + printf(" (wds vap destroyed)"); + free(p); +} + +static void +wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]) +{ + struct wds *p, **pp; + + for (pp = &wds; (p = *pp) != NULL; pp = &p->next) + if (IEEE80211_ADDR_EQ(p->bssid, bssid)) + break; + /* XXX fall back to check device */ + if (p == NULL) /* not ours/known */ + return; + *pp = p->next; + if (wds_vap_destroy(p->ifname) >= 0) + printf(" (wds vap destroyed)"); + free(p); +} + +static int +wds_vap_create(const char *parent, struct wds *p) +{ + struct ieee80211_clone_params cp; + struct ifreq ifr; + int s, status; + + memset(&cp, 0, sizeof(cp)); + strncpy(cp.icp_parent, parent, IFNAMSIZ); + cp.icp_opmode = IEEE80211_M_WDS; + IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, "wlan", IFNAMSIZ); + ifr.ifr_data = (void *) &cp; + + status = -1; + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s >= 0) { + if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) { + strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ); + status = 0; + } else { + warn("SIOCIFCREATE2(" + "mode %u flags 0x%x parent %s bssid %s)", + cp.icp_opmode, cp.icp_flags, parent, + ether_sprintf(cp.icp_bssid)); + } + close(s); + } else + warn("socket(SOCK_DRAGM)"); + return status; +} + +static int +wds_vap_destroy(const char *ifname) +{ + struct ieee80211req ifr; + int s, status; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + warn("socket(SOCK_DRAGM)"); + return -1; + } + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.i_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { + warn("ioctl(SIOCIFDESTROY)"); + status = -1; + } else + status = 0; + close(s); + return status; +} |