wlanwds.c revision 178360
154359Sroberto/*-
254359Sroberto * Copyright (c) 2006-2007 Sam Leffler, Errno Consulting
354359Sroberto * All rights reserved.
454359Sroberto *
554359Sroberto * Redistribution and use in source and binary forms, with or without
654359Sroberto * modification, are permitted provided that the following conditions
754359Sroberto * are met:
854359Sroberto * 1. Redistributions of source code must retain the above copyright
954359Sroberto *    notice, this list of conditions and the following disclaimer,
1054359Sroberto *    without modification.
1154359Sroberto * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1254359Sroberto *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1354359Sroberto *    redistribution must be conditioned upon including a substantially
1454359Sroberto *    similar Disclaimer requirement for further binary redistribution.
1554359Sroberto *
1654359Sroberto * NO WARRANTY
1754359Sroberto * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1854359Sroberto * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1954359Sroberto * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2054359Sroberto * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2154359Sroberto * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2254359Sroberto * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2354359Sroberto * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2454359Sroberto * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2554359Sroberto * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2654359Sroberto * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2754359Sroberto * THE POSSIBILITY OF SUCH DAMAGES.
2854359Sroberto *
2954359Sroberto * $FreeBSD: head/tools/tools/net80211/wlanwds/wlanwds.c 178360 2008-04-20 20:43:13Z sam $
3054359Sroberto */
3154359Sroberto
3254359Sroberto/*
3354359Sroberto * Test app to demonstrate how to handle dynamic WDS links:
3454359Sroberto * o monitor 802.11 events for wds discovery events
35280849Scy * o create wds vap's in response to wds discovery events
3654359Sroberto *   and launch a script to handle adding the vap to the
3754359Sroberto *   bridge, etc.
3854359Sroberto * o destroy wds vap's when station leaves
3954359Sroberto *
4054359Sroberto * Note we query only internal state which means if we don't see
4154359Sroberto * a vap created we won't handle leave/delete properly.  Also there
4254359Sroberto * are several fixed pathnames/strings.  Some require fixing
4354359Sroberto * kernel support (e.g. sysctl to find parent device of a vap).
4454359Sroberto *
4554359Sroberto * Code liberaly swiped from wlanwatch; probably should nuke printfs.
4654359Sroberto */
4754359Sroberto#include <sys/param.h>
4854359Sroberto#include <sys/file.h>
4954359Sroberto#include <sys/socket.h>
5054359Sroberto#include <sys/ioctl.h>
5154359Sroberto#include <sys/sysctl.h>
5254359Sroberto#include <sys/types.h>
5354359Sroberto
5454359Sroberto#include <net/if.h>
5554359Sroberto#include "net/if_media.h"
5654359Sroberto#include <net/route.h>
5754359Sroberto#include <net/if_dl.h>
5854359Sroberto#include <netinet/in.h>
5954359Sroberto#include <netinet/if_ether.h>
6054359Sroberto#include <netatalk/at.h>
6154359Sroberto#include "net80211/ieee80211_ioctl.h"
6254359Sroberto#include "net80211/ieee80211_freebsd.h"
6354359Sroberto#include <arpa/inet.h>
6454359Sroberto#include <netdb.h>
6554359Sroberto
6654359Sroberto#include <ctype.h>
6754359Sroberto#include <err.h>
68280849Scy#include <errno.h>
6954359Sroberto#include <paths.h>
7054359Sroberto#include <stdio.h>
7154359Sroberto#include <stdlib.h>
7254359Sroberto#include <string.h>
7354359Sroberto#include <sysexits.h>
7454359Sroberto#include <unistd.h>
7554359Sroberto#include <ifaddrs.h>
7654359Sroberto
7754359Sroberto#define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
78280849Scy#define	IEEE80211_ADDR_COPY(dst,src)	memcpy(dst,src,IEEE80211_ADDR_LEN)
79280849Scy
80280849Scystruct wds {
81280849Scy	struct wds *next;
82280849Scy	uint8_t	bssid[IEEE80211_ADDR_LEN];	/* bssid of associated sta */
8354359Sroberto	char	ifname[IFNAMSIZ];		/* vap interface name */
84280849Scy};
85280849Scystatic struct wds *wds;
86280849Scy
87280849Scystatic	const char *bridge = "bridge0";
88280849Scystatic	const char *parent = "mv0";		/* XXX no sysctl to find this */
89280849Scystatic	const char *script = "/usr/local/bin/wdsup";
90280849Scystatic	int verbose = 0;
91280849Scy
92280849Scystatic	void handle_rtmsg(struct rt_msghdr *rtm, int msglen);
93280849Scystatic	void wds_discovery(const char *ifname,
94280849Scy		const uint8_t bssid[IEEE80211_ADDR_LEN]);
95280849Scystatic	void wds_destroy(const char *ifname);
96280849Scystatic	void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]);
97280849Scystatic	int wds_vap_create(const char *ifname, struct wds *);
98280849Scystatic	int wds_vap_destroy(const char *ifname);
99280849Scy
100280849Scyint
101280849Scymain(int argc, char *argv[])
102280849Scy{
10354359Sroberto	int n, s, c;
10454359Sroberto	char msg[2048];
10554359Sroberto
10654359Sroberto	while ((c = getopt(argc, argv, "b:p:s:vn")) != -1)
10754359Sroberto		switch (c) {
10854359Sroberto		case 'b':
109280849Scy			bridge = optarg;
11054359Sroberto			break;
11154359Sroberto		case 'p':
11254359Sroberto			parent = optarg;
11354359Sroberto			break;
114280849Scy		case 's':
115280849Scy			script = optarg;
11654359Sroberto			break;
11754359Sroberto		case 'v':
11854359Sroberto			verbose = 1;
11954359Sroberto			break;
12054359Sroberto		case '?':
12154359Sroberto			errx(1, "usage: %s [-b <bridgename>] [-p <parentname>] [-s <set_scriptname>]\n"
12254359Sroberto				" [-v (for verbose)]\n", argv[0]);
123280849Scy			/*NOTREACHED*/
12454359Sroberto		}
125280849Scy
126280849Scy	s = socket(PF_ROUTE, SOCK_RAW, 0);
127280849Scy	if (s < 0)
12854359Sroberto		err(EX_OSERR, "socket");
129280849Scy	for(;;) {
13054359Sroberto		n = read(s, msg, 2048);
131280849Scy		handle_rtmsg((struct rt_msghdr *)msg, n);
132280849Scy	}
133280849Scy	return 0;
13454359Sroberto}
135280849Scy
13654359Srobertostatic const char *
137280849Scyether_sprintf(const uint8_t mac[6])
138280849Scy{
139280849Scy	static char buf[32];
140280849Scy
14154359Sroberto	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
142280849Scy		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
14354359Sroberto	return buf;
144280849Scy}
145280849Scy
146280849Scystatic void
147280849Scyhandle_rtmsg(struct rt_msghdr *rtm, int msglen)
148280849Scy{
149280849Scy	struct if_announcemsghdr *ifan;
150280849Scy	time_t now = time(NULL);
151280849Scy	char *cnow = ctime(&now);
152280849Scy
153280849Scy	if (rtm->rtm_version != RTM_VERSION) {
154280849Scy		(void) printf("routing message version %d not understood\n",
15554359Sroberto		    rtm->rtm_version);
15654359Sroberto		return;
15754359Sroberto	}
158280849Scy	switch (rtm->rtm_type) {
159280849Scy	case RTM_IFANNOUNCE:
160280849Scy		ifan = (struct if_announcemsghdr *)rtm;
161280849Scy		if (!verbose)
16254359Sroberto			break;
16354359Sroberto		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
16454359Sroberto			cnow, ifan->ifan_index);
165280849Scy		switch (ifan->ifan_what) {
166280849Scy		case IFAN_ARRIVAL:
167280849Scy			printf("arrival");
16854359Sroberto			break;
16954359Sroberto		case IFAN_DEPARTURE:
17054359Sroberto			printf("departure");
171280849Scy			wds_destroy(ifan->ifan_name);
172280849Scy			break;
173280849Scy		default:
17454359Sroberto			printf("#%d", ifan->ifan_what);
17554359Sroberto			break;
17654359Sroberto		}
177280849Scy		printf("\n");
178280849Scy		break;
179280849Scy	case RTM_IEEE80211:
18054359Sroberto#define	V(type)	((struct type *)(&ifan[1]))
181280849Scy		ifan = (struct if_announcemsghdr *)rtm;
18254359Sroberto		switch (ifan->ifan_what) {
183280849Scy		case RTM_IEEE80211_LEAVE:
184280849Scy			if (verbose)
185280849Scy				printf("%.19s %s station leave", cnow,
186280849Scy				    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
18754359Sroberto			wds_leave(V(ieee80211_leave_event)->iev_addr);
188280849Scy			if (verbose)
18954359Sroberto				printf("\n");
19054359Sroberto			break;
19154359Sroberto		case RTM_IEEE80211_WDS:
19254359Sroberto			if (verbose)
19354359Sroberto				printf("%.19s %s wds discovery", cnow,
19454359Sroberto				    ether_sprintf(V(ieee80211_wds_event)->iev_addr));
19554359Sroberto			/* XXX wlan0 */
196280849Scy			wds_discovery("wlan0", V(ieee80211_wds_event)->iev_addr);
197280849Scy			if (verbose)
198280849Scy				printf("\n");
199280849Scy			break;
200280849Scy		case RTM_IEEE80211_ASSOC:
20154359Sroberto		case RTM_IEEE80211_REASSOC:
20254359Sroberto		case RTM_IEEE80211_DISASSOC:
203280849Scy		case RTM_IEEE80211_JOIN:
20454359Sroberto		case RTM_IEEE80211_REJOIN:
20554359Sroberto		case RTM_IEEE80211_SCAN:
206280849Scy		case RTM_IEEE80211_REPLAY:
207280849Scy		case RTM_IEEE80211_MICHAEL:
208280849Scy			break;
209280849Scy		default:
210280849Scy			if (verbose)
211280849Scy				printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow,
212280849Scy					ifan->ifan_index, ifan->ifan_what);
213280849Scy			break;
21454359Sroberto		}
21554359Sroberto		break;
21654359Sroberto#undef V
21754359Sroberto	}
21854359Sroberto}
219280849Scy
220280849Scystatic void
22154359Srobertowds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN])
22254359Sroberto{
223280849Scy	struct wds *p;
22454359Sroberto
22554359Sroberto	for (p = wds; p != NULL; p = p->next)
22654359Sroberto		if (IEEE80211_ADDR_EQ(p->bssid, bssid)) {
22754359Sroberto			if (verbose)
22854359Sroberto				printf(" (already created)");
22954359Sroberto			return;
23054359Sroberto		}
23154359Sroberto	p = malloc(sizeof(struct wds));
23254359Sroberto	if (p == NULL) {
23354359Sroberto		warn("%s: malloc", __func__);
23454359Sroberto		return;
235182007Sroberto	}
23654359Sroberto	IEEE80211_ADDR_COPY(p->bssid, bssid);
23754359Sroberto	/* XXX mv0: no sysctl to find parent device */
23854359Sroberto	if (wds_vap_create(parent, p) >= 0) {
239280849Scy		char cmd[1024];
240280849Scy		int status;
241280849Scy
242280849Scy		/*
243280849Scy		 * Add to table.
244280849Scy		 */
24554359Sroberto		p->next = wds;
24654359Sroberto		wds = p;
24754359Sroberto		if (verbose)
24854359Sroberto			printf(" (create %s)", p->ifname);
24954359Sroberto		/*
250280849Scy		 * XXX launch script to setup bridge, etc.
25154359Sroberto		 */
25254359Sroberto		snprintf(cmd, sizeof(cmd), "%s %s %s",
25354359Sroberto			script, p->ifname, bridge);
25454359Sroberto		status = system(cmd);
25554359Sroberto		if (status)
25654359Sroberto			warnx("vap setup script %s exited with status %d\n",
25754359Sroberto				script, status);
258280849Scy	} else
259280849Scy		free(p);
260280849Scy}
261280849Scy
262280849Scystatic void
263280849Scywds_destroy(const char *ifname)
264280849Scy{
265280849Scy	struct wds *p, **pp;
266280849Scy
267282408Scy	for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
268282408Scy		if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0)
269280849Scy			break;
270280849Scy	/* XXX check for device directly */
271280849Scy	if (p == NULL)		/* not ours/known */
272282408Scy		return;
273282408Scy	*pp = p->next;
274280849Scy	if (wds_vap_destroy(p->ifname) >= 0)
275280849Scy		if (verbose)
276280849Scy			printf(" (wds vap destroyed)");
277280849Scy	free(p);
278280849Scy}
279280849Scy
280280849Scystatic void
281280849Scywds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
282280849Scy{
283280849Scy	struct wds *p, **pp;
284280849Scy
285280849Scy	for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
286280849Scy		if (IEEE80211_ADDR_EQ(p->bssid, bssid))
287280849Scy			break;
288280849Scy	/* XXX fall back to check device */
289280849Scy	if (p == NULL)		/* not ours/known */
290280849Scy		return;
291280849Scy	*pp = p->next;
292280849Scy	if (wds_vap_destroy(p->ifname) >= 0)
293280849Scy		printf(" (wds vap destroyed)");
294280849Scy	free(p);
295280849Scy}
296280849Scy
297280849Scystatic int
298280849Scywds_vap_create(const char *parent, struct wds *p)
299280849Scy{
300280849Scy	struct ieee80211_clone_params cp;
301280849Scy	struct ifreq ifr;
302280849Scy	int s, status;
303280849Scy
304280849Scy	memset(&cp, 0, sizeof(cp));
305280849Scy	strncpy(cp.icp_parent, parent, IFNAMSIZ);
306280849Scy	cp.icp_opmode = IEEE80211_M_WDS;
307280849Scy	IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid);
308280849Scy
309280849Scy	memset(&ifr, 0, sizeof(ifr));
310280849Scy	strncpy(ifr.ifr_name, "wlan", IFNAMSIZ);
311280849Scy	ifr.ifr_data = (void *) &cp;
312280849Scy
313280849Scy	status = -1;
314280849Scy	s = socket(AF_INET, SOCK_DGRAM, 0);
315280849Scy	if (s >= 0) {
316280849Scy		if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) {
31754359Sroberto			strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ);
318280849Scy			status = 0;
319280849Scy		} else {
320280849Scy			warn("SIOCIFCREATE2("
321280849Scy			    "mode %u flags 0x%x parent %s bssid %s)",
322280849Scy			    cp.icp_opmode, cp.icp_flags, parent,
32354359Sroberto			    ether_sprintf(cp.icp_bssid));
324280849Scy		}
325280849Scy		close(s);
32654359Sroberto	} else
32754359Sroberto		warn("socket(SOCK_DRAGM)");
328280849Scy	return status;
32954359Sroberto}
330280849Scy
331280849Scystatic int
332280849Scywds_vap_destroy(const char *ifname)
333280849Scy{
334280849Scy	struct ieee80211req ifr;
33554359Sroberto	int s, status;
336280849Scy
33754359Sroberto	s = socket(AF_INET, SOCK_DGRAM, 0);
33854359Sroberto	if (s < 0) {
339280849Scy		warn("socket(SOCK_DRAGM)");
340280849Scy		return -1;
34154359Sroberto	}
34254359Sroberto	memset(&ifr, 0, sizeof(ifr));
34354359Sroberto	strncpy(ifr.i_name, ifname, IFNAMSIZ);
34454359Sroberto	if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
34554359Sroberto		warn("ioctl(SIOCIFDESTROY)");
34654359Sroberto		status = -1;
347358659Scy	} else
348358659Scy		status = 0;
34954359Sroberto	close(s);
350280849Scy	return status;
351280849Scy}
352280849Scy