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