1 /*-
2  * Copyright (c) 2002, 2003 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  * 3. Neither the names of the above-listed copyright holders nor the names
16  *    of any contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * Alternatively, this software may be distributed under the terms of the
20  * GNU General Public License ("GPL") version 2 as published by the Free
21  * Software Foundation.
22  *
23  * NO WARRANTY
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
27  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
29  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
32  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34  * THE POSSIBILITY OF SUCH DAMAGES.
35  *
36  * $FreeBSD$
37  */
38 
39 /*
40  * Monitor 802.11 events using a routing socket.
41  * Code liberaly swiped from route(8).
42  */
43 #include <sys/param.h>
44 #include <sys/file.h>
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
47 #include <sys/sysctl.h>
48 #include <sys/types.h>
49 
50 #include <net/if.h>
51 #include <net/route.h>
52 #include <net/if_dl.h>
53 #include <netinet/in.h>
54 #include <netinet/if_ether.h>
55 #include <netatalk/at.h>
56 #ifdef __NetBSD__
57 #include <net80211/ieee80211_netbsd.h>
58 #elif __FreeBSD__
59 #include <net80211/ieee80211_freebsd.h>
60 #else
61 #error	"No support for your operating system!"
62 #endif
63 #include <arpa/inet.h>
64 #include <netdb.h>
65 
66 #include <ctype.h>
67 #include <err.h>
68 #include <errno.h>
69 #include <paths.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <sysexits.h>
74 #include <unistd.h>
75 #include <ifaddrs.h>
76 
77 static	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
78 
79 int	nflag = 0;
80 
81 int
82 main(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 
97 static void
98 bprintf(fp, b, s)
99 	FILE *fp;
100 	int b;
101 	u_char *s;
102 {
103 	int i;
104 	int gotsome = 0;
105 
106 	if (b == 0)
107 		return;
108 	while ((i = *s++) != 0) {
109 		if (b & (1 << (i-1))) {
110 			if (gotsome == 0)
111 				i = '<';
112 			else
113 				i = ',';
114 			(void) putc(i, fp);
115 			gotsome = 1;
116 			for (; (i = *s) > 32; s++)
117 				(void) putc(i, fp);
118 		} else
119 			while (*s > 32)
120 				s++;
121 	}
122 	if (gotsome)
123 		putc('>', fp);
124 }
125 
126 char metricnames[] =
127 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
128 "\1mtu";
129 char routeflags[] =
130 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
131 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
132 "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
133 "\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
134 char ifnetflags[] =
135 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
136 "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
137 "\017LINK2\020MULTICAST";
138 char addrnames[] =
139 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
140 
141 const char *
142 routename(sa)
143 	struct sockaddr *sa;
144 {
145 	char *cp;
146 	static char line[MAXHOSTNAMELEN + 1];
147 	struct hostent *hp;
148 	static char domain[MAXHOSTNAMELEN + 1];
149 	static int first = 1, n;
150 
151 	if (first) {
152 		first = 0;
153 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
154 		    (cp = strchr(domain, '.'))) {
155 			domain[MAXHOSTNAMELEN] = '\0';
156 			(void) strcpy(domain, cp + 1);
157 		} else
158 			domain[0] = 0;
159 	}
160 
161 	if (sa->sa_len == 0)
162 		strcpy(line, "default");
163 	else switch (sa->sa_family) {
164 
165 	case AF_INET:
166 	    {	struct in_addr in;
167 		in = ((struct sockaddr_in *)sa)->sin_addr;
168 
169 		cp = 0;
170 		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
171 			cp = "default";
172 		if (cp == 0 && !nflag) {
173 			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
174 				AF_INET);
175 			if (hp) {
176 				if ((cp = strchr(hp->h_name, '.')) &&
177 				    !strcmp(cp + 1, domain))
178 					*cp = 0;
179 				cp = hp->h_name;
180 			}
181 		}
182 		if (cp) {
183 			strncpy(line, cp, sizeof(line) - 1);
184 			line[sizeof(line) - 1] = '\0';
185 		} else
186 			(void) sprintf(line, "%s", inet_ntoa(in));
187 		break;
188 	    }
189 
190 #ifdef INET6
191 	case AF_INET6:
192 	{
193 		struct sockaddr_in6 sin6; /* use static var for safety */
194 		int niflags = 0;
195 #ifdef NI_WITHSCOPEID
196 		niflags = NI_WITHSCOPEID;
197 #endif
198 
199 		memset(&sin6, 0, sizeof(sin6));
200 		memcpy(&sin6, sa, sa->sa_len);
201 		sin6.sin6_len = sizeof(struct sockaddr_in6);
202 		sin6.sin6_family = AF_INET6;
203 #ifdef __KAME__
204 		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
205 		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
206 		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
207 		    sin6.sin6_scope_id == 0) {
208 			sin6.sin6_scope_id =
209 			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
210 			sin6.sin6_addr.s6_addr[2] = 0;
211 			sin6.sin6_addr.s6_addr[3] = 0;
212 		}
213 #endif
214 		if (nflag)
215 			niflags |= NI_NUMERICHOST;
216 		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
217 		    line, sizeof(line), NULL, 0, niflags) != 0)
218 			strncpy(line, "invalid", sizeof(line));
219 
220 		return(line);
221 	}
222 #endif
223 
224 	case AF_LINK:
225 		return (link_ntoa((struct sockaddr_dl *)sa));
226 
227 	default:
228 	    {	u_short *s = (u_short *)sa;
229 		u_short *slim = s + ((sa->sa_len + 1) >> 1);
230 		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
231 		char *cpe = line + sizeof(line);
232 
233 		while (++s < slim && cp < cpe) /* start with sa->sa_data */
234 			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
235 				cp += n;
236 			else
237 				*cp = '\0';
238 		break;
239 	    }
240 	}
241 	return (line);
242 }
243 
244 #ifndef SA_SIZE
245 /*
246  * This macro returns the size of a struct sockaddr when passed
247  * through a routing socket. Basically we round up sa_len to
248  * a multiple of sizeof(long), with a minimum of sizeof(long).
249  * The check for a NULL pointer is just a convenience, probably never used.
250  * The case sa_len == 0 should only apply to empty structures.
251  */
252 #define SA_SIZE(sa)						\
253     (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
254 	sizeof(long)		:				\
255 	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
256 #endif
257 
258 static void
259 pmsg_addrs(char *cp, int addrs)
260 {
261 	struct sockaddr *sa;
262 	int i;
263 
264 	if (addrs == 0) {
265 		(void) putchar('\n');
266 		return;
267 	}
268 	printf("\nsockaddrs: ");
269 	bprintf(stdout, addrs, addrnames);
270 	putchar('\n');
271 	for (i = 1; i; i <<= 1)
272 		if (i & addrs) {
273 			sa = (struct sockaddr *)cp;
274 			printf(" %s", routename(sa));
275 			cp += SA_SIZE(sa);
276 		}
277 	putchar('\n');
278 	fflush(stdout);
279 }
280 
281 static const char *
282 ether_sprintf(const uint8_t mac[6])
283 {
284 	static char buf[32];
285 
286 	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
287 		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
288 	return buf;
289 }
290 
291 static void
292 print_rtmsg(struct rt_msghdr *rtm, int msglen)
293 {
294 	struct if_msghdr *ifm;
295 	struct if_announcemsghdr *ifan;
296 	char *state;
297 	time_t now = time(NULL);
298 	char *cnow = ctime(&now);
299 
300 	if (rtm->rtm_version != RTM_VERSION) {
301 		(void) printf("routing message version %d not understood\n",
302 		    rtm->rtm_version);
303 		return;
304 	}
305 	switch (rtm->rtm_type) {
306 	case RTM_IFINFO:
307 		ifm = (struct if_msghdr *)rtm;
308 		printf("%.19s RTM_IFINFO: if# %d, ",
309 			cnow, ifm->ifm_index);
310 		switch (ifm->ifm_data.ifi_link_state) {
311 		case LINK_STATE_DOWN:
312 			state = "down";
313 			break;
314 		case LINK_STATE_UP:
315 			state = "up";
316 			break;
317 		default:
318 			state = "unknown";
319 			break;
320 		}
321 		printf("link: %s, flags:", state);
322 		bprintf(stdout, ifm->ifm_flags, ifnetflags);
323 		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
324 		break;
325 	case RTM_IFANNOUNCE:
326 		ifan = (struct if_announcemsghdr *)rtm;
327 		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
328 			cnow, ifan->ifan_index);
329 		switch (ifan->ifan_what) {
330 		case IFAN_ARRIVAL:
331 			printf("arrival");
332 			break;
333 		case IFAN_DEPARTURE:
334 			printf("departure");
335 			break;
336 		default:
337 			printf("#%d", ifan->ifan_what);
338 			break;
339 		}
340 		printf("\n");
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: ", cnow);
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 		default:
395 			printf("if# %d, what: #%d",
396 				ifan->ifan_index, ifan->ifan_what);
397 			break;
398 		}
399 		printf("\n");
400 		break;
401 #undef V
402 	}
403 }
404