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