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