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