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