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