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