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