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