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