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