wlanwatch.c revision 174244
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 174244 2007-12-04 05:52:01Z sam $
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
70static	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
71
72int	nflag = 0;
73
74int
75main(int argc, char *argv[])
76{
77	int n, s;
78	char msg[2048];
79
80	s = socket(PF_ROUTE, SOCK_RAW, 0);
81	if (s < 0)
82		err(EX_OSERR, "socket");
83	for(;;) {
84		n = read(s, msg, 2048);
85		print_rtmsg((struct rt_msghdr *)msg, n);
86	}
87	return 0;
88}
89
90static void
91bprintf(fp, b, s)
92	FILE *fp;
93	int b;
94	u_char *s;
95{
96	int i;
97	int gotsome = 0;
98
99	if (b == 0)
100		return;
101	while ((i = *s++) != 0) {
102		if (b & (1 << (i-1))) {
103			if (gotsome == 0)
104				i = '<';
105			else
106				i = ',';
107			(void) putc(i, fp);
108			gotsome = 1;
109			for (; (i = *s) > 32; s++)
110				(void) putc(i, fp);
111		} else
112			while (*s > 32)
113				s++;
114	}
115	if (gotsome)
116		putc('>', fp);
117}
118
119char metricnames[] =
120"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
121"\1mtu";
122char routeflags[] =
123"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
124"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
125"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
126"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
127char ifnetflags[] =
128"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
129"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
130"\017LINK2\020MULTICAST";
131char addrnames[] =
132"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
133
134const char *
135routename(sa)
136	struct sockaddr *sa;
137{
138	char *cp;
139	static char line[MAXHOSTNAMELEN + 1];
140	struct hostent *hp;
141	static char domain[MAXHOSTNAMELEN + 1];
142	static int first = 1, n;
143
144	if (first) {
145		first = 0;
146		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
147		    (cp = strchr(domain, '.'))) {
148			domain[MAXHOSTNAMELEN] = '\0';
149			(void) strcpy(domain, cp + 1);
150		} else
151			domain[0] = 0;
152	}
153
154	if (sa->sa_len == 0)
155		strcpy(line, "default");
156	else switch (sa->sa_family) {
157
158	case AF_INET:
159	    {	struct in_addr in;
160		in = ((struct sockaddr_in *)sa)->sin_addr;
161
162		cp = 0;
163		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
164			cp = "default";
165		if (cp == 0 && !nflag) {
166			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
167				AF_INET);
168			if (hp) {
169				if ((cp = strchr(hp->h_name, '.')) &&
170				    !strcmp(cp + 1, domain))
171					*cp = 0;
172				cp = hp->h_name;
173			}
174		}
175		if (cp) {
176			strncpy(line, cp, sizeof(line) - 1);
177			line[sizeof(line) - 1] = '\0';
178		} else
179			(void) sprintf(line, "%s", inet_ntoa(in));
180		break;
181	    }
182
183#ifdef INET6
184	case AF_INET6:
185	{
186		struct sockaddr_in6 sin6; /* use static var for safety */
187		int niflags = 0;
188#ifdef NI_WITHSCOPEID
189		niflags = NI_WITHSCOPEID;
190#endif
191
192		memset(&sin6, 0, sizeof(sin6));
193		memcpy(&sin6, sa, sa->sa_len);
194		sin6.sin6_len = sizeof(struct sockaddr_in6);
195		sin6.sin6_family = AF_INET6;
196#ifdef __KAME__
197		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
198		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
199		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
200		    sin6.sin6_scope_id == 0) {
201			sin6.sin6_scope_id =
202			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
203			sin6.sin6_addr.s6_addr[2] = 0;
204			sin6.sin6_addr.s6_addr[3] = 0;
205		}
206#endif
207		if (nflag)
208			niflags |= NI_NUMERICHOST;
209		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
210		    line, sizeof(line), NULL, 0, niflags) != 0)
211			strncpy(line, "invalid", sizeof(line));
212
213		return(line);
214	}
215#endif
216
217	case AF_LINK:
218		return (link_ntoa((struct sockaddr_dl *)sa));
219
220	default:
221	    {	u_short *s = (u_short *)sa;
222		u_short *slim = s + ((sa->sa_len + 1) >> 1);
223		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
224		char *cpe = line + sizeof(line);
225
226		while (++s < slim && cp < cpe) /* start with sa->sa_data */
227			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
228				cp += n;
229			else
230				*cp = '\0';
231		break;
232	    }
233	}
234	return (line);
235}
236
237#ifndef SA_SIZE
238/*
239 * This macro returns the size of a struct sockaddr when passed
240 * through a routing socket. Basically we round up sa_len to
241 * a multiple of sizeof(long), with a minimum of sizeof(long).
242 * The check for a NULL pointer is just a convenience, probably never used.
243 * The case sa_len == 0 should only apply to empty structures.
244 */
245#define SA_SIZE(sa)						\
246    (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
247	sizeof(long)		:				\
248	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
249#endif
250
251static void
252pmsg_addrs(char *cp, int addrs)
253{
254	struct sockaddr *sa;
255	int i;
256
257	if (addrs == 0) {
258		(void) putchar('\n');
259		return;
260	}
261	printf("\nsockaddrs: ");
262	bprintf(stdout, addrs, addrnames);
263	putchar('\n');
264	for (i = 1; i; i <<= 1)
265		if (i & addrs) {
266			sa = (struct sockaddr *)cp;
267			printf(" %s", routename(sa));
268			cp += SA_SIZE(sa);
269		}
270	putchar('\n');
271	fflush(stdout);
272}
273
274static const char *
275ether_sprintf(const uint8_t mac[6])
276{
277	static char buf[32];
278
279	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
280		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
281	return buf;
282}
283
284static void
285print_rtmsg(struct rt_msghdr *rtm, int msglen)
286{
287	struct if_msghdr *ifm;
288	struct if_announcemsghdr *ifan;
289	char *state;
290	time_t now = time(NULL);
291	char *cnow = ctime(&now);
292
293	if (rtm->rtm_version != RTM_VERSION) {
294		(void) printf("routing message version %d not understood\n",
295		    rtm->rtm_version);
296		return;
297	}
298	switch (rtm->rtm_type) {
299	case RTM_IFINFO:
300		ifm = (struct if_msghdr *)rtm;
301		printf("%.19s RTM_IFINFO: if# %d, ",
302			cnow, ifm->ifm_index);
303		switch (ifm->ifm_data.ifi_link_state) {
304		case LINK_STATE_DOWN:
305			state = "down";
306			break;
307		case LINK_STATE_UP:
308			state = "up";
309			break;
310		default:
311			state = "unknown";
312			break;
313		}
314		printf("link: %s, flags:", state);
315		bprintf(stdout, ifm->ifm_flags, ifnetflags);
316		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
317		break;
318	case RTM_IFANNOUNCE:
319		ifan = (struct if_announcemsghdr *)rtm;
320		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
321			cnow, ifan->ifan_index);
322		switch (ifan->ifan_what) {
323		case IFAN_ARRIVAL:
324			printf("arrival");
325			break;
326		case IFAN_DEPARTURE:
327			printf("departure");
328			break;
329		default:
330			printf("#%d", ifan->ifan_what);
331			break;
332		}
333		printf("\n");
334		break;
335	case RTM_IEEE80211:
336#define	V(type)	((struct type *)(&ifan[1]))
337		ifan = (struct if_announcemsghdr *)rtm;
338		printf("%.19s RTM_IEEE80211: ", cnow);
339		switch (ifan->ifan_what) {
340		case RTM_IEEE80211_ASSOC:
341			printf("associate with %s",
342			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
343			break;
344		case RTM_IEEE80211_REASSOC:
345			printf("reassociate with %s",
346			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
347			break;
348		case RTM_IEEE80211_DISASSOC:
349			printf("disassociate");
350			break;
351		case RTM_IEEE80211_JOIN:
352		case RTM_IEEE80211_REJOIN:
353			printf("%s station %sjoin",
354			    ether_sprintf(V(ieee80211_join_event)->iev_addr),
355			    ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : ""
356			);
357			break;
358		case RTM_IEEE80211_LEAVE:
359			printf("%s station leave",
360			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
361			break;
362		case RTM_IEEE80211_SCAN:
363			printf("scan complete");
364			break;
365		case RTM_IEEE80211_REPLAY:
366			printf("replay failure: src %s "
367			    , ether_sprintf(V(ieee80211_replay_event)->iev_src)
368			);
369			printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu"
370			    , ether_sprintf(V(ieee80211_replay_event)->iev_dst)
371			    , V(ieee80211_replay_event)->iev_cipher
372			    , V(ieee80211_replay_event)->iev_keyix
373			    , V(ieee80211_replay_event)->iev_keyrsc
374			    , V(ieee80211_replay_event)->iev_rsc
375			);
376			break;
377		case RTM_IEEE80211_MICHAEL:
378			printf("michael failure: src %s "
379			    , ether_sprintf(V(ieee80211_michael_event)->iev_src)
380			);
381			printf("dst %s cipher %u keyix %u"
382			    , ether_sprintf(V(ieee80211_michael_event)->iev_dst)
383			    , V(ieee80211_michael_event)->iev_cipher
384			    , V(ieee80211_michael_event)->iev_keyix
385			);
386			break;
387		default:
388			printf("if# %d, what: #%d",
389				ifan->ifan_index, ifan->ifan_what);
390			break;
391		}
392		printf("\n");
393		break;
394#undef V
395	}
396}
397