1153317Ssam/*-
2174244Ssam * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
3153317Ssam * All rights reserved.
4153317Ssam *
5153317Ssam * Redistribution and use in source and binary forms, with or without
6153317Ssam * modification, are permitted provided that the following conditions
7153317Ssam * are met:
8153317Ssam * 1. Redistributions of source code must retain the above copyright
9153317Ssam *    notice, this list of conditions and the following disclaimer,
10153317Ssam *    without modification.
11153317Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12153317Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13153317Ssam *    redistribution must be conditioned upon including a substantially
14153317Ssam *    similar Disclaimer requirement for further binary redistribution.
15153317Ssam *
16153317Ssam * NO WARRANTY
17153317Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18153317Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19153317Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20153317Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21153317Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22153317Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23153317Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24153317Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25153317Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26153317Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27153317Ssam * THE POSSIBILITY OF SUCH DAMAGES.
28153317Ssam *
29153317Ssam * $FreeBSD$
30153317Ssam */
31153317Ssam
32153317Ssam/*
33153317Ssam * Monitor 802.11 events using a routing socket.
34153317Ssam * Code liberaly swiped from route(8).
35153317Ssam */
36153317Ssam#include <sys/param.h>
37153317Ssam#include <sys/file.h>
38153317Ssam#include <sys/socket.h>
39153317Ssam#include <sys/ioctl.h>
40153317Ssam#include <sys/sysctl.h>
41153317Ssam#include <sys/types.h>
42153317Ssam
43153317Ssam#include <net/if.h>
44153317Ssam#include <net/route.h>
45153317Ssam#include <net/if_dl.h>
46153317Ssam#include <netinet/in.h>
47153317Ssam#include <netinet/if_ether.h>
48153317Ssam#include <netatalk/at.h>
49160053Ssam#ifdef __NetBSD__
50160053Ssam#include <net80211/ieee80211_netbsd.h>
51160053Ssam#elif __FreeBSD__
52153317Ssam#include <net80211/ieee80211_freebsd.h>
53160053Ssam#else
54160053Ssam#error	"No support for your operating system!"
55160053Ssam#endif
56153317Ssam#include <arpa/inet.h>
57153317Ssam#include <netdb.h>
58153317Ssam
59153317Ssam#include <ctype.h>
60153317Ssam#include <err.h>
61153317Ssam#include <errno.h>
62153317Ssam#include <paths.h>
63153317Ssam#include <stdio.h>
64153317Ssam#include <stdlib.h>
65153317Ssam#include <string.h>
66153317Ssam#include <sysexits.h>
67153317Ssam#include <unistd.h>
68153317Ssam#include <ifaddrs.h>
69153317Ssam
70178698Ssam/* XXX */
71178698Ssamenum ieee80211_notify_cac_event {
72178698Ssam	IEEE80211_NOTIFY_CAC_START  = 0, /* CAC timer started */
73178698Ssam	IEEE80211_NOTIFY_CAC_STOP   = 1, /* CAC intentionally stopped */
74178698Ssam	IEEE80211_NOTIFY_CAC_RADAR  = 2, /* CAC stopped due to radar detectio */
75178698Ssam	IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
76178698Ssam};
77178698Ssam
78153317Ssamstatic	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
79153317Ssam
80153317Ssamint	nflag = 0;
81153317Ssam
82153317Ssamint
83153317Ssammain(int argc, char *argv[])
84153317Ssam{
85153317Ssam	int n, s;
86153317Ssam	char msg[2048];
87153317Ssam
88153317Ssam	s = socket(PF_ROUTE, SOCK_RAW, 0);
89153317Ssam	if (s < 0)
90153317Ssam		err(EX_OSERR, "socket");
91153317Ssam	for(;;) {
92153317Ssam		n = read(s, msg, 2048);
93153317Ssam		print_rtmsg((struct rt_msghdr *)msg, n);
94153317Ssam	}
95153317Ssam	return 0;
96153317Ssam}
97153317Ssam
98153317Ssamstatic void
99177504Ssambprintf(FILE *fp, int b, char *s)
100153317Ssam{
101153317Ssam	int i;
102153317Ssam	int gotsome = 0;
103153317Ssam
104153317Ssam	if (b == 0)
105153317Ssam		return;
106153317Ssam	while ((i = *s++) != 0) {
107153317Ssam		if (b & (1 << (i-1))) {
108153317Ssam			if (gotsome == 0)
109153317Ssam				i = '<';
110153317Ssam			else
111153317Ssam				i = ',';
112153317Ssam			(void) putc(i, fp);
113153317Ssam			gotsome = 1;
114153317Ssam			for (; (i = *s) > 32; s++)
115153317Ssam				(void) putc(i, fp);
116153317Ssam		} else
117153317Ssam			while (*s > 32)
118153317Ssam				s++;
119153317Ssam	}
120153317Ssam	if (gotsome)
121153317Ssam		putc('>', fp);
122153317Ssam}
123153317Ssam
124153317Ssamchar metricnames[] =
125153317Ssam"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
126153317Ssam"\1mtu";
127153317Ssamchar routeflags[] =
128153317Ssam"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
129153317Ssam"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
130153317Ssam"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
131153317Ssam"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
132153317Ssamchar ifnetflags[] =
133153317Ssam"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
134153317Ssam"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
135153317Ssam"\017LINK2\020MULTICAST";
136153317Ssamchar addrnames[] =
137153317Ssam"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
138153317Ssam
139177504Ssamstatic const char *
140177504Ssamroutename(struct sockaddr *sa)
141153317Ssam{
142153317Ssam	char *cp;
143153317Ssam	static char line[MAXHOSTNAMELEN + 1];
144153317Ssam	struct hostent *hp;
145153317Ssam	static char domain[MAXHOSTNAMELEN + 1];
146153317Ssam	static int first = 1, n;
147153317Ssam
148153317Ssam	if (first) {
149153317Ssam		first = 0;
150153317Ssam		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
151153317Ssam		    (cp = strchr(domain, '.'))) {
152153317Ssam			domain[MAXHOSTNAMELEN] = '\0';
153153317Ssam			(void) strcpy(domain, cp + 1);
154153317Ssam		} else
155153317Ssam			domain[0] = 0;
156153317Ssam	}
157153317Ssam
158153317Ssam	if (sa->sa_len == 0)
159153317Ssam		strcpy(line, "default");
160153317Ssam	else switch (sa->sa_family) {
161153317Ssam
162153317Ssam	case AF_INET:
163153317Ssam	    {	struct in_addr in;
164153317Ssam		in = ((struct sockaddr_in *)sa)->sin_addr;
165153317Ssam
166153317Ssam		cp = 0;
167153317Ssam		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
168153317Ssam			cp = "default";
169153317Ssam		if (cp == 0 && !nflag) {
170153317Ssam			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
171153317Ssam				AF_INET);
172153317Ssam			if (hp) {
173153317Ssam				if ((cp = strchr(hp->h_name, '.')) &&
174153317Ssam				    !strcmp(cp + 1, domain))
175153317Ssam					*cp = 0;
176153317Ssam				cp = hp->h_name;
177153317Ssam			}
178153317Ssam		}
179153317Ssam		if (cp) {
180153317Ssam			strncpy(line, cp, sizeof(line) - 1);
181153317Ssam			line[sizeof(line) - 1] = '\0';
182153317Ssam		} else
183153317Ssam			(void) sprintf(line, "%s", inet_ntoa(in));
184153317Ssam		break;
185153317Ssam	    }
186153317Ssam
187153317Ssam#ifdef INET6
188153317Ssam	case AF_INET6:
189153317Ssam	{
190153317Ssam		struct sockaddr_in6 sin6; /* use static var for safety */
191153317Ssam		int niflags = 0;
192153317Ssam#ifdef NI_WITHSCOPEID
193153317Ssam		niflags = NI_WITHSCOPEID;
194153317Ssam#endif
195153317Ssam
196153317Ssam		memset(&sin6, 0, sizeof(sin6));
197153317Ssam		memcpy(&sin6, sa, sa->sa_len);
198153317Ssam		sin6.sin6_len = sizeof(struct sockaddr_in6);
199153317Ssam		sin6.sin6_family = AF_INET6;
200153317Ssam#ifdef __KAME__
201153317Ssam		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
202153317Ssam		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
203153317Ssam		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
204153317Ssam		    sin6.sin6_scope_id == 0) {
205153317Ssam			sin6.sin6_scope_id =
206153317Ssam			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
207153317Ssam			sin6.sin6_addr.s6_addr[2] = 0;
208153317Ssam			sin6.sin6_addr.s6_addr[3] = 0;
209153317Ssam		}
210153317Ssam#endif
211153317Ssam		if (nflag)
212153317Ssam			niflags |= NI_NUMERICHOST;
213153317Ssam		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
214153317Ssam		    line, sizeof(line), NULL, 0, niflags) != 0)
215153317Ssam			strncpy(line, "invalid", sizeof(line));
216153317Ssam
217153317Ssam		return(line);
218153317Ssam	}
219153317Ssam#endif
220153317Ssam
221153317Ssam	case AF_LINK:
222153317Ssam		return (link_ntoa((struct sockaddr_dl *)sa));
223153317Ssam
224153317Ssam	default:
225153317Ssam	    {	u_short *s = (u_short *)sa;
226153317Ssam		u_short *slim = s + ((sa->sa_len + 1) >> 1);
227153317Ssam		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
228153317Ssam		char *cpe = line + sizeof(line);
229153317Ssam
230153317Ssam		while (++s < slim && cp < cpe) /* start with sa->sa_data */
231153317Ssam			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
232153317Ssam				cp += n;
233153317Ssam			else
234153317Ssam				*cp = '\0';
235153317Ssam		break;
236153317Ssam	    }
237153317Ssam	}
238153317Ssam	return (line);
239153317Ssam}
240153317Ssam
241160053Ssam#ifndef SA_SIZE
242160053Ssam/*
243160053Ssam * This macro returns the size of a struct sockaddr when passed
244160053Ssam * through a routing socket. Basically we round up sa_len to
245160053Ssam * a multiple of sizeof(long), with a minimum of sizeof(long).
246160053Ssam * The check for a NULL pointer is just a convenience, probably never used.
247160053Ssam * The case sa_len == 0 should only apply to empty structures.
248160053Ssam */
249160053Ssam#define SA_SIZE(sa)						\
250160053Ssam    (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
251160053Ssam	sizeof(long)		:				\
252160053Ssam	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
253160053Ssam#endif
254153317Ssam
255153317Ssamstatic void
256153317Ssampmsg_addrs(char *cp, int addrs)
257153317Ssam{
258153317Ssam	struct sockaddr *sa;
259153317Ssam	int i;
260153317Ssam
261153317Ssam	if (addrs == 0) {
262153317Ssam		(void) putchar('\n');
263153317Ssam		return;
264153317Ssam	}
265153317Ssam	printf("\nsockaddrs: ");
266153317Ssam	bprintf(stdout, addrs, addrnames);
267153317Ssam	putchar('\n');
268153317Ssam	for (i = 1; i; i <<= 1)
269153317Ssam		if (i & addrs) {
270153317Ssam			sa = (struct sockaddr *)cp;
271153317Ssam			printf(" %s", routename(sa));
272153317Ssam			cp += SA_SIZE(sa);
273153317Ssam		}
274153317Ssam	putchar('\n');
275153317Ssam}
276153317Ssam
277153317Ssamstatic const char *
278153317Ssamether_sprintf(const uint8_t mac[6])
279153317Ssam{
280153317Ssam	static char buf[32];
281153317Ssam
282153317Ssam	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
283153317Ssam		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
284153317Ssam	return buf;
285153317Ssam}
286153317Ssam
287153317Ssamstatic void
288153317Ssamprint_rtmsg(struct rt_msghdr *rtm, int msglen)
289153317Ssam{
290153317Ssam	struct if_msghdr *ifm;
291153317Ssam	struct if_announcemsghdr *ifan;
292153317Ssam	time_t now = time(NULL);
293153317Ssam	char *cnow = ctime(&now);
294153317Ssam
295153317Ssam	if (rtm->rtm_version != RTM_VERSION) {
296153317Ssam		(void) printf("routing message version %d not understood\n",
297153317Ssam		    rtm->rtm_version);
298153317Ssam		return;
299153317Ssam	}
300153317Ssam	switch (rtm->rtm_type) {
301153317Ssam	case RTM_IFINFO:
302153317Ssam		ifm = (struct if_msghdr *)rtm;
303153317Ssam		printf("%.19s RTM_IFINFO: if# %d, ",
304153317Ssam			cnow, ifm->ifm_index);
305153317Ssam		switch (ifm->ifm_data.ifi_link_state) {
306153317Ssam		case LINK_STATE_DOWN:
307177504Ssam			printf("link: down, flags:");
308153317Ssam			break;
309153317Ssam		case LINK_STATE_UP:
310177504Ssam			printf("link: up, flags:");
311153317Ssam			break;
312153317Ssam		default:
313177504Ssam			printf("link: unknown<%d>, flags:",
314177504Ssam			    ifm->ifm_data.ifi_link_state);
315153317Ssam			break;
316153317Ssam		}
317153317Ssam		bprintf(stdout, ifm->ifm_flags, ifnetflags);
318153317Ssam		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
319179122Sthompsa		fflush(stdout);
320153317Ssam		break;
321153317Ssam	case RTM_IFANNOUNCE:
322153317Ssam		ifan = (struct if_announcemsghdr *)rtm;
323153317Ssam		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
324153317Ssam			cnow, ifan->ifan_index);
325153317Ssam		switch (ifan->ifan_what) {
326153317Ssam		case IFAN_ARRIVAL:
327153317Ssam			printf("arrival");
328153317Ssam			break;
329153317Ssam		case IFAN_DEPARTURE:
330153317Ssam			printf("departure");
331153317Ssam			break;
332153317Ssam		default:
333153317Ssam			printf("#%d", ifan->ifan_what);
334153317Ssam			break;
335153317Ssam		}
336153317Ssam		printf("\n");
337179122Sthompsa		fflush(stdout);
338153317Ssam		break;
339153317Ssam	case RTM_IEEE80211:
340153317Ssam#define	V(type)	((struct type *)(&ifan[1]))
341153317Ssam		ifan = (struct if_announcemsghdr *)rtm;
342177504Ssam		printf("%.19s RTM_IEEE80211: if# %d, ", cnow, ifan->ifan_index);
343153317Ssam		switch (ifan->ifan_what) {
344153317Ssam		case RTM_IEEE80211_ASSOC:
345153317Ssam			printf("associate with %s",
346153317Ssam			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
347153317Ssam			break;
348153317Ssam		case RTM_IEEE80211_REASSOC:
349153317Ssam			printf("reassociate with %s",
350153317Ssam			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
351153317Ssam			break;
352153317Ssam		case RTM_IEEE80211_DISASSOC:
353153317Ssam			printf("disassociate");
354153317Ssam			break;
355153317Ssam		case RTM_IEEE80211_JOIN:
356153317Ssam		case RTM_IEEE80211_REJOIN:
357153317Ssam			printf("%s station %sjoin",
358160053Ssam			    ether_sprintf(V(ieee80211_join_event)->iev_addr),
359160053Ssam			    ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : ""
360160053Ssam			);
361153317Ssam			break;
362153317Ssam		case RTM_IEEE80211_LEAVE:
363153317Ssam			printf("%s station leave",
364153317Ssam			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
365153317Ssam			break;
366153317Ssam		case RTM_IEEE80211_SCAN:
367153317Ssam			printf("scan complete");
368153317Ssam			break;
369153317Ssam		case RTM_IEEE80211_REPLAY:
370153317Ssam			printf("replay failure: src %s "
371153317Ssam			    , ether_sprintf(V(ieee80211_replay_event)->iev_src)
372153317Ssam			);
373153317Ssam			printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu"
374153317Ssam			    , ether_sprintf(V(ieee80211_replay_event)->iev_dst)
375153317Ssam			    , V(ieee80211_replay_event)->iev_cipher
376153317Ssam			    , V(ieee80211_replay_event)->iev_keyix
377153317Ssam			    , V(ieee80211_replay_event)->iev_keyrsc
378153317Ssam			    , V(ieee80211_replay_event)->iev_rsc
379153317Ssam			);
380153317Ssam			break;
381153317Ssam		case RTM_IEEE80211_MICHAEL:
382153317Ssam			printf("michael failure: src %s "
383153317Ssam			    , ether_sprintf(V(ieee80211_michael_event)->iev_src)
384153317Ssam			);
385153317Ssam			printf("dst %s cipher %u keyix %u"
386153317Ssam			    , ether_sprintf(V(ieee80211_michael_event)->iev_dst)
387153317Ssam			    , V(ieee80211_michael_event)->iev_cipher
388153317Ssam			    , V(ieee80211_michael_event)->iev_keyix
389153317Ssam			);
390153317Ssam			break;
391178698Ssam		case RTM_IEEE80211_WDS:
392178698Ssam			printf("%s wds discovery",
393178698Ssam			    ether_sprintf(V(ieee80211_wds_event)->iev_addr));
394178698Ssam			break;
395178698Ssam		case RTM_IEEE80211_CSA:
396178698Ssam			printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d"
397178698Ssam			    , V(ieee80211_csa_event)->iev_ieee
398178698Ssam			    , V(ieee80211_csa_event)->iev_freq
399178698Ssam			    , V(ieee80211_csa_event)->iev_flags
400178698Ssam			    , V(ieee80211_csa_event)->iev_mode
401178698Ssam			    , V(ieee80211_csa_event)->iev_count
402178698Ssam			);
403178698Ssam			break;
404178698Ssam		case RTM_IEEE80211_CAC:
405178698Ssam			printf("channel availability check "
406178698Ssam			    "(channel %u, %u MHz flags 0x%x) "
407178698Ssam			    , V(ieee80211_cac_event)->iev_ieee
408178698Ssam			    , V(ieee80211_cac_event)->iev_freq
409178698Ssam			    , V(ieee80211_cac_event)->iev_flags
410178698Ssam			);
411178698Ssam			switch (V(ieee80211_cac_event)->iev_type) {
412178698Ssam			case IEEE80211_NOTIFY_CAC_START:
413178698Ssam				printf("start timer");
414178698Ssam				break;
415178698Ssam			case IEEE80211_NOTIFY_CAC_STOP:
416178698Ssam				printf("stop timer");
417178698Ssam				break;
418178698Ssam			case IEEE80211_NOTIFY_CAC_EXPIRE:
419178698Ssam				printf("timer expired");
420178698Ssam				break;
421178698Ssam			case IEEE80211_NOTIFY_CAC_RADAR:
422178698Ssam				printf("radar detected");
423178698Ssam				break;
424178698Ssam			default:
425178698Ssam				printf("unknown type %d",
426178698Ssam				   V(ieee80211_cac_event)->iev_type);
427178698Ssam				break;
428178698Ssam			}
429178698Ssam			break;
430178698Ssam		case RTM_IEEE80211_DEAUTH:
431178698Ssam			printf("%s wds deauth",
432178698Ssam			    ether_sprintf(V(ieee80211_deauth_event)->iev_addr));
433178698Ssam			break;
434178698Ssam		case RTM_IEEE80211_AUTH:
435178698Ssam			printf("%s node authenticate",
436178698Ssam			    ether_sprintf(V(ieee80211_auth_event)->iev_addr));
437178698Ssam			break;
438178698Ssam		case RTM_IEEE80211_COUNTRY:
439178698Ssam			printf("%s adopt country code '%c%c'",
440178698Ssam			    ether_sprintf(V(ieee80211_country_event)->iev_addr),
441178698Ssam			    V(ieee80211_country_event)->iev_cc[0],
442178698Ssam			    V(ieee80211_country_event)->iev_cc[1]);
443178698Ssam			break;
444178698Ssam		case RTM_IEEE80211_RADIO:
445178698Ssam			printf("radio %s",
446178698Ssam			    V(ieee80211_radio_event)->iev_state ? "ON" : "OFF");
447178698Ssam			break;
448153317Ssam		default:
449177504Ssam			printf("what: #%d", ifan->ifan_what);
450153317Ssam			break;
451153317Ssam		}
452153317Ssam		printf("\n");
453179122Sthompsa		fflush(stdout);
454153317Ssam		break;
455153317Ssam#undef V
456153317Ssam	}
457153317Ssam}
458