svn commit: r191247 - head/tools/tools/net80211/wlanwds

Sam Leffler sam at FreeBSD.org
Sat Apr 18 16:14:06 UTC 2009


Author: sam
Date: Sat Apr 18 16:14:03 2009
New Revision: 191247
URL: http://svn.freebsd.org/changeset/base/191247

Log:
  Cleanups to prepare this code for wider use (likely merged into hostapd):
  o add (required) cmd line args to specify the set of ifnet's to monitor
    for WDS discovery msgs; "any" is a wildcard
  o change the default script run on wds vap create to the "null script"
  o auto-daemonize; add -f option to force foreground operation
  o add -P option for integration with rc.d (implementation missing, tba)
  o use syslog; default to log up to LOG_INFO, -t (terse) gives you up to
    LOG_ERR, and -v (verbose) gives you up to LOG_DEBUG
  o scan for existing vaps on startup to recover existing state
  o correct some types

Modified:
  head/tools/tools/net80211/wlanwds/wlanwds.c

Modified: head/tools/tools/net80211/wlanwds/wlanwds.c
==============================================================================
--- head/tools/tools/net80211/wlanwds/wlanwds.c	Sat Apr 18 15:59:09 2009	(r191246)
+++ head/tools/tools/net80211/wlanwds/wlanwds.c	Sat Apr 18 16:14:03 2009	(r191247)
@@ -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 bssi
 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
-handle_rtmsg(struct rt_msghdr *rtm, int msglen)
+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, 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 
 	}
 }
 
+/*
+ * 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_
 	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, struc
 			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;


More information about the svn-src-head mailing list