wlanwatch.c revision 153317
1/*-
2 * Copyright (c) 2002, 2003 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 * 3. Neither the names of the above-listed copyright holders nor the names
16 *    of any contributors may be used to endorse or promote products derived
17 *    from this software without specific prior written permission.
18 *
19 * Alternatively, this software may be distributed under the terms of the
20 * GNU General Public License ("GPL") version 2 as published by the Free
21 * Software Foundation.
22 *
23 * NO WARRANTY
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
29 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
32 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34 * THE POSSIBILITY OF SUCH DAMAGES.
35 *
36 * $FreeBSD: head/tools/tools/net80211/wlanwatch/wlanwatch.c 153317 2005-12-11 23:13:54Z sam $
37 */
38
39/*
40 * Monitor 802.11 events using a routing socket.
41 * Code liberaly swiped from route(8).
42 */
43#include <sys/param.h>
44#include <sys/file.h>
45#include <sys/socket.h>
46#include <sys/ioctl.h>
47#include <sys/sysctl.h>
48#include <sys/types.h>
49
50#include <net/if.h>
51#include <net/route.h>
52#include <net/if_dl.h>
53#include <netinet/in.h>
54#include <netinet/if_ether.h>
55#include <netatalk/at.h>
56#include <net80211/ieee80211_freebsd.h>
57#include <arpa/inet.h>
58#include <netdb.h>
59
60#include <ctype.h>
61#include <err.h>
62#include <errno.h>
63#include <paths.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <sysexits.h>
68#include <unistd.h>
69#include <ifaddrs.h>
70
71static	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
72
73int	nflag = 0;
74
75int
76main(int argc, char *argv[])
77{
78	int n, s;
79	char msg[2048];
80
81	s = socket(PF_ROUTE, SOCK_RAW, 0);
82	if (s < 0)
83		err(EX_OSERR, "socket");
84	for(;;) {
85		n = read(s, msg, 2048);
86		print_rtmsg((struct rt_msghdr *)msg, n);
87	}
88	return 0;
89}
90
91static void
92bprintf(fp, b, s)
93	FILE *fp;
94	int b;
95	u_char *s;
96{
97	int i;
98	int gotsome = 0;
99
100	if (b == 0)
101		return;
102	while ((i = *s++) != 0) {
103		if (b & (1 << (i-1))) {
104			if (gotsome == 0)
105				i = '<';
106			else
107				i = ',';
108			(void) putc(i, fp);
109			gotsome = 1;
110			for (; (i = *s) > 32; s++)
111				(void) putc(i, fp);
112		} else
113			while (*s > 32)
114				s++;
115	}
116	if (gotsome)
117		putc('>', fp);
118}
119
120char metricnames[] =
121"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
122"\1mtu";
123char routeflags[] =
124"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
125"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
126"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
127"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
128char ifnetflags[] =
129"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
130"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
131"\017LINK2\020MULTICAST";
132char addrnames[] =
133"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
134
135const char *
136routename(sa)
137	struct sockaddr *sa;
138{
139	char *cp;
140	static char line[MAXHOSTNAMELEN + 1];
141	struct hostent *hp;
142	static char domain[MAXHOSTNAMELEN + 1];
143	static int first = 1, n;
144
145	if (first) {
146		first = 0;
147		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
148		    (cp = strchr(domain, '.'))) {
149			domain[MAXHOSTNAMELEN] = '\0';
150			(void) strcpy(domain, cp + 1);
151		} else
152			domain[0] = 0;
153	}
154
155	if (sa->sa_len == 0)
156		strcpy(line, "default");
157	else switch (sa->sa_family) {
158
159	case AF_INET:
160	    {	struct in_addr in;
161		in = ((struct sockaddr_in *)sa)->sin_addr;
162
163		cp = 0;
164		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
165			cp = "default";
166		if (cp == 0 && !nflag) {
167			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
168				AF_INET);
169			if (hp) {
170				if ((cp = strchr(hp->h_name, '.')) &&
171				    !strcmp(cp + 1, domain))
172					*cp = 0;
173				cp = hp->h_name;
174			}
175		}
176		if (cp) {
177			strncpy(line, cp, sizeof(line) - 1);
178			line[sizeof(line) - 1] = '\0';
179		} else
180			(void) sprintf(line, "%s", inet_ntoa(in));
181		break;
182	    }
183
184#ifdef INET6
185	case AF_INET6:
186	{
187		struct sockaddr_in6 sin6; /* use static var for safety */
188		int niflags = 0;
189#ifdef NI_WITHSCOPEID
190		niflags = NI_WITHSCOPEID;
191#endif
192
193		memset(&sin6, 0, sizeof(sin6));
194		memcpy(&sin6, sa, sa->sa_len);
195		sin6.sin6_len = sizeof(struct sockaddr_in6);
196		sin6.sin6_family = AF_INET6;
197#ifdef __KAME__
198		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
199		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
200		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
201		    sin6.sin6_scope_id == 0) {
202			sin6.sin6_scope_id =
203			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
204			sin6.sin6_addr.s6_addr[2] = 0;
205			sin6.sin6_addr.s6_addr[3] = 0;
206		}
207#endif
208		if (nflag)
209			niflags |= NI_NUMERICHOST;
210		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
211		    line, sizeof(line), NULL, 0, niflags) != 0)
212			strncpy(line, "invalid", sizeof(line));
213
214		return(line);
215	}
216#endif
217
218	case AF_LINK:
219		return (link_ntoa((struct sockaddr_dl *)sa));
220
221	default:
222	    {	u_short *s = (u_short *)sa;
223		u_short *slim = s + ((sa->sa_len + 1) >> 1);
224		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
225		char *cpe = line + sizeof(line);
226
227		while (++s < slim && cp < cpe) /* start with sa->sa_data */
228			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
229				cp += n;
230			else
231				*cp = '\0';
232		break;
233	    }
234	}
235	return (line);
236}
237
238
239static void
240pmsg_addrs(char *cp, int addrs)
241{
242	struct sockaddr *sa;
243	int i;
244
245	if (addrs == 0) {
246		(void) putchar('\n');
247		return;
248	}
249	printf("\nsockaddrs: ");
250	bprintf(stdout, addrs, addrnames);
251	putchar('\n');
252	for (i = 1; i; i <<= 1)
253		if (i & addrs) {
254			sa = (struct sockaddr *)cp;
255			printf(" %s", routename(sa));
256			cp += SA_SIZE(sa);
257		}
258	putchar('\n');
259	fflush(stdout);
260}
261
262static const char *
263ether_sprintf(const uint8_t mac[6])
264{
265	static char buf[32];
266
267	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
268		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
269	return buf;
270}
271
272static void
273print_rtmsg(struct rt_msghdr *rtm, int msglen)
274{
275	struct if_msghdr *ifm;
276	struct if_announcemsghdr *ifan;
277	char *state;
278	time_t now = time(NULL);
279	char *cnow = ctime(&now);
280
281	if (rtm->rtm_version != RTM_VERSION) {
282		(void) printf("routing message version %d not understood\n",
283		    rtm->rtm_version);
284		return;
285	}
286	switch (rtm->rtm_type) {
287	case RTM_IFINFO:
288		ifm = (struct if_msghdr *)rtm;
289		printf("%.19s RTM_IFINFO: if# %d, ",
290			cnow, ifm->ifm_index);
291		switch (ifm->ifm_data.ifi_link_state) {
292		case LINK_STATE_DOWN:
293			state = "down";
294			break;
295		case LINK_STATE_UP:
296			state = "up";
297			break;
298		default:
299			state = "unknown";
300			break;
301		}
302		printf("link: %s, flags:", state);
303		bprintf(stdout, ifm->ifm_flags, ifnetflags);
304		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
305		break;
306	case RTM_IFANNOUNCE:
307		ifan = (struct if_announcemsghdr *)rtm;
308		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
309			cnow, ifan->ifan_index);
310		switch (ifan->ifan_what) {
311		case IFAN_ARRIVAL:
312			printf("arrival");
313			break;
314		case IFAN_DEPARTURE:
315			printf("departure");
316			break;
317		default:
318			printf("#%d", ifan->ifan_what);
319			break;
320		}
321		printf("\n");
322		break;
323	case RTM_IEEE80211:
324#define	V(type)	((struct type *)(&ifan[1]))
325		ifan = (struct if_announcemsghdr *)rtm;
326		printf("%.19s RTM_IEEE80211: ", cnow);
327		switch (ifan->ifan_what) {
328		case RTM_IEEE80211_ASSOC:
329			printf("associate with %s",
330			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
331			break;
332		case RTM_IEEE80211_REASSOC:
333			printf("reassociate with %s",
334			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
335			break;
336		case RTM_IEEE80211_DISASSOC:
337			printf("disassociate");
338			break;
339		case RTM_IEEE80211_JOIN:
340		case RTM_IEEE80211_REJOIN:
341			printf("%s station %sjoin",
342			    ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "",
343			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
344			break;
345		case RTM_IEEE80211_LEAVE:
346			printf("%s station leave",
347			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
348			break;
349		case RTM_IEEE80211_SCAN:
350			printf("scan complete");
351			break;
352		case RTM_IEEE80211_REPLAY:
353			printf("replay failure: src %s "
354			    , ether_sprintf(V(ieee80211_replay_event)->iev_src)
355			);
356			printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu"
357			    , ether_sprintf(V(ieee80211_replay_event)->iev_dst)
358			    , V(ieee80211_replay_event)->iev_cipher
359			    , V(ieee80211_replay_event)->iev_keyix
360			    , V(ieee80211_replay_event)->iev_keyrsc
361			    , V(ieee80211_replay_event)->iev_rsc
362			);
363			break;
364		case RTM_IEEE80211_MICHAEL:
365			printf("michael failure: src %s "
366			    , ether_sprintf(V(ieee80211_michael_event)->iev_src)
367			);
368			printf("dst %s cipher %u keyix %u"
369			    , ether_sprintf(V(ieee80211_michael_event)->iev_dst)
370			    , V(ieee80211_michael_event)->iev_cipher
371			    , V(ieee80211_michael_event)->iev_keyix
372			);
373			break;
374		default:
375			printf("if# %d, what: #%d",
376				ifan->ifan_index, ifan->ifan_what);
377			break;
378		}
379		printf("\n");
380		break;
381#undef V
382	}
383}
384