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