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 197 memset(&sin6, 0, sizeof(sin6)); 198 memcpy(&sin6, sa, sa->sa_len); 199 sin6.sin6_len = sizeof(struct sockaddr_in6); 200 sin6.sin6_family = AF_INET6; 201 #ifdef __KAME__ 202 if (sa->sa_len == sizeof(struct sockaddr_in6) && 203 (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 204 IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && 205 sin6.sin6_scope_id == 0) { 206 sin6.sin6_scope_id = 207 ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 208 sin6.sin6_addr.s6_addr[2] = 0; 209 sin6.sin6_addr.s6_addr[3] = 0; 210 } 211 #endif 212 if (nflag) 213 niflags |= NI_NUMERICHOST; 214 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 215 line, sizeof(line), NULL, 0, niflags) != 0) 216 strncpy(line, "invalid", sizeof(line)); 217 218 return(line); 219 } 220 #endif 221 222 case AF_LINK: 223 return (link_ntoa((struct sockaddr_dl *)sa)); 224 225 default: 226 { u_short *s = (u_short *)sa; 227 u_short *slim = s + ((sa->sa_len + 1) >> 1); 228 char *cp = line + sprintf(line, "(%d)", sa->sa_family); 229 char *cpe = line + sizeof(line); 230 231 while (++s < slim && cp < cpe) /* start with sa->sa_data */ 232 if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0) 233 cp += n; 234 else 235 *cp = '\0'; 236 break; 237 } 238 } 239 return (line); 240 } 241 242 #ifndef SA_SIZE 243 /* 244 * This macro returns the size of a struct sockaddr when passed 245 * through a routing socket. Basically we round up sa_len to 246 * a multiple of sizeof(long), with a minimum of sizeof(long). 247 * The check for a NULL pointer is just a convenience, probably never used. 248 * The case sa_len == 0 should only apply to empty structures. 249 */ 250 #define SA_SIZE(sa) \ 251 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 252 sizeof(long) : \ 253 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) 254 #endif 255 256 static void 257 pmsg_addrs(char *cp, int addrs) 258 { 259 struct sockaddr *sa; 260 int i; 261 262 if (addrs == 0) { 263 (void) putchar('\n'); 264 return; 265 } 266 printf("\nsockaddrs: "); 267 bprintf(stdout, addrs, addrnames); 268 putchar('\n'); 269 for (i = 1; i; i <<= 1) 270 if (i & addrs) { 271 sa = (struct sockaddr *)cp; 272 printf(" %s", routename(sa)); 273 cp += SA_SIZE(sa); 274 } 275 putchar('\n'); 276 } 277 278 static const char * 279 ether_sprintf(const uint8_t mac[6]) 280 { 281 static char buf[32]; 282 283 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 284 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 285 return buf; 286 } 287 288 static void 289 print_rtmsg(struct rt_msghdr *rtm, int msglen) 290 { 291 struct if_msghdr *ifm; 292 struct if_announcemsghdr *ifan; 293 time_t now = time(NULL); 294 char *cnow = ctime(&now); 295 296 (void) msglen; /* UNUSED */ 297 298 if (rtm->rtm_version != RTM_VERSION) { 299 (void) printf("routing message version %d not understood\n", 300 rtm->rtm_version); 301 return; 302 } 303 switch (rtm->rtm_type) { 304 case RTM_IFINFO: 305 ifm = (struct if_msghdr *)rtm; 306 printf("%.19s RTM_IFINFO: if# %d, ", 307 cnow, ifm->ifm_index); 308 switch (ifm->ifm_data.ifi_link_state) { 309 case LINK_STATE_DOWN: 310 printf("link: down, flags:"); 311 break; 312 case LINK_STATE_UP: 313 printf("link: up, flags:"); 314 break; 315 default: 316 printf("link: unknown<%d>, flags:", 317 ifm->ifm_data.ifi_link_state); 318 break; 319 } 320 bprintf(stdout, ifm->ifm_flags, ifnetflags); 321 pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 322 fflush(stdout); 323 break; 324 case RTM_IFANNOUNCE: 325 ifan = (struct if_announcemsghdr *)rtm; 326 printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 327 cnow, ifan->ifan_index); 328 switch (ifan->ifan_what) { 329 case IFAN_ARRIVAL: 330 printf("arrival"); 331 break; 332 case IFAN_DEPARTURE: 333 printf("departure"); 334 break; 335 default: 336 printf("#%d", ifan->ifan_what); 337 break; 338 } 339 printf("\n"); 340 fflush(stdout); 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: if# %d, ", cnow, ifan->ifan_index); 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 case RTM_IEEE80211_WDS: 395 printf("%s wds discovery", 396 ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 397 break; 398 case RTM_IEEE80211_CSA: 399 printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d" 400 , V(ieee80211_csa_event)->iev_ieee 401 , V(ieee80211_csa_event)->iev_freq 402 , V(ieee80211_csa_event)->iev_flags 403 , V(ieee80211_csa_event)->iev_mode 404 , V(ieee80211_csa_event)->iev_count 405 ); 406 break; 407 case RTM_IEEE80211_CAC: 408 printf("channel availability check " 409 "(channel %u, %u MHz flags 0x%x) " 410 , V(ieee80211_cac_event)->iev_ieee 411 , V(ieee80211_cac_event)->iev_freq 412 , V(ieee80211_cac_event)->iev_flags 413 ); 414 switch (V(ieee80211_cac_event)->iev_type) { 415 case IEEE80211_NOTIFY_CAC_START: 416 printf("start timer"); 417 break; 418 case IEEE80211_NOTIFY_CAC_STOP: 419 printf("stop timer"); 420 break; 421 case IEEE80211_NOTIFY_CAC_EXPIRE: 422 printf("timer expired"); 423 break; 424 case IEEE80211_NOTIFY_CAC_RADAR: 425 printf("radar detected"); 426 break; 427 default: 428 printf("unknown type %d", 429 V(ieee80211_cac_event)->iev_type); 430 break; 431 } 432 break; 433 case RTM_IEEE80211_DEAUTH: 434 printf("%s wds deauth", 435 ether_sprintf(V(ieee80211_deauth_event)->iev_addr)); 436 break; 437 case RTM_IEEE80211_AUTH: 438 printf("%s node authenticate", 439 ether_sprintf(V(ieee80211_auth_event)->iev_addr)); 440 break; 441 case RTM_IEEE80211_COUNTRY: 442 printf("%s adopt country code '%c%c'", 443 ether_sprintf(V(ieee80211_country_event)->iev_addr), 444 V(ieee80211_country_event)->iev_cc[0], 445 V(ieee80211_country_event)->iev_cc[1]); 446 break; 447 case RTM_IEEE80211_RADIO: 448 printf("radio %s", 449 V(ieee80211_radio_event)->iev_state ? "ON" : "OFF"); 450 break; 451 default: 452 printf("what: #%d", ifan->ifan_what); 453 break; 454 } 455 printf("\n"); 456 fflush(stdout); 457 break; 458 #undef V 459 } 460 } 461