summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/tools/net80211/wlanwds/wlanwds.c351
1 files changed, 249 insertions, 102 deletions
diff --git a/tools/tools/net80211/wlanwds/wlanwds.c b/tools/tools/net80211/wlanwds/wlanwds.c
index e443b32..c722693 100644
--- a/tools/tools/net80211/wlanwds/wlanwds.c
+++ b/tools/tools/net80211/wlanwds/wlanwds.c
@@ -36,12 +36,6 @@
* 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.
- *
- * Code liberaly swiped from wlanwatch; probably should nuke printfs.
*/
#include <sys/param.h>
#include <sys/file.h>
@@ -66,10 +60,12 @@
#include <err.h>
#include <errno.h>
#include <paths.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
+#include <syslog.h>
#include <unistd.h>
#include <ifaddrs.h>
@@ -83,11 +79,14 @@ struct wds {
};
static struct wds *wds;
-static const char *script = "/usr/local/bin/wdsup";
+static const char *script = NULL;
+static char **ifnets;
+static int nifnets = 0;
static int verbose = 0;
static int discover_on_join = 0;
-static void handle_rtmsg(struct rt_msghdr *rtm, int msglen);
+static void scanforvaps(int s);
+static void handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen);
static void wds_discovery(const char *ifname,
const uint8_t bssid[IEEE80211_ADDR_LEN]);
static void wds_destroy(const char *ifname);
@@ -95,42 +94,80 @@ 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);
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-fjtv] [-P pidfile] [-s <set_scriptname>] [ifnet0 ... | any]\n",
+ progname);
+ exit(-1);
+}
+
int
main(int argc, char *argv[])
{
- int n, s, c;
+ const char *progname = argv[0];
+ const char *pidfile = NULL;
+ int s, c, logmask, bg = 1;
char msg[2048];
- while ((c = getopt(argc, argv, "js:vn")) != -1)
+ logmask = LOG_UPTO(LOG_INFO);
+ while ((c = getopt(argc, argv, "fjP:s:tv")) != -1)
switch (c) {
+ case 'f':
+ bg = 0;
+ break;
case 'j':
discover_on_join = 1;
break;
+ case 'P':
+ pidfile = optarg;
+ break;
case 's':
script = optarg;
break;
+ case 't':
+ logmask = LOG_UPTO(LOG_ERR);
+ break;
case 'v':
- verbose = 1;
+ logmask = LOG_UPTO(LOG_DEBUG);
break;
case '?':
- errx(1, "usage: %s [-s <set_scriptname>]\n"
- " [-v (for verbose)]\n"
- " [-j (act on join/rejoin events)]\n", argv[0]);
+ usage(progname);
/*NOTREACHED*/
}
+ argc -= optind, argv += optind;
+ if (argc == 0) {
+ fprintf(stderr, "%s: no ifnet's specified to monitor\n",
+ progname);
+ usage(progname);
+ }
+ ifnets = argv;
+ nifnets = argc;
s = socket(PF_ROUTE, SOCK_RAW, 0);
if (s < 0)
err(EX_OSERR, "socket");
- for(;;) {
- n = read(s, msg, 2048);
+ /*
+ * Scan for inherited state.
+ */
+ scanforvaps(s);
+
+ /* XXX what directory to work in? */
+ if (bg && daemon(0, 0) < 0)
+ err(EX_OSERR, "daemon");
+
+ openlog("wlanwds", LOG_PID | LOG_CONS, LOG_DAEMON);
+ setlogmask(logmask);
+
+ for (;;) {
+ ssize_t n = read(s, msg, sizeof(msg));
handle_rtmsg((struct rt_msghdr *)msg, n);
}
return 0;
}
static const char *
-ether_sprintf(const uint8_t mac[6])
+ether_sprintf(const uint8_t mac[IEEE80211_ADDR_LEN])
{
static char buf[32];
@@ -139,76 +176,182 @@ ether_sprintf(const uint8_t mac[6])
return buf;
}
+/*
+ * Fetch a vap's parent ifnet name.
+ */
+static int
+getparent(const char *ifname, char parent[IFNAMSIZ+1])
+{
+ char oid[256];
+ int parentlen;
+
+ /* fetch parent interface name */
+ snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4);
+ parentlen = IFNAMSIZ;
+ if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0)
+ return -1;
+ parent[parentlen] = '\0';
+ return 0;
+}
+
+/*
+ * Check if the specified ifnet is one we're supposed to monitor.
+ * The ifnet is assumed to be a vap; we find it's parent and check
+ * it against the set of ifnet's specified on the command line.
+ */
+static int
+checkifnet(const char *ifname, int complain)
+{
+ char parent[256];
+ int i;
+
+ if (getparent(ifname, parent) < 0) {
+ if (complain)
+ syslog(LOG_ERR,
+ "%s: no pointer to parent interface: %m", ifname);
+ return 0;
+ }
+
+ for (i = 0; i < nifnets; i++)
+ if (strcasecmp(ifnets[i], "any") == 0 ||
+ strcmp(ifnets[i], parent) == 0)
+ return 1;
+ syslog(LOG_DEBUG, "%s: parent %s not being monitored", ifname, parent);
+ return 0;
+}
+
+/*
+ * Return 1 if the specified ifnet is a WDS vap.
+ */
+static int
+iswdsvap(int s, const char *ifname)
+{
+ struct ifmediareq ifmr;
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+ err(-1, "%s: cannot get media", ifname);
+ return (ifmr.ifm_current & IFM_IEEE80211_WDS) != 0;
+}
+
+/*
+ * Fetch the bssid for an ifnet. The caller is assumed
+ * to have already verified this is possible.
+ */
+static void
+getbssid(int s, const char *ifname, char bssid[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, ifname, sizeof(ireq.i_name));
+ ireq.i_type = IEEE80211_IOC_BSSID;
+ ireq.i_data = bssid;
+ ireq.i_len = IEEE80211_ADDR_LEN;
+ if (ioctl(s, SIOCG80211, &ireq) < 0)
+ err(-1, "%s: cannot fetch bssid", ifname);
+}
+
+/*
+ * Scan the system for WDS vaps associated with the ifnet's we're
+ * supposed to monitor. Any vaps are added to our internal table
+ * so we can find them (and destroy them) on station leave.
+ */
+static void
+scanforvaps(int s)
+{
+ char ifname[IFNAMSIZ+1];
+ char bssid[IEEE80211_ADDR_LEN];
+ int i;
+
+ /* XXX brutal; should just walk sysctl tree */
+ for (i = 0; i < 128; i++) {
+ snprintf(ifname, sizeof(ifname), "wlan%d", i);
+ if (checkifnet(ifname, 0) && iswdsvap(s, ifname)) {
+ struct wds *p = malloc(sizeof(struct wds));
+ if (p == NULL)
+ err(-1, "%s: malloc failed", __func__);
+ strlcpy(p->ifname, ifname, IFNAMSIZ);
+ getbssid(s, ifname, p->bssid);
+ p->next = wds;
+ wds = p;
+
+ syslog(LOG_INFO, "[%s] discover wds vap %s",
+ ether_sprintf(bssid), ifname);
+ }
+ }
+}
+
+/*
+ * Process a routing socket message. We handle messages related
+ * to dynamic WDS:
+ * o on WDS discovery (rx of a 4-address frame with DWDS enabled)
+ * we create a WDS vap for the specified mac address
+ * o on station leave we destroy any associated WDS vap
+ * o on ifnet destroy we update state if this is manual destroy of
+ * a WDS vap in our table
+ * o if the -j option is supplied on the command line we create
+ * WDS vaps on station join/rejoin, this is useful for some setups
+ * where a WDS vap is required for 4-address traffic to flow
+ */
static void
-handle_rtmsg(struct rt_msghdr *rtm, int msglen)
+handle_rtmsg(struct rt_msghdr *rtm, ssize_t 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",
+ syslog(LOG_ERR, "routing message version %d not understood",
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");
+ syslog(LOG_DEBUG,
+ "RTM_IFANNOUNCE: if# %d, what: arrival",
+ ifan->ifan_index);
break;
case IFAN_DEPARTURE:
- printf("departure");
+ syslog(LOG_DEBUG,
+ "RTM_IFANNOUNCE: if# %d, what: departure",
+ ifan->ifan_index);
+ /* NB: ok to call w/ unmonitored ifnets */
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_DISASSOC:
+ if (!discover_on_join)
+ break;
+ /* fall thru... */
case RTM_IEEE80211_LEAVE:
- if (verbose)
- printf("%.19s %s station leave", cnow,
- ether_sprintf(V(ieee80211_leave_event)->iev_addr));
+ if (!checkifnet(ifan->ifan_name, 1))
+ break;
+ syslog(LOG_INFO, "[%s] station leave",
+ ether_sprintf(V(ieee80211_leave_event)->iev_addr));
wds_leave(V(ieee80211_leave_event)->iev_addr);
- if (verbose)
- printf("\n");
break;
case RTM_IEEE80211_JOIN:
case RTM_IEEE80211_REJOIN:
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
if (!discover_on_join)
break;
/* fall thru... */
case RTM_IEEE80211_WDS:
- if (verbose)
- printf("%.19s %s wds discovery", cnow,
- ether_sprintf(V(ieee80211_wds_event)->iev_addr));
+ syslog(LOG_INFO, "[%s] wds discovery",
+ ether_sprintf(V(ieee80211_wds_event)->iev_addr));
+ if (!checkifnet(ifan->ifan_name, 1))
+ break;
wds_discovery(ifan->ifan_name,
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_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;
@@ -216,58 +359,61 @@ handle_rtmsg(struct rt_msghdr *rtm, int msglen)
}
}
+/*
+ * Handle WDS discovery; create a WDS vap for the specified bssid.
+ * If a vap already exists then do nothing (can happen when a flood
+ * of 4-address frames causes multiple events to be queued before
+ * we create a vap).
+ */
static void
wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN])
{
struct wds *p;
- char oid[256], parent[256];
- int parentlen;
+ char parent[256];
+ char cmd[1024];
+ int status;
for (p = wds; p != NULL; p = p->next)
if (IEEE80211_ADDR_EQ(p->bssid, bssid)) {
- if (verbose)
- printf(" (already created)");
+ syslog(LOG_INFO, "[%s] wds vap already created (%s)",
+ ether_sprintf(bssid), ifname);
return;
}
-
- /* fetch parent interface name */
- snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4);
- parentlen = sizeof(parent);
- if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0) {
- warn("%s: no pointer to parent interface", __func__);
+ if (getparent(ifname, parent) < 0) {
+ syslog(LOG_ERR, "%s: no pointer to parent interface: %m",
+ ifname);
return;
}
- parent[parentlen] = '\0';
p = malloc(sizeof(struct wds));
if (p == NULL) {
- warn("%s: malloc", __func__);
+ syslog(LOG_ERR, "%s: malloc failed: %m", __func__);
return;
}
IEEE80211_ADDR_COPY(p->bssid, bssid);
- 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.
- */
+ if (wds_vap_create(parent, p) < 0) {
+ free(p);
+ return;
+ }
+ /*
+ * Add to table and launch setup script.
+ */
+ p->next = wds;
+ wds = p;
+ syslog(LOG_INFO, "[%s] create wds vap %s", ether_sprintf(bssid),
+ p->ifname);
+ if (script != NULL) {
snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname);
status = system(cmd);
if (status)
- warnx("vap setup script %s exited with status %d\n",
- script, status);
- } else
- free(p);
+ syslog(LOG_ERR, "vap setup script %s exited with "
+ "status %d", script, status);
+ }
}
+/*
+ * Destroy a WDS vap (if known).
+ */
static void
wds_destroy(const char *ifname)
{
@@ -276,16 +422,17 @@ wds_destroy(const char *ifname)
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 */
+ if (p != NULL) {
+ *pp = p->next;
+ /* NB: vap already destroyed */
+ free(p);
return;
- *pp = p->next;
- if (wds_vap_destroy(p->ifname) >= 0)
- if (verbose)
- printf(" (wds vap destroyed)");
- free(p);
+ }
}
+/*
+ * Handle a station leave event; destroy any associated WDS vap.
+ */
static void
wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
{
@@ -294,13 +441,13 @@ wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
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);
+ if (p != NULL) {
+ *pp = p->next;
+ if (wds_vap_destroy(p->ifname) >= 0)
+ syslog(LOG_INFO, "[%s] wds vap %s destroyed",
+ ether_sprintf(bssid), p->ifname);
+ free(p);
+ }
}
static int
@@ -326,14 +473,14 @@ wds_vap_create(const char *parent, struct wds *p)
strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ);
status = 0;
} else {
- warn("SIOCIFCREATE2("
- "mode %u flags 0x%x parent %s bssid %s)",
+ syslog(LOG_ERR, "SIOCIFCREATE2("
+ "mode %u flags 0x%x parent %s bssid %s): %m",
cp.icp_opmode, cp.icp_flags, parent,
ether_sprintf(cp.icp_bssid));
}
close(s);
} else
- warn("socket(SOCK_DRAGM)");
+ syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
return status;
}
@@ -345,13 +492,13 @@ wds_vap_destroy(const char *ifname)
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
- warn("socket(SOCK_DRAGM)");
+ syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.i_name, ifname, IFNAMSIZ);
if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
- warn("ioctl(SIOCIFDESTROY)");
+ syslog(LOG_ERR, "ioctl(SIOCIFDESTROY): %m");
status = -1;
} else
status = 0;
OpenPOWER on IntegriCloud