wlanwatch.c revision 179122
1/*-
2 * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD: head/tools/tools/net80211/wlanwatch/wlanwatch.c 179122 2008-05-19 17:51:00Z thompsa $
30 */
31
32/*
33 * Monitor 802.11 events using a routing socket.
34 * Code liberaly swiped from route(8).
35 */
36#include <sys/param.h>
37#include <sys/file.h>
38#include <sys/socket.h>
39#include <sys/ioctl.h>
40#include <sys/sysctl.h>
41#include <sys/types.h>
42
43#include <net/if.h>
44#include <net/route.h>
45#include <net/if_dl.h>
46#include <netinet/in.h>
47#include <netinet/if_ether.h>
48#include <netatalk/at.h>
49#ifdef __NetBSD__
50#include <net80211/ieee80211_netbsd.h>
51#elif __FreeBSD__
52#include <net80211/ieee80211_freebsd.h>
53#else
54#error	"No support for your operating system!"
55#endif
56#include <arpa/inet.h>
57#include <netdb.h>
58
59#include <ctype.h>
60#include <err.h>
61#include <errno.h>
62#include <paths.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <sysexits.h>
67#include <unistd.h>
68#include <ifaddrs.h>
69
70/* XXX */
71enum ieee80211_notify_cac_event {
72	IEEE80211_NOTIFY_CAC_START  = 0, /* CAC timer started */
73	IEEE80211_NOTIFY_CAC_STOP   = 1, /* CAC intentionally stopped */
74	IEEE80211_NOTIFY_CAC_RADAR  = 2, /* CAC stopped due to radar detectio */
75	IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
76};
77
78static	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
79
80int	nflag = 0;
81
82int
83main(int argc, char *argv[])
84{
85	int n, s;
86	char msg[2048];
87
88	s = socket(PF_ROUTE, SOCK_RAW, 0);
89	if (s < 0)
90		err(EX_OSERR, "socket");
91	for(;;) {
92		n = read(s, msg, 2048);
93		print_rtmsg((struct rt_msghdr *)msg, n);
94	}
95	return 0;
96}
97
98static void
99bprintf(FILE *fp, int b, char *s)
100{
101	int i;
102	int gotsome = 0;
103
104	if (b == 0)
105		return;
106	while ((i = *s++) != 0) {
107		if (b & (1 << (i-1))) {
108			if (gotsome == 0)
109				i = '<';
110			else
111				i = ',';
112			(void) putc(i, fp);
113			gotsome = 1;
114			for (; (i = *s) > 32; s++)
115				(void) putc(i, fp);
116		} else
117			while (*s > 32)
118				s++;
119	}
120	if (gotsome)
121		putc('>', fp);
122}
123
124char metricnames[] =
125"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
126"\1mtu";
127char routeflags[] =
128"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
129"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
130"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
131"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
132char ifnetflags[] =
133"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
134"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
135"\017LINK2\020MULTICAST";
136char addrnames[] =
137"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
138
139static const char *
140routename(struct sockaddr *sa)
141{
142	char *cp;
143	static char line[MAXHOSTNAMELEN + 1];
144	struct hostent *hp;
145	static char domain[MAXHOSTNAMELEN + 1];
146	static int first = 1, n;
147
148	if (first) {
149		first = 0;
150		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
151		    (cp = strchr(domain, '.'))) {
152			domain[MAXHOSTNAMELEN] = '\0';
153			(void) strcpy(domain, cp + 1);
154		} else
155			domain[0] = 0;
156	}
157
158	if (sa->sa_len == 0)
159		strcpy(line, "default");
160	else switch (sa->sa_family) {
161
162	case AF_INET:
163	    {	struct in_addr in;
164		in = ((struct sockaddr_in *)sa)->sin_addr;
165
166		cp = 0;
167		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
168			cp = "default";
169		if (cp == 0 && !nflag) {
170			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
171				AF_INET);
172			if (hp) {
173				if ((cp = strchr(hp->h_name, '.')) &&
174				    !strcmp(cp + 1, domain))
175					*cp = 0;
176				cp = hp->h_name;
177			}
178		}
179		if (cp) {
180			strncpy(line, cp, sizeof(line) - 1);
181			line[sizeof(line) - 1] = '\0';
182		} else
183			(void) sprintf(line, "%s", inet_ntoa(in));
184		break;
185	    }
186
187#ifdef INET6
188	case AF_INET6:
189	{
190		struct sockaddr_in6 sin6; /* use static var for safety */
191		int niflags = 0;
192#ifdef NI_WITHSCOPEID
193		niflags = NI_WITHSCOPEID;
194#endif
195
196		memset(&sin6, 0, sizeof(sin6));
197		memcpy(&sin6, sa, sa->sa_len);
198		sin6.sin6_len = sizeof(struct sockaddr_in6);
199		sin6.sin6_family = AF_INET6;
200#ifdef __KAME__
201		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
202		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
203		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
204		    sin6.sin6_scope_id == 0) {
205			sin6.sin6_scope_id =
206			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
207			sin6.sin6_addr.s6_addr[2] = 0;
208			sin6.sin6_addr.s6_addr[3] = 0;
209		}
210#endif
211		if (nflag)
212			niflags |= NI_NUMERICHOST;
213		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
214		    line, sizeof(line), NULL, 0, niflags) != 0)
215			strncpy(line, "invalid", sizeof(line));
216
217		return(line);
218	}
219#endif
220
221	case AF_LINK:
222		return (link_ntoa((struct sockaddr_dl *)sa));
223
224	default:
225	    {	u_short *s = (u_short *)sa;
226		u_short *slim = s + ((sa->sa_len + 1) >> 1);
227		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
228		char *cpe = line + sizeof(line);
229
230		while (++s < slim && cp < cpe) /* start with sa->sa_data */
231			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
232				cp += n;
233			else
234				*cp = '\0';
235		break;
236	    }
237	}
238	return (line);
239}
240
241#ifndef SA_SIZE
242/*
243 * This macro returns the size of a struct sockaddr when passed
244 * through a routing socket. Basically we round up sa_len to
245 * a multiple of sizeof(long), with a minimum of sizeof(long).
246 * The check for a NULL pointer is just a convenience, probably never used.
247 * The case sa_len == 0 should only apply to empty structures.
248 */
249#define SA_SIZE(sa)						\
250    (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
251	sizeof(long)		:				\
252	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
253#endif
254
255static void
256pmsg_addrs(char *cp, int addrs)
257{
258	struct sockaddr *sa;
259	int i;
260
261	if (addrs == 0) {
262		(void) putchar('\n');
263		return;
264	}
265	printf("\nsockaddrs: ");
266	bprintf(stdout, addrs, addrnames);
267	putchar('\n');
268	for (i = 1; i; i <<= 1)
269		if (i & addrs) {
270			sa = (struct sockaddr *)cp;
271			printf(" %s", routename(sa));
272			cp += SA_SIZE(sa);
273		}
274	putchar('\n');
275}
276
277static const char *
278ether_sprintf(const uint8_t mac[6])
279{
280	static char buf[32];
281
282	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
283		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
284	return buf;
285}
286
287static void
288print_rtmsg(struct rt_msghdr *rtm, int msglen)
289{
290	struct if_msghdr *ifm;
291	struct if_announcemsghdr *ifan;
292	time_t now = time(NULL);
293	char *cnow = ctime(&now);
294
295	if (rtm->rtm_version != RTM_VERSION) {
296		(void) printf("routing message version %d not understood\n",
297		    rtm->rtm_version);
298		return;
299	}
300	switch (rtm->rtm_type) {
301	case RTM_IFINFO:
302		ifm = (struct if_msghdr *)rtm;
303		printf("%.19s RTM_IFINFO: if# %d, ",
304			cnow, ifm->ifm_index);
305		switch (ifm->ifm_data.ifi_link_state) {
306		case LINK_STATE_DOWN:
307			printf("link: down, flags:");
308			break;
309		case LINK_STATE_UP:
310			printf("link: up, flags:");
311			break;
312		default:
313			printf("link: unknown<%d>, flags:",
314			    ifm->ifm_data.ifi_link_state);
315			break;
316		}
317		bprintf(stdout, ifm->ifm_flags, ifnetflags);
318		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
319		fflush(stdout);
320		break;
321	case RTM_IFANNOUNCE:
322		ifan = (struct if_announcemsghdr *)rtm;
323		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
324			cnow, ifan->ifan_index);
325		switch (ifan->ifan_what) {
326		case IFAN_ARRIVAL:
327			printf("arrival");
328			break;
329		case IFAN_DEPARTURE:
330			printf("departure");
331			break;
332		default:
333			printf("#%d", ifan->ifan_what);
334			break;
335		}
336		printf("\n");
337		fflush(stdout);
338		break;
339	case RTM_IEEE80211:
340#define	V(type)	((struct type *)(&ifan[1]))
341		ifan = (struct if_announcemsghdr *)rtm;
342		printf("%.19s RTM_IEEE80211: if# %d, ", cnow, ifan->ifan_index);
343		switch (ifan->ifan_what) {
344		case RTM_IEEE80211_ASSOC:
345			printf("associate with %s",
346			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
347			break;
348		case RTM_IEEE80211_REASSOC:
349			printf("reassociate with %s",
350			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
351			break;
352		case RTM_IEEE80211_DISASSOC:
353			printf("disassociate");
354			break;
355		case RTM_IEEE80211_JOIN:
356		case RTM_IEEE80211_REJOIN:
357			printf("%s station %sjoin",
358			    ether_sprintf(V(ieee80211_join_event)->iev_addr),
359			    ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : ""
360			);
361			break;
362		case RTM_IEEE80211_LEAVE:
363			printf("%s station leave",
364			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
365			break;
366		case RTM_IEEE80211_SCAN:
367			printf("scan complete");
368			break;
369		case RTM_IEEE80211_REPLAY:
370			printf("replay failure: src %s "
371			    , ether_sprintf(V(ieee80211_replay_event)->iev_src)
372			);
373			printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu"
374			    , ether_sprintf(V(ieee80211_replay_event)->iev_dst)
375			    , V(ieee80211_replay_event)->iev_cipher
376			    , V(ieee80211_replay_event)->iev_keyix
377			    , V(ieee80211_replay_event)->iev_keyrsc
378			    , V(ieee80211_replay_event)->iev_rsc
379			);
380			break;
381		case RTM_IEEE80211_MICHAEL:
382			printf("michael failure: src %s "
383			    , ether_sprintf(V(ieee80211_michael_event)->iev_src)
384			);
385			printf("dst %s cipher %u keyix %u"
386			    , ether_sprintf(V(ieee80211_michael_event)->iev_dst)
387			    , V(ieee80211_michael_event)->iev_cipher
388			    , V(ieee80211_michael_event)->iev_keyix
389			);
390			break;
391		case RTM_IEEE80211_WDS:
392			printf("%s wds discovery",
393			    ether_sprintf(V(ieee80211_wds_event)->iev_addr));
394			break;
395		case RTM_IEEE80211_CSA:
396			printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d"
397			    , V(ieee80211_csa_event)->iev_ieee
398			    , V(ieee80211_csa_event)->iev_freq
399			    , V(ieee80211_csa_event)->iev_flags
400			    , V(ieee80211_csa_event)->iev_mode
401			    , V(ieee80211_csa_event)->iev_count
402			);
403			break;
404		case RTM_IEEE80211_CAC:
405			printf("channel availability check "
406			    "(channel %u, %u MHz flags 0x%x) "
407			    , V(ieee80211_cac_event)->iev_ieee
408			    , V(ieee80211_cac_event)->iev_freq
409			    , V(ieee80211_cac_event)->iev_flags
410			);
411			switch (V(ieee80211_cac_event)->iev_type) {
412			case IEEE80211_NOTIFY_CAC_START:
413				printf("start timer");
414				break;
415			case IEEE80211_NOTIFY_CAC_STOP:
416				printf("stop timer");
417				break;
418			case IEEE80211_NOTIFY_CAC_EXPIRE:
419				printf("timer expired");
420				break;
421			case IEEE80211_NOTIFY_CAC_RADAR:
422				printf("radar detected");
423				break;
424			default:
425				printf("unknown type %d",
426				   V(ieee80211_cac_event)->iev_type);
427				break;
428			}
429			break;
430		case RTM_IEEE80211_DEAUTH:
431			printf("%s wds deauth",
432			    ether_sprintf(V(ieee80211_deauth_event)->iev_addr));
433			break;
434		case RTM_IEEE80211_AUTH:
435			printf("%s node authenticate",
436			    ether_sprintf(V(ieee80211_auth_event)->iev_addr));
437			break;
438		case RTM_IEEE80211_COUNTRY:
439			printf("%s adopt country code '%c%c'",
440			    ether_sprintf(V(ieee80211_country_event)->iev_addr),
441			    V(ieee80211_country_event)->iev_cc[0],
442			    V(ieee80211_country_event)->iev_cc[1]);
443			break;
444		case RTM_IEEE80211_RADIO:
445			printf("radio %s",
446			    V(ieee80211_radio_event)->iev_state ? "ON" : "OFF");
447			break;
448		default:
449			printf("what: #%d", ifan->ifan_what);
450			break;
451		}
452		printf("\n");
453		fflush(stdout);
454		break;
455#undef V
456	}
457}
458