xref: /freebsd/tools/tools/net80211/wlanwds/wlanwds.c (revision db80116da404c90e6f0d69350554291f866e8ebb)
16c2ea87dSSam Leffler /*-
23bf80639SSam Leffler  * Copyright (c) 2006-2009 Sam Leffler, Errno Consulting
36c2ea87dSSam Leffler  * All rights reserved.
46c2ea87dSSam Leffler  *
56c2ea87dSSam Leffler  * Redistribution and use in source and binary forms, with or without
66c2ea87dSSam Leffler  * modification, are permitted provided that the following conditions
76c2ea87dSSam Leffler  * are met:
86c2ea87dSSam Leffler  * 1. Redistributions of source code must retain the above copyright
96c2ea87dSSam Leffler  *    notice, this list of conditions and the following disclaimer,
106c2ea87dSSam Leffler  *    without modification.
116c2ea87dSSam Leffler  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
126c2ea87dSSam Leffler  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
136c2ea87dSSam Leffler  *    redistribution must be conditioned upon including a substantially
146c2ea87dSSam Leffler  *    similar Disclaimer requirement for further binary redistribution.
156c2ea87dSSam Leffler  *
166c2ea87dSSam Leffler  * NO WARRANTY
176c2ea87dSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
186c2ea87dSSam Leffler  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
196c2ea87dSSam Leffler  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
206c2ea87dSSam Leffler  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
216c2ea87dSSam Leffler  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
226c2ea87dSSam Leffler  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
236c2ea87dSSam Leffler  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
246c2ea87dSSam Leffler  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
256c2ea87dSSam Leffler  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
266c2ea87dSSam Leffler  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
276c2ea87dSSam Leffler  * THE POSSIBILITY OF SUCH DAMAGES.
286c2ea87dSSam Leffler  */
296c2ea87dSSam Leffler 
306c2ea87dSSam Leffler /*
316c2ea87dSSam Leffler  * Test app to demonstrate how to handle dynamic WDS links:
326c2ea87dSSam Leffler  * o monitor 802.11 events for wds discovery events
336c2ea87dSSam Leffler  * o create wds vap's in response to wds discovery events
346c2ea87dSSam Leffler  *   and launch a script to handle adding the vap to the
356c2ea87dSSam Leffler  *   bridge, etc.
366c2ea87dSSam Leffler  * o destroy wds vap's when station leaves
376c2ea87dSSam Leffler  */
386c2ea87dSSam Leffler #include <sys/param.h>
396c2ea87dSSam Leffler #include <sys/file.h>
406c2ea87dSSam Leffler #include <sys/socket.h>
416c2ea87dSSam Leffler #include <sys/ioctl.h>
426c2ea87dSSam Leffler #include <sys/sysctl.h>
436c2ea87dSSam Leffler #include <sys/types.h>
446c2ea87dSSam Leffler 
456c2ea87dSSam Leffler #include <net/if.h>
466c2ea87dSSam Leffler #include "net/if_media.h"
476c2ea87dSSam Leffler #include <net/route.h>
486c2ea87dSSam Leffler #include <net/if_dl.h>
496c2ea87dSSam Leffler #include <netinet/in.h>
506c2ea87dSSam Leffler #include <netinet/if_ether.h>
516c2ea87dSSam Leffler #include "net80211/ieee80211_ioctl.h"
526c2ea87dSSam Leffler #include "net80211/ieee80211_freebsd.h"
536c2ea87dSSam Leffler #include <arpa/inet.h>
546c2ea87dSSam Leffler #include <netdb.h>
556c2ea87dSSam Leffler 
56495842b1SAdrian Chadd #include <net/if.h>
57495842b1SAdrian Chadd #include <net/if_types.h>
58495842b1SAdrian Chadd 
596c2ea87dSSam Leffler #include <ctype.h>
606c2ea87dSSam Leffler #include <err.h>
616c2ea87dSSam Leffler #include <errno.h>
626c2ea87dSSam Leffler #include <paths.h>
636f20ea67SSam Leffler #include <stdarg.h>
646c2ea87dSSam Leffler #include <stdio.h>
656c2ea87dSSam Leffler #include <stdlib.h>
666c2ea87dSSam Leffler #include <string.h>
676c2ea87dSSam Leffler #include <sysexits.h>
686f20ea67SSam Leffler #include <syslog.h>
696c2ea87dSSam Leffler #include <unistd.h>
706c2ea87dSSam Leffler #include <ifaddrs.h>
71*db80116dSAdrian Chadd #include <libutil.h>
726c2ea87dSSam Leffler 
736c2ea87dSSam Leffler #define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
746c2ea87dSSam Leffler #define	IEEE80211_ADDR_COPY(dst,src)	memcpy(dst,src,IEEE80211_ADDR_LEN)
756c2ea87dSSam Leffler 
766c2ea87dSSam Leffler struct wds {
776c2ea87dSSam Leffler 	struct wds *next;
786c2ea87dSSam Leffler 	uint8_t	bssid[IEEE80211_ADDR_LEN];	/* bssid of associated sta */
796c2ea87dSSam Leffler 	char	ifname[IFNAMSIZ];		/* vap interface name */
806c2ea87dSSam Leffler };
816c2ea87dSSam Leffler static struct wds *wds;
826c2ea87dSSam Leffler 
836f20ea67SSam Leffler static	const char *script = NULL;
846f20ea67SSam Leffler static	char **ifnets;
856f20ea67SSam Leffler static	int nifnets = 0;
863bf80639SSam Leffler static	int discover_on_join = 0;
876c2ea87dSSam Leffler 
886f20ea67SSam Leffler static	void scanforvaps(int s);
896f20ea67SSam Leffler static	void handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen);
906c2ea87dSSam Leffler static	void wds_discovery(const char *ifname,
916c2ea87dSSam Leffler 		const uint8_t bssid[IEEE80211_ADDR_LEN]);
926c2ea87dSSam Leffler static	void wds_destroy(const char *ifname);
936c2ea87dSSam Leffler static	void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]);
94495842b1SAdrian Chadd static	int wds_vap_create(const char *ifname, uint8_t macaddr[ETHER_ADDR_LEN],
95495842b1SAdrian Chadd 	    struct wds *);
966c2ea87dSSam Leffler static	int wds_vap_destroy(const char *ifname);
976c2ea87dSSam Leffler 
986f20ea67SSam Leffler static void
usage(const char * progname)996f20ea67SSam Leffler usage(const char *progname)
1006f20ea67SSam Leffler {
1019673a6bbSAdrian Chadd 	fprintf(stderr, "usage: %s [-efjtv] [-P pidfile] [-s <set_scriptname>] [ifnet0 ... | any]\n",
1026f20ea67SSam Leffler 		progname);
1036f20ea67SSam Leffler 	exit(-1);
1046f20ea67SSam Leffler }
1056f20ea67SSam Leffler 
1066c2ea87dSSam Leffler int
main(int argc,char * argv[])1076c2ea87dSSam Leffler main(int argc, char *argv[])
1086c2ea87dSSam Leffler {
1096f20ea67SSam Leffler 	const char *progname = argv[0];
1106f20ea67SSam Leffler 	const char *pidfile = NULL;
111*db80116dSAdrian Chadd 	struct pidfh *pfh = NULL;
1126f20ea67SSam Leffler 	int s, c, logmask, bg = 1;
1136c2ea87dSSam Leffler 	char msg[2048];
1149673a6bbSAdrian Chadd 	int log_stderr = 0;
115*db80116dSAdrian Chadd 	pid_t otherpid;
1166c2ea87dSSam Leffler 
1176f20ea67SSam Leffler 	logmask = LOG_UPTO(LOG_INFO);
1189673a6bbSAdrian Chadd 	while ((c = getopt(argc, argv, "efjP:s:tv")) != -1)
1196c2ea87dSSam Leffler 		switch (c) {
1209673a6bbSAdrian Chadd 		case 'e':
1219673a6bbSAdrian Chadd 			log_stderr = LOG_PERROR;
1229673a6bbSAdrian Chadd 			break;
1236f20ea67SSam Leffler 		case 'f':
1246f20ea67SSam Leffler 			bg = 0;
1256f20ea67SSam Leffler 			break;
1263bf80639SSam Leffler 		case 'j':
1273bf80639SSam Leffler 			discover_on_join = 1;
1286c2ea87dSSam Leffler 			break;
1296f20ea67SSam Leffler 		case 'P':
1306f20ea67SSam Leffler 			pidfile = optarg;
1316f20ea67SSam Leffler 			break;
1326c2ea87dSSam Leffler 		case 's':
1336c2ea87dSSam Leffler 			script = optarg;
1346c2ea87dSSam Leffler 			break;
1356f20ea67SSam Leffler 		case 't':
1366f20ea67SSam Leffler 			logmask = LOG_UPTO(LOG_ERR);
1376f20ea67SSam Leffler 			break;
1386c2ea87dSSam Leffler 		case 'v':
1396f20ea67SSam Leffler 			logmask = LOG_UPTO(LOG_DEBUG);
1406c2ea87dSSam Leffler 			break;
1416c2ea87dSSam Leffler 		case '?':
1426f20ea67SSam Leffler 			usage(progname);
1436c2ea87dSSam Leffler 			/*NOTREACHED*/
1446c2ea87dSSam Leffler 		}
1456f20ea67SSam Leffler 	argc -= optind, argv += optind;
1466f20ea67SSam Leffler 	if (argc == 0) {
1476f20ea67SSam Leffler 		fprintf(stderr, "%s: no ifnet's specified to monitor\n",
1486f20ea67SSam Leffler 		    progname);
1496f20ea67SSam Leffler 		usage(progname);
1506f20ea67SSam Leffler 	}
1516f20ea67SSam Leffler 	ifnets = argv;
1526f20ea67SSam Leffler 	nifnets = argc;
1536c2ea87dSSam Leffler 
154*db80116dSAdrian Chadd 	if (pidfile != NULL) {
155*db80116dSAdrian Chadd 		pfh = pidfile_open(pidfile, 0600, &otherpid);
156*db80116dSAdrian Chadd 		if (pfh == NULL) {
157*db80116dSAdrian Chadd 			if (errno == EEXIST)
158*db80116dSAdrian Chadd 				errx(EXIT_FAILURE, "Daemon already running; pid: %jd.",
159*db80116dSAdrian Chadd 				    (intmax_t)otherpid);
160*db80116dSAdrian Chadd 
161*db80116dSAdrian Chadd 			warn("Cannot open or create pidfile");
162*db80116dSAdrian Chadd 		}
163*db80116dSAdrian Chadd 	}
164*db80116dSAdrian Chadd 
1656c2ea87dSSam Leffler 	s = socket(PF_ROUTE, SOCK_RAW, 0);
1666c2ea87dSSam Leffler 	if (s < 0)
1676c2ea87dSSam Leffler 		err(EX_OSERR, "socket");
1686f20ea67SSam Leffler 	/*
1696f20ea67SSam Leffler 	 * Scan for inherited state.
1706f20ea67SSam Leffler 	 */
1716f20ea67SSam Leffler 	scanforvaps(s);
1726f20ea67SSam Leffler 
1736f20ea67SSam Leffler 	/* XXX what directory to work in? */
174*db80116dSAdrian Chadd 	if (bg && daemon(0, 0) < 0) {
175*db80116dSAdrian Chadd 		pidfile_remove(pfh);
1766f20ea67SSam Leffler 		err(EX_OSERR, "daemon");
177*db80116dSAdrian Chadd 	}
178*db80116dSAdrian Chadd 
179*db80116dSAdrian Chadd 	pidfile_write(pfh);
1806f20ea67SSam Leffler 
1819673a6bbSAdrian Chadd 	openlog("wlanwds", log_stderr | LOG_PID | LOG_CONS, LOG_DAEMON);
1826f20ea67SSam Leffler 	setlogmask(logmask);
1836f20ea67SSam Leffler 
1846c2ea87dSSam Leffler 	for (;;) {
1856f20ea67SSam Leffler 		ssize_t n = read(s, msg, sizeof(msg));
1866c2ea87dSSam Leffler 		handle_rtmsg((struct rt_msghdr *)msg, n);
1876c2ea87dSSam Leffler 	}
188*db80116dSAdrian Chadd 
189*db80116dSAdrian Chadd 	pidfile_remove(pfh);
1906c2ea87dSSam Leffler 	return 0;
1916c2ea87dSSam Leffler }
1926c2ea87dSSam Leffler 
1936c2ea87dSSam Leffler static const char *
ether_sprintf(const uint8_t mac[IEEE80211_ADDR_LEN])1946f20ea67SSam Leffler ether_sprintf(const uint8_t mac[IEEE80211_ADDR_LEN])
1956c2ea87dSSam Leffler {
1966c2ea87dSSam Leffler 	static char buf[32];
1976c2ea87dSSam Leffler 
1986c2ea87dSSam Leffler 	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
1996c2ea87dSSam Leffler 		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
2006c2ea87dSSam Leffler 	return buf;
2016c2ea87dSSam Leffler }
2026c2ea87dSSam Leffler 
2036f20ea67SSam Leffler /*
2046f20ea67SSam Leffler  * Fetch a vap's parent ifnet name.
2056f20ea67SSam Leffler  */
2066f20ea67SSam Leffler static int
getparent(const char * ifname,char parent[IFNAMSIZ+1])2076f20ea67SSam Leffler getparent(const char *ifname, char parent[IFNAMSIZ+1])
2086f20ea67SSam Leffler {
2096f20ea67SSam Leffler 	char oid[256];
2106da4c4c5SAdrian Chadd 	size_t parentlen;
2116f20ea67SSam Leffler 
2126f20ea67SSam Leffler 	/* fetch parent interface name */
2136f20ea67SSam Leffler 	snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4);
2146f20ea67SSam Leffler 	parentlen = IFNAMSIZ;
2156f20ea67SSam Leffler 	if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0)
2166f20ea67SSam Leffler 		return -1;
2176f20ea67SSam Leffler 	parent[parentlen] = '\0';
2186f20ea67SSam Leffler 	return 0;
2196f20ea67SSam Leffler }
2206f20ea67SSam Leffler 
2216f20ea67SSam Leffler /*
2226f20ea67SSam Leffler  * Check if the specified ifnet is one we're supposed to monitor.
2236f20ea67SSam Leffler  * The ifnet is assumed to be a vap; we find it's parent and check
2246f20ea67SSam Leffler  * it against the set of ifnet's specified on the command line.
225495842b1SAdrian Chadd  *
226495842b1SAdrian Chadd  * TODO: extend this to also optionally allow the specific DWDS
227495842b1SAdrian Chadd  * VAP to be monitored, instead of assuming all VAPs on a parent
228495842b1SAdrian Chadd  * physical interface are being monitored by this instance of
229495842b1SAdrian Chadd  * wlanwds.
2306f20ea67SSam Leffler  */
2316f20ea67SSam Leffler static int
checkifnet(const char * ifname,int complain)2326f20ea67SSam Leffler checkifnet(const char *ifname, int complain)
2336f20ea67SSam Leffler {
2346f20ea67SSam Leffler 	char parent[256];
2356f20ea67SSam Leffler 	int i;
2366f20ea67SSam Leffler 
2376f20ea67SSam Leffler 	if (getparent(ifname, parent) < 0) {
2386f20ea67SSam Leffler 		if (complain)
2396f20ea67SSam Leffler 			syslog(LOG_ERR,
2406f20ea67SSam Leffler 			   "%s: no pointer to parent interface: %m", ifname);
2416f20ea67SSam Leffler 		return 0;
2426f20ea67SSam Leffler 	}
2436f20ea67SSam Leffler 
2446f20ea67SSam Leffler 	for (i = 0; i < nifnets; i++)
2456f20ea67SSam Leffler 		if (strcasecmp(ifnets[i], "any") == 0 ||
2466f20ea67SSam Leffler 		    strcmp(ifnets[i], parent) == 0)
2476f20ea67SSam Leffler 			return 1;
2486f20ea67SSam Leffler 	syslog(LOG_DEBUG, "%s: parent %s not being monitored", ifname, parent);
2496f20ea67SSam Leffler 	return 0;
2506f20ea67SSam Leffler }
2516f20ea67SSam Leffler 
2526f20ea67SSam Leffler /*
2536f20ea67SSam Leffler  * Return 1 if the specified ifnet is a WDS vap.
2546f20ea67SSam Leffler  */
2556f20ea67SSam Leffler static int
iswdsvap(int s,const char * ifname)2566f20ea67SSam Leffler iswdsvap(int s, const char *ifname)
2576f20ea67SSam Leffler {
2586f20ea67SSam Leffler 	struct ifmediareq ifmr;
2596f20ea67SSam Leffler 
2606f20ea67SSam Leffler 	memset(&ifmr, 0, sizeof(ifmr));
2616f20ea67SSam Leffler 	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
2626f20ea67SSam Leffler 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
2636f20ea67SSam Leffler 		err(-1, "%s: cannot get media", ifname);
2646f20ea67SSam Leffler 	return (ifmr.ifm_current & IFM_IEEE80211_WDS) != 0;
2656f20ea67SSam Leffler }
2666f20ea67SSam Leffler 
2676f20ea67SSam Leffler /*
2686f20ea67SSam Leffler  * Fetch the bssid for an ifnet.  The caller is assumed
2696f20ea67SSam Leffler  * to have already verified this is possible.
2706f20ea67SSam Leffler  */
2716c2ea87dSSam Leffler static void
getbssid(int s,const char * ifname,uint8_t bssid[IEEE80211_ADDR_LEN])2726da4c4c5SAdrian Chadd getbssid(int s, const char *ifname, uint8_t bssid[IEEE80211_ADDR_LEN])
2736f20ea67SSam Leffler {
2746f20ea67SSam Leffler 	struct ieee80211req ireq;
2756f20ea67SSam Leffler 
2766f20ea67SSam Leffler 	memset(&ireq, 0, sizeof(ireq));
2776f20ea67SSam Leffler 	strncpy(ireq.i_name, ifname, sizeof(ireq.i_name));
2786f20ea67SSam Leffler 	ireq.i_type = IEEE80211_IOC_BSSID;
2796f20ea67SSam Leffler 	ireq.i_data = bssid;
2806f20ea67SSam Leffler 	ireq.i_len = IEEE80211_ADDR_LEN;
2816f20ea67SSam Leffler 	if (ioctl(s, SIOCG80211, &ireq) < 0)
2826f20ea67SSam Leffler 		err(-1, "%s: cannot fetch bssid", ifname);
2836f20ea67SSam Leffler }
2846f20ea67SSam Leffler 
2856f20ea67SSam Leffler /*
286495842b1SAdrian Chadd  * Fetch the mac address configured for a given ifnet.
287495842b1SAdrian Chadd  * (Note - the current link level address, NOT hwaddr.)
288495842b1SAdrian Chadd  *
289495842b1SAdrian Chadd  * This is currently, sigh, O(n) because there's no current kernel
290495842b1SAdrian Chadd  * API that will do it for a single interface.
291495842b1SAdrian Chadd  *
292495842b1SAdrian Chadd  * Return 0 if successful, -1 if failure.
293495842b1SAdrian Chadd  */
294495842b1SAdrian Chadd static int
getlladdr(const char * ifname,uint8_t macaddr[ETHER_ADDR_LEN])295495842b1SAdrian Chadd getlladdr(const char *ifname, uint8_t macaddr[ETHER_ADDR_LEN])
296495842b1SAdrian Chadd {
297495842b1SAdrian Chadd 	struct ifaddrs *ifap, *ifa;
298495842b1SAdrian Chadd 	struct sockaddr_dl *sdl;
299495842b1SAdrian Chadd 
300495842b1SAdrian Chadd 	if (getifaddrs(&ifap) < 0) {
301495842b1SAdrian Chadd 		warn("%s: getifaddrs", __func__);
302495842b1SAdrian Chadd 		return (-1);
303495842b1SAdrian Chadd 	}
304495842b1SAdrian Chadd 
305495842b1SAdrian Chadd 	/* Look for a matching interface */
30636347cfbSAdrian Chadd 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
307495842b1SAdrian Chadd 		if (strcmp(ifname, ifa->ifa_name) != 0)
308495842b1SAdrian Chadd 			continue;
309495842b1SAdrian Chadd 
310495842b1SAdrian Chadd 		/* Found it - check if there's an ifa_addr */
311495842b1SAdrian Chadd 		if (ifa->ifa_addr == NULL) {
312495842b1SAdrian Chadd 			syslog(LOG_CRIT, "%s: ifname %s; ifa_addr is NULL\n",
313495842b1SAdrian Chadd 			    __func__, ifname);
314495842b1SAdrian Chadd 			goto err;
315495842b1SAdrian Chadd 		}
316495842b1SAdrian Chadd 
317495842b1SAdrian Chadd 		/* Check address family */
318571743bdSAdrian Chadd 		sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
319495842b1SAdrian Chadd 		if (sdl->sdl_type != IFT_ETHER) {
320495842b1SAdrian Chadd 			syslog(LOG_CRIT, "%s: %s: unknown aftype (%d)\n",
321495842b1SAdrian Chadd 			    __func__,
322495842b1SAdrian Chadd 			    ifname,
323495842b1SAdrian Chadd 			    sdl->sdl_type);
324495842b1SAdrian Chadd 			goto err;
325495842b1SAdrian Chadd 		}
326495842b1SAdrian Chadd 		if (sdl->sdl_alen != ETHER_ADDR_LEN) {
327495842b1SAdrian Chadd 			syslog(LOG_CRIT, "%s: %s: aflen too short (%d)\n",
328495842b1SAdrian Chadd 			    __func__,
329495842b1SAdrian Chadd 			    ifname,
330495842b1SAdrian Chadd 			    sdl->sdl_alen);
331495842b1SAdrian Chadd 			goto err;
332495842b1SAdrian Chadd 		}
333495842b1SAdrian Chadd 
334495842b1SAdrian Chadd 		/* Ok, found it */
335495842b1SAdrian Chadd 		memcpy(macaddr, (void *) LLADDR(sdl), ETHER_ADDR_LEN);
336495842b1SAdrian Chadd 		goto ok;
337495842b1SAdrian Chadd 	}
338495842b1SAdrian Chadd 	syslog(LOG_CRIT, "%s: couldn't find ifname %s\n", __func__, ifname);
339495842b1SAdrian Chadd 	/* FALLTHROUGH */
340495842b1SAdrian Chadd err:
341495842b1SAdrian Chadd 	freeifaddrs(ifap);
342495842b1SAdrian Chadd 	return (-1);
343495842b1SAdrian Chadd 
344495842b1SAdrian Chadd ok:
345495842b1SAdrian Chadd 	freeifaddrs(ifap);
346495842b1SAdrian Chadd 	return (0);
347495842b1SAdrian Chadd }
348495842b1SAdrian Chadd 
349495842b1SAdrian Chadd /*
3506f20ea67SSam Leffler  * Scan the system for WDS vaps associated with the ifnet's we're
3516f20ea67SSam Leffler  * supposed to monitor.  Any vaps are added to our internal table
3526f20ea67SSam Leffler  * so we can find them (and destroy them) on station leave.
3536f20ea67SSam Leffler  */
3546f20ea67SSam Leffler static void
scanforvaps(int s)3556f20ea67SSam Leffler scanforvaps(int s)
3566f20ea67SSam Leffler {
3576f20ea67SSam Leffler 	char ifname[IFNAMSIZ+1];
3586da4c4c5SAdrian Chadd 	uint8_t bssid[IEEE80211_ADDR_LEN];
3596f20ea67SSam Leffler 	int i;
3606f20ea67SSam Leffler 
3616f20ea67SSam Leffler 	/* XXX brutal; should just walk sysctl tree */
3626f20ea67SSam Leffler 	for (i = 0; i < 128; i++) {
3636f20ea67SSam Leffler 		snprintf(ifname, sizeof(ifname), "wlan%d", i);
3646f20ea67SSam Leffler 		if (checkifnet(ifname, 0) && iswdsvap(s, ifname)) {
3656f20ea67SSam Leffler 			struct wds *p = malloc(sizeof(struct wds));
3666f20ea67SSam Leffler 			if (p == NULL)
3676f20ea67SSam Leffler 				err(-1, "%s: malloc failed", __func__);
3686f20ea67SSam Leffler 			strlcpy(p->ifname, ifname, IFNAMSIZ);
3696f20ea67SSam Leffler 			getbssid(s, ifname, p->bssid);
3706f20ea67SSam Leffler 			p->next = wds;
3716f20ea67SSam Leffler 			wds = p;
3726f20ea67SSam Leffler 
3736f20ea67SSam Leffler 			syslog(LOG_INFO, "[%s] discover wds vap %s",
3746f20ea67SSam Leffler 			    ether_sprintf(bssid), ifname);
3756f20ea67SSam Leffler 		}
3766f20ea67SSam Leffler 	}
3776f20ea67SSam Leffler }
3786f20ea67SSam Leffler 
3796f20ea67SSam Leffler /*
3806f20ea67SSam Leffler  * Process a routing socket message.  We handle messages related
3816f20ea67SSam Leffler  * to dynamic WDS:
3826f20ea67SSam Leffler  * o on WDS discovery (rx of a 4-address frame with DWDS enabled)
3836f20ea67SSam Leffler  *   we create a WDS vap for the specified mac address
3846f20ea67SSam Leffler  * o on station leave we destroy any associated WDS vap
3856f20ea67SSam Leffler  * o on ifnet destroy we update state if this is manual destroy of
3866f20ea67SSam Leffler  *   a WDS vap in our table
3876f20ea67SSam Leffler  * o if the -j option is supplied on the command line we create
3886f20ea67SSam Leffler  *   WDS vaps on station join/rejoin, this is useful for some setups
3896f20ea67SSam Leffler  *   where a WDS vap is required for 4-address traffic to flow
3906f20ea67SSam Leffler  */
3916f20ea67SSam Leffler static void
handle_rtmsg(struct rt_msghdr * rtm,ssize_t msglen)3926f20ea67SSam Leffler handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen)
3936c2ea87dSSam Leffler {
3946c2ea87dSSam Leffler 	struct if_announcemsghdr *ifan;
3956c2ea87dSSam Leffler 
3961fef838bSAdrian Chadd 	(void) msglen; /* UNUSED */
3971fef838bSAdrian Chadd 
3986c2ea87dSSam Leffler 	if (rtm->rtm_version != RTM_VERSION) {
3996f20ea67SSam Leffler 		syslog(LOG_ERR, "routing message version %d not understood",
4006c2ea87dSSam Leffler 		    rtm->rtm_version);
4016c2ea87dSSam Leffler 		return;
4026c2ea87dSSam Leffler 	}
4036c2ea87dSSam Leffler 	switch (rtm->rtm_type) {
4046c2ea87dSSam Leffler 	case RTM_IFANNOUNCE:
4056c2ea87dSSam Leffler 		ifan = (struct if_announcemsghdr *)rtm;
4066c2ea87dSSam Leffler 		switch (ifan->ifan_what) {
4076c2ea87dSSam Leffler 		case IFAN_ARRIVAL:
4086f20ea67SSam Leffler 			syslog(LOG_DEBUG,
4096f20ea67SSam Leffler 			    "RTM_IFANNOUNCE: if# %d, what: arrival",
4106f20ea67SSam Leffler 			    ifan->ifan_index);
4116c2ea87dSSam Leffler 			break;
4126c2ea87dSSam Leffler 		case IFAN_DEPARTURE:
4136f20ea67SSam Leffler 			syslog(LOG_DEBUG,
4146f20ea67SSam Leffler 			    "RTM_IFANNOUNCE: if# %d, what: departure",
4156f20ea67SSam Leffler 			    ifan->ifan_index);
4166f20ea67SSam Leffler 			/* NB: ok to call w/ unmonitored ifnets */
4176c2ea87dSSam Leffler 			wds_destroy(ifan->ifan_name);
4186c2ea87dSSam Leffler 			break;
4196c2ea87dSSam Leffler 		}
4206c2ea87dSSam Leffler 		break;
4216c2ea87dSSam Leffler 	case RTM_IEEE80211:
4226c2ea87dSSam Leffler #define	V(type)	((struct type *)(&ifan[1]))
4236c2ea87dSSam Leffler 		ifan = (struct if_announcemsghdr *)rtm;
4246c2ea87dSSam Leffler 		switch (ifan->ifan_what) {
4256f20ea67SSam Leffler 		case RTM_IEEE80211_DISASSOC:
4266f20ea67SSam Leffler 			if (!discover_on_join)
4276f20ea67SSam Leffler 				break;
4286f20ea67SSam Leffler 			/* fall thru... */
4296c2ea87dSSam Leffler 		case RTM_IEEE80211_LEAVE:
4306f20ea67SSam Leffler 			if (!checkifnet(ifan->ifan_name, 1))
4316f20ea67SSam Leffler 				break;
4326f20ea67SSam Leffler 			syslog(LOG_INFO, "[%s] station leave",
4336c2ea87dSSam Leffler 			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
4346c2ea87dSSam Leffler 			wds_leave(V(ieee80211_leave_event)->iev_addr);
4356c2ea87dSSam Leffler 			break;
4363bf80639SSam Leffler 		case RTM_IEEE80211_JOIN:
4373bf80639SSam Leffler 		case RTM_IEEE80211_REJOIN:
4386f20ea67SSam Leffler 		case RTM_IEEE80211_ASSOC:
4396f20ea67SSam Leffler 		case RTM_IEEE80211_REASSOC:
4403bf80639SSam Leffler 			if (!discover_on_join)
4413bf80639SSam Leffler 				break;
4423bf80639SSam Leffler 			/* fall thru... */
4436c2ea87dSSam Leffler 		case RTM_IEEE80211_WDS:
4446f20ea67SSam Leffler 			syslog(LOG_INFO, "[%s] wds discovery",
4456c2ea87dSSam Leffler 			    ether_sprintf(V(ieee80211_wds_event)->iev_addr));
4466f20ea67SSam Leffler 			if (!checkifnet(ifan->ifan_name, 1))
4476f20ea67SSam Leffler 				break;
4483bf80639SSam Leffler 			wds_discovery(ifan->ifan_name,
4493bf80639SSam Leffler 			    V(ieee80211_wds_event)->iev_addr);
4506c2ea87dSSam Leffler 			break;
4516c2ea87dSSam Leffler 		}
4526c2ea87dSSam Leffler 		break;
4536c2ea87dSSam Leffler #undef V
4546c2ea87dSSam Leffler 	}
4556c2ea87dSSam Leffler }
4566c2ea87dSSam Leffler 
4576f20ea67SSam Leffler /*
4586f20ea67SSam Leffler  * Handle WDS discovery; create a WDS vap for the specified bssid.
4596f20ea67SSam Leffler  * If a vap already exists then do nothing (can happen when a flood
4606f20ea67SSam Leffler  * of 4-address frames causes multiple events to be queued before
4616f20ea67SSam Leffler  * we create a vap).
4626f20ea67SSam Leffler  */
4636c2ea87dSSam Leffler static void
wds_discovery(const char * ifname,const uint8_t bssid[IEEE80211_ADDR_LEN])4646c2ea87dSSam Leffler wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN])
4656c2ea87dSSam Leffler {
4666c2ea87dSSam Leffler 	struct wds *p;
4676f20ea67SSam Leffler 	char parent[256];
4686c2ea87dSSam Leffler 	char cmd[1024];
469495842b1SAdrian Chadd 	uint8_t macaddr[ETHER_ADDR_LEN];
4706c2ea87dSSam Leffler 	int status;
4716c2ea87dSSam Leffler 
4726f20ea67SSam Leffler 	for (p = wds; p != NULL; p = p->next)
4736f20ea67SSam Leffler 		if (IEEE80211_ADDR_EQ(p->bssid, bssid)) {
4746f20ea67SSam Leffler 			syslog(LOG_INFO, "[%s] wds vap already created (%s)",
4756f20ea67SSam Leffler 			    ether_sprintf(bssid), ifname);
4766f20ea67SSam Leffler 			return;
4776f20ea67SSam Leffler 		}
4786f20ea67SSam Leffler 	if (getparent(ifname, parent) < 0) {
4796f20ea67SSam Leffler 		syslog(LOG_ERR, "%s: no pointer to parent interface: %m",
4806f20ea67SSam Leffler 		    ifname);
4816f20ea67SSam Leffler 		return;
4826f20ea67SSam Leffler 	}
4836f20ea67SSam Leffler 
484495842b1SAdrian Chadd 	if (getlladdr(ifname, macaddr) < 0) {
485495842b1SAdrian Chadd 		syslog(LOG_ERR, "%s: couldn't get lladdr for parent interface: %m",
486495842b1SAdrian Chadd 		    ifname);
487495842b1SAdrian Chadd 		return;
488495842b1SAdrian Chadd 	}
489495842b1SAdrian Chadd 
4906f20ea67SSam Leffler 	p = malloc(sizeof(struct wds));
4916f20ea67SSam Leffler 	if (p == NULL) {
4926f20ea67SSam Leffler 		syslog(LOG_ERR, "%s: malloc failed: %m", __func__);
4936f20ea67SSam Leffler 		return;
4946f20ea67SSam Leffler 	}
4956f20ea67SSam Leffler 	IEEE80211_ADDR_COPY(p->bssid, bssid);
496495842b1SAdrian Chadd 	if (wds_vap_create(parent, macaddr, p) < 0) {
4976f20ea67SSam Leffler 		free(p);
4986f20ea67SSam Leffler 		return;
4996f20ea67SSam Leffler 	}
5006c2ea87dSSam Leffler 	/*
5016f20ea67SSam Leffler 	 * Add to table and launch setup script.
5026c2ea87dSSam Leffler 	 */
5036c2ea87dSSam Leffler 	p->next = wds;
5046c2ea87dSSam Leffler 	wds = p;
505495842b1SAdrian Chadd 	syslog(LOG_INFO, "[%s] create wds vap %s, parent %s (%s)",
506495842b1SAdrian Chadd 	    ether_sprintf(bssid),
507495842b1SAdrian Chadd 	    p->ifname,
508495842b1SAdrian Chadd 	    ifname,
509495842b1SAdrian Chadd 	    parent);
5106f20ea67SSam Leffler 	if (script != NULL) {
5113bf80639SSam Leffler 		snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname);
5126c2ea87dSSam Leffler 		status = system(cmd);
5136c2ea87dSSam Leffler 		if (status)
5146f20ea67SSam Leffler 			syslog(LOG_ERR, "vap setup script %s exited with "
5156f20ea67SSam Leffler 			    "status %d", script, status);
5166f20ea67SSam Leffler 	}
5176c2ea87dSSam Leffler }
5186c2ea87dSSam Leffler 
5196f20ea67SSam Leffler /*
5206f20ea67SSam Leffler  * Destroy a WDS vap (if known).
5216f20ea67SSam Leffler  */
5226c2ea87dSSam Leffler static void
wds_destroy(const char * ifname)5236c2ea87dSSam Leffler wds_destroy(const char *ifname)
5246c2ea87dSSam Leffler {
5256c2ea87dSSam Leffler 	struct wds *p, **pp;
5266c2ea87dSSam Leffler 
5276c2ea87dSSam Leffler 	for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
5286c2ea87dSSam Leffler 		if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0)
5296c2ea87dSSam Leffler 			break;
5306f20ea67SSam Leffler 	if (p != NULL) {
5316c2ea87dSSam Leffler 		*pp = p->next;
5326f20ea67SSam Leffler 		/* NB: vap already destroyed */
5336c2ea87dSSam Leffler 		free(p);
5346f20ea67SSam Leffler 		return;
5356f20ea67SSam Leffler 	}
5366c2ea87dSSam Leffler }
5376c2ea87dSSam Leffler 
5386f20ea67SSam Leffler /*
5396f20ea67SSam Leffler  * Handle a station leave event; destroy any associated WDS vap.
5406f20ea67SSam Leffler  */
5416c2ea87dSSam Leffler static void
wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])5426c2ea87dSSam Leffler wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
5436c2ea87dSSam Leffler {
5446c2ea87dSSam Leffler 	struct wds *p, **pp;
5456c2ea87dSSam Leffler 
5466c2ea87dSSam Leffler 	for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
5476c2ea87dSSam Leffler 		if (IEEE80211_ADDR_EQ(p->bssid, bssid))
5486c2ea87dSSam Leffler 			break;
5496f20ea67SSam Leffler 	if (p != NULL) {
5506c2ea87dSSam Leffler 		*pp = p->next;
5516c2ea87dSSam Leffler 		if (wds_vap_destroy(p->ifname) >= 0)
5526f20ea67SSam Leffler 			syslog(LOG_INFO, "[%s] wds vap %s destroyed",
5536f20ea67SSam Leffler 			    ether_sprintf(bssid), p->ifname);
5546c2ea87dSSam Leffler 		free(p);
5556c2ea87dSSam Leffler 	}
5566f20ea67SSam Leffler }
5576c2ea87dSSam Leffler 
5586c2ea87dSSam Leffler static int
wds_vap_create(const char * parent,uint8_t macaddr[ETHER_ADDR_LEN],struct wds * p)559495842b1SAdrian Chadd wds_vap_create(const char *parent, uint8_t macaddr[ETHER_ADDR_LEN],
560495842b1SAdrian Chadd     struct wds *p)
5616c2ea87dSSam Leffler {
5626c2ea87dSSam Leffler 	struct ieee80211_clone_params cp;
5636c2ea87dSSam Leffler 	struct ifreq ifr;
5646c2ea87dSSam Leffler 	int s, status;
565495842b1SAdrian Chadd 	char bssid_str[32], macaddr_str[32];
5666c2ea87dSSam Leffler 
5676c2ea87dSSam Leffler 	memset(&cp, 0, sizeof(cp));
568495842b1SAdrian Chadd 
569495842b1SAdrian Chadd 	/* Parent interface */
5706c2ea87dSSam Leffler 	strncpy(cp.icp_parent, parent, IFNAMSIZ);
571495842b1SAdrian Chadd 
572495842b1SAdrian Chadd 	/* WDS interface */
5736c2ea87dSSam Leffler 	cp.icp_opmode = IEEE80211_M_WDS;
574495842b1SAdrian Chadd 
575495842b1SAdrian Chadd 	/* BSSID for the current node */
5766c2ea87dSSam Leffler 	IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid);
5776c2ea87dSSam Leffler 
578495842b1SAdrian Chadd 	/*
579495842b1SAdrian Chadd 	 * Set the MAC address to match the actual interface
580495842b1SAdrian Chadd 	 * that we received the discovery event from.
581495842b1SAdrian Chadd 	 * That way we can run WDS on any VAP rather than
582495842b1SAdrian Chadd 	 * only the first VAP and then correctly set the
583495842b1SAdrian Chadd 	 * MAC address.
584495842b1SAdrian Chadd 	 */
585495842b1SAdrian Chadd 	cp.icp_flags |= IEEE80211_CLONE_MACADDR;
586495842b1SAdrian Chadd 	IEEE80211_ADDR_COPY(cp.icp_macaddr, macaddr);
587495842b1SAdrian Chadd 
5886c2ea87dSSam Leffler 	memset(&ifr, 0, sizeof(ifr));
5896c2ea87dSSam Leffler 	strncpy(ifr.ifr_name, "wlan", IFNAMSIZ);
5906c2ea87dSSam Leffler 	ifr.ifr_data = (void *) &cp;
5916c2ea87dSSam Leffler 
5926c2ea87dSSam Leffler 	status = -1;
5936c2ea87dSSam Leffler 	s = socket(AF_INET, SOCK_DGRAM, 0);
5946c2ea87dSSam Leffler 	if (s >= 0) {
5956c2ea87dSSam Leffler 		if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) {
5966c2ea87dSSam Leffler 			strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ);
5976c2ea87dSSam Leffler 			status = 0;
5986c2ea87dSSam Leffler 		} else {
5996f20ea67SSam Leffler 			syslog(LOG_ERR, "SIOCIFCREATE2("
600495842b1SAdrian Chadd 			    "mode %u flags 0x%x parent %s bssid %s macaddr %s): %m",
6016c2ea87dSSam Leffler 			    cp.icp_opmode, cp.icp_flags, parent,
602495842b1SAdrian Chadd 			    ether_ntoa_r((void *) cp.icp_bssid, bssid_str),
603495842b1SAdrian Chadd 			    ether_ntoa_r((void *) cp.icp_macaddr, macaddr_str));
6046c2ea87dSSam Leffler 		}
6056c2ea87dSSam Leffler 		close(s);
6066c2ea87dSSam Leffler 	} else
6076f20ea67SSam Leffler 		syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
6086c2ea87dSSam Leffler 	return status;
6096c2ea87dSSam Leffler }
6106c2ea87dSSam Leffler 
6116c2ea87dSSam Leffler static int
wds_vap_destroy(const char * ifname)6126c2ea87dSSam Leffler wds_vap_destroy(const char *ifname)
6136c2ea87dSSam Leffler {
6146c2ea87dSSam Leffler 	struct ieee80211req ifr;
6156c2ea87dSSam Leffler 	int s, status;
6166c2ea87dSSam Leffler 
6176c2ea87dSSam Leffler 	s = socket(AF_INET, SOCK_DGRAM, 0);
6186c2ea87dSSam Leffler 	if (s < 0) {
6196f20ea67SSam Leffler 		syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
6206c2ea87dSSam Leffler 		return -1;
6216c2ea87dSSam Leffler 	}
6226c2ea87dSSam Leffler 	memset(&ifr, 0, sizeof(ifr));
6236c2ea87dSSam Leffler 	strncpy(ifr.i_name, ifname, IFNAMSIZ);
6246c2ea87dSSam Leffler 	if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
6256f20ea67SSam Leffler 		syslog(LOG_ERR, "ioctl(SIOCIFDESTROY): %m");
6266c2ea87dSSam Leffler 		status = -1;
6276c2ea87dSSam Leffler 	} else
6286c2ea87dSSam Leffler 		status = 0;
6296c2ea87dSSam Leffler 	close(s);
6306c2ea87dSSam Leffler 	return status;
6316c2ea87dSSam Leffler }
632