1153317Ssam/*- 2174244Ssam * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting 3153317Ssam * All rights reserved. 4153317Ssam * 5153317Ssam * Redistribution and use in source and binary forms, with or without 6153317Ssam * modification, are permitted provided that the following conditions 7153317Ssam * are met: 8153317Ssam * 1. Redistributions of source code must retain the above copyright 9153317Ssam * notice, this list of conditions and the following disclaimer, 10153317Ssam * without modification. 11153317Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12153317Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13153317Ssam * redistribution must be conditioned upon including a substantially 14153317Ssam * similar Disclaimer requirement for further binary redistribution. 15153317Ssam * 16153317Ssam * NO WARRANTY 17153317Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18153317Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19153317Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20153317Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21153317Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22153317Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23153317Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24153317Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25153317Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26153317Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27153317Ssam * THE POSSIBILITY OF SUCH DAMAGES. 28153317Ssam * 29153317Ssam * $FreeBSD$ 30153317Ssam */ 31153317Ssam 32153317Ssam/* 33153317Ssam * Monitor 802.11 events using a routing socket. 34153317Ssam * Code liberaly swiped from route(8). 35153317Ssam */ 36153317Ssam#include <sys/param.h> 37153317Ssam#include <sys/file.h> 38153317Ssam#include <sys/socket.h> 39153317Ssam#include <sys/ioctl.h> 40153317Ssam#include <sys/sysctl.h> 41153317Ssam#include <sys/types.h> 42153317Ssam 43153317Ssam#include <net/if.h> 44153317Ssam#include <net/route.h> 45153317Ssam#include <net/if_dl.h> 46153317Ssam#include <netinet/in.h> 47153317Ssam#include <netinet/if_ether.h> 48160053Ssam#ifdef __NetBSD__ 49160053Ssam#include <net80211/ieee80211_netbsd.h> 50160053Ssam#elif __FreeBSD__ 51153317Ssam#include <net80211/ieee80211_freebsd.h> 52160053Ssam#else 53160053Ssam#error "No support for your operating system!" 54160053Ssam#endif 55153317Ssam#include <arpa/inet.h> 56153317Ssam#include <netdb.h> 57153317Ssam 58153317Ssam#include <ctype.h> 59153317Ssam#include <err.h> 60153317Ssam#include <errno.h> 61153317Ssam#include <paths.h> 62153317Ssam#include <stdio.h> 63153317Ssam#include <stdlib.h> 64153317Ssam#include <string.h> 65153317Ssam#include <sysexits.h> 66153317Ssam#include <unistd.h> 67153317Ssam#include <ifaddrs.h> 68153317Ssam 69178698Ssam/* XXX */ 70178698Ssamenum ieee80211_notify_cac_event { 71178698Ssam IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */ 72178698Ssam IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */ 73178698Ssam IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */ 74178698Ssam IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */ 75178698Ssam}; 76178698Ssam 77153317Ssamstatic void print_rtmsg(struct rt_msghdr *rtm, int msglen); 78153317Ssam 79153317Ssamint nflag = 0; 80153317Ssam 81153317Ssamint 82153317Ssammain(int argc, char *argv[]) 83153317Ssam{ 84153317Ssam int n, s; 85153317Ssam char msg[2048]; 86153317Ssam 87153317Ssam s = socket(PF_ROUTE, SOCK_RAW, 0); 88153317Ssam if (s < 0) 89153317Ssam err(EX_OSERR, "socket"); 90153317Ssam for(;;) { 91153317Ssam n = read(s, msg, 2048); 92153317Ssam print_rtmsg((struct rt_msghdr *)msg, n); 93153317Ssam } 94153317Ssam return 0; 95153317Ssam} 96153317Ssam 97153317Ssamstatic void 98177504Ssambprintf(FILE *fp, int b, char *s) 99153317Ssam{ 100153317Ssam int i; 101153317Ssam int gotsome = 0; 102153317Ssam 103153317Ssam if (b == 0) 104153317Ssam return; 105153317Ssam while ((i = *s++) != 0) { 106153317Ssam if (b & (1 << (i-1))) { 107153317Ssam if (gotsome == 0) 108153317Ssam i = '<'; 109153317Ssam else 110153317Ssam i = ','; 111153317Ssam (void) putc(i, fp); 112153317Ssam gotsome = 1; 113153317Ssam for (; (i = *s) > 32; s++) 114153317Ssam (void) putc(i, fp); 115153317Ssam } else 116153317Ssam while (*s > 32) 117153317Ssam s++; 118153317Ssam } 119153317Ssam if (gotsome) 120153317Ssam putc('>', fp); 121153317Ssam} 122153317Ssam 123153317Ssamchar metricnames[] = 124153317Ssam"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" 125153317Ssam"\1mtu"; 126153317Ssamchar routeflags[] = 127153317Ssam"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" 128153317Ssam"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016" 129153317Ssam"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE" 130153317Ssam"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST"; 131153317Ssamchar ifnetflags[] = 132153317Ssam"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" 133153317Ssam"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" 134153317Ssam"\017LINK2\020MULTICAST"; 135153317Ssamchar addrnames[] = 136153317Ssam"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; 137153317Ssam 138177504Ssamstatic const char * 139177504Ssamroutename(struct sockaddr *sa) 140153317Ssam{ 141153317Ssam char *cp; 142153317Ssam static char line[MAXHOSTNAMELEN + 1]; 143153317Ssam struct hostent *hp; 144153317Ssam static char domain[MAXHOSTNAMELEN + 1]; 145153317Ssam static int first = 1, n; 146153317Ssam 147153317Ssam if (first) { 148153317Ssam first = 0; 149153317Ssam if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 150153317Ssam (cp = strchr(domain, '.'))) { 151153317Ssam domain[MAXHOSTNAMELEN] = '\0'; 152153317Ssam (void) strcpy(domain, cp + 1); 153153317Ssam } else 154153317Ssam domain[0] = 0; 155153317Ssam } 156153317Ssam 157153317Ssam if (sa->sa_len == 0) 158153317Ssam strcpy(line, "default"); 159153317Ssam else switch (sa->sa_family) { 160153317Ssam 161153317Ssam case AF_INET: 162153317Ssam { struct in_addr in; 163153317Ssam in = ((struct sockaddr_in *)sa)->sin_addr; 164153317Ssam 165153317Ssam cp = 0; 166153317Ssam if (in.s_addr == INADDR_ANY || sa->sa_len < 4) 167153317Ssam cp = "default"; 168153317Ssam if (cp == 0 && !nflag) { 169153317Ssam hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), 170153317Ssam AF_INET); 171153317Ssam if (hp) { 172153317Ssam if ((cp = strchr(hp->h_name, '.')) && 173153317Ssam !strcmp(cp + 1, domain)) 174153317Ssam *cp = 0; 175153317Ssam cp = hp->h_name; 176153317Ssam } 177153317Ssam } 178153317Ssam if (cp) { 179153317Ssam strncpy(line, cp, sizeof(line) - 1); 180153317Ssam line[sizeof(line) - 1] = '\0'; 181153317Ssam } else 182153317Ssam (void) sprintf(line, "%s", inet_ntoa(in)); 183153317Ssam break; 184153317Ssam } 185153317Ssam 186153317Ssam#ifdef INET6 187153317Ssam case AF_INET6: 188153317Ssam { 189153317Ssam struct sockaddr_in6 sin6; /* use static var for safety */ 190153317Ssam int niflags = 0; 191153317Ssam#ifdef NI_WITHSCOPEID 192153317Ssam niflags = NI_WITHSCOPEID; 193153317Ssam#endif 194153317Ssam 195153317Ssam memset(&sin6, 0, sizeof(sin6)); 196153317Ssam memcpy(&sin6, sa, sa->sa_len); 197153317Ssam sin6.sin6_len = sizeof(struct sockaddr_in6); 198153317Ssam sin6.sin6_family = AF_INET6; 199153317Ssam#ifdef __KAME__ 200153317Ssam if (sa->sa_len == sizeof(struct sockaddr_in6) && 201153317Ssam (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 202153317Ssam IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && 203153317Ssam sin6.sin6_scope_id == 0) { 204153317Ssam sin6.sin6_scope_id = 205153317Ssam ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 206153317Ssam sin6.sin6_addr.s6_addr[2] = 0; 207153317Ssam sin6.sin6_addr.s6_addr[3] = 0; 208153317Ssam } 209153317Ssam#endif 210153317Ssam if (nflag) 211153317Ssam niflags |= NI_NUMERICHOST; 212153317Ssam if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 213153317Ssam line, sizeof(line), NULL, 0, niflags) != 0) 214153317Ssam strncpy(line, "invalid", sizeof(line)); 215153317Ssam 216153317Ssam return(line); 217153317Ssam } 218153317Ssam#endif 219153317Ssam 220153317Ssam case AF_LINK: 221153317Ssam return (link_ntoa((struct sockaddr_dl *)sa)); 222153317Ssam 223153317Ssam default: 224153317Ssam { u_short *s = (u_short *)sa; 225153317Ssam u_short *slim = s + ((sa->sa_len + 1) >> 1); 226153317Ssam char *cp = line + sprintf(line, "(%d)", sa->sa_family); 227153317Ssam char *cpe = line + sizeof(line); 228153317Ssam 229153317Ssam while (++s < slim && cp < cpe) /* start with sa->sa_data */ 230153317Ssam if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0) 231153317Ssam cp += n; 232153317Ssam else 233153317Ssam *cp = '\0'; 234153317Ssam break; 235153317Ssam } 236153317Ssam } 237153317Ssam return (line); 238153317Ssam} 239153317Ssam 240160053Ssam#ifndef SA_SIZE 241160053Ssam/* 242160053Ssam * This macro returns the size of a struct sockaddr when passed 243160053Ssam * through a routing socket. Basically we round up sa_len to 244160053Ssam * a multiple of sizeof(long), with a minimum of sizeof(long). 245160053Ssam * The check for a NULL pointer is just a convenience, probably never used. 246160053Ssam * The case sa_len == 0 should only apply to empty structures. 247160053Ssam */ 248160053Ssam#define SA_SIZE(sa) \ 249160053Ssam ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 250160053Ssam sizeof(long) : \ 251160053Ssam 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) 252160053Ssam#endif 253153317Ssam 254153317Ssamstatic void 255153317Ssampmsg_addrs(char *cp, int addrs) 256153317Ssam{ 257153317Ssam struct sockaddr *sa; 258153317Ssam int i; 259153317Ssam 260153317Ssam if (addrs == 0) { 261153317Ssam (void) putchar('\n'); 262153317Ssam return; 263153317Ssam } 264153317Ssam printf("\nsockaddrs: "); 265153317Ssam bprintf(stdout, addrs, addrnames); 266153317Ssam putchar('\n'); 267153317Ssam for (i = 1; i; i <<= 1) 268153317Ssam if (i & addrs) { 269153317Ssam sa = (struct sockaddr *)cp; 270153317Ssam printf(" %s", routename(sa)); 271153317Ssam cp += SA_SIZE(sa); 272153317Ssam } 273153317Ssam putchar('\n'); 274153317Ssam} 275153317Ssam 276153317Ssamstatic const char * 277153317Ssamether_sprintf(const uint8_t mac[6]) 278153317Ssam{ 279153317Ssam static char buf[32]; 280153317Ssam 281153317Ssam snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 282153317Ssam mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 283153317Ssam return buf; 284153317Ssam} 285153317Ssam 286153317Ssamstatic void 287153317Ssamprint_rtmsg(struct rt_msghdr *rtm, int msglen) 288153317Ssam{ 289153317Ssam struct if_msghdr *ifm; 290153317Ssam struct if_announcemsghdr *ifan; 291153317Ssam time_t now = time(NULL); 292153317Ssam char *cnow = ctime(&now); 293153317Ssam 294153317Ssam if (rtm->rtm_version != RTM_VERSION) { 295153317Ssam (void) printf("routing message version %d not understood\n", 296153317Ssam rtm->rtm_version); 297153317Ssam return; 298153317Ssam } 299153317Ssam switch (rtm->rtm_type) { 300153317Ssam case RTM_IFINFO: 301153317Ssam ifm = (struct if_msghdr *)rtm; 302153317Ssam printf("%.19s RTM_IFINFO: if# %d, ", 303153317Ssam cnow, ifm->ifm_index); 304153317Ssam switch (ifm->ifm_data.ifi_link_state) { 305153317Ssam case LINK_STATE_DOWN: 306177504Ssam printf("link: down, flags:"); 307153317Ssam break; 308153317Ssam case LINK_STATE_UP: 309177504Ssam printf("link: up, flags:"); 310153317Ssam break; 311153317Ssam default: 312177504Ssam printf("link: unknown<%d>, flags:", 313177504Ssam ifm->ifm_data.ifi_link_state); 314153317Ssam break; 315153317Ssam } 316153317Ssam bprintf(stdout, ifm->ifm_flags, ifnetflags); 317153317Ssam pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 318179122Sthompsa fflush(stdout); 319153317Ssam break; 320153317Ssam case RTM_IFANNOUNCE: 321153317Ssam ifan = (struct if_announcemsghdr *)rtm; 322153317Ssam printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 323153317Ssam cnow, ifan->ifan_index); 324153317Ssam switch (ifan->ifan_what) { 325153317Ssam case IFAN_ARRIVAL: 326153317Ssam printf("arrival"); 327153317Ssam break; 328153317Ssam case IFAN_DEPARTURE: 329153317Ssam printf("departure"); 330153317Ssam break; 331153317Ssam default: 332153317Ssam printf("#%d", ifan->ifan_what); 333153317Ssam break; 334153317Ssam } 335153317Ssam printf("\n"); 336179122Sthompsa fflush(stdout); 337153317Ssam break; 338153317Ssam case RTM_IEEE80211: 339153317Ssam#define V(type) ((struct type *)(&ifan[1])) 340153317Ssam ifan = (struct if_announcemsghdr *)rtm; 341177504Ssam printf("%.19s RTM_IEEE80211: if# %d, ", cnow, ifan->ifan_index); 342153317Ssam switch (ifan->ifan_what) { 343153317Ssam case RTM_IEEE80211_ASSOC: 344153317Ssam printf("associate with %s", 345153317Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr)); 346153317Ssam break; 347153317Ssam case RTM_IEEE80211_REASSOC: 348153317Ssam printf("reassociate with %s", 349153317Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr)); 350153317Ssam break; 351153317Ssam case RTM_IEEE80211_DISASSOC: 352153317Ssam printf("disassociate"); 353153317Ssam break; 354153317Ssam case RTM_IEEE80211_JOIN: 355153317Ssam case RTM_IEEE80211_REJOIN: 356153317Ssam printf("%s station %sjoin", 357160053Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr), 358160053Ssam ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "" 359160053Ssam ); 360153317Ssam break; 361153317Ssam case RTM_IEEE80211_LEAVE: 362153317Ssam printf("%s station leave", 363153317Ssam ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 364153317Ssam break; 365153317Ssam case RTM_IEEE80211_SCAN: 366153317Ssam printf("scan complete"); 367153317Ssam break; 368153317Ssam case RTM_IEEE80211_REPLAY: 369153317Ssam printf("replay failure: src %s " 370153317Ssam , ether_sprintf(V(ieee80211_replay_event)->iev_src) 371153317Ssam ); 372153317Ssam printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu" 373153317Ssam , ether_sprintf(V(ieee80211_replay_event)->iev_dst) 374153317Ssam , V(ieee80211_replay_event)->iev_cipher 375153317Ssam , V(ieee80211_replay_event)->iev_keyix 376153317Ssam , V(ieee80211_replay_event)->iev_keyrsc 377153317Ssam , V(ieee80211_replay_event)->iev_rsc 378153317Ssam ); 379153317Ssam break; 380153317Ssam case RTM_IEEE80211_MICHAEL: 381153317Ssam printf("michael failure: src %s " 382153317Ssam , ether_sprintf(V(ieee80211_michael_event)->iev_src) 383153317Ssam ); 384153317Ssam printf("dst %s cipher %u keyix %u" 385153317Ssam , ether_sprintf(V(ieee80211_michael_event)->iev_dst) 386153317Ssam , V(ieee80211_michael_event)->iev_cipher 387153317Ssam , V(ieee80211_michael_event)->iev_keyix 388153317Ssam ); 389153317Ssam break; 390178698Ssam case RTM_IEEE80211_WDS: 391178698Ssam printf("%s wds discovery", 392178698Ssam ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 393178698Ssam break; 394178698Ssam case RTM_IEEE80211_CSA: 395178698Ssam printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d" 396178698Ssam , V(ieee80211_csa_event)->iev_ieee 397178698Ssam , V(ieee80211_csa_event)->iev_freq 398178698Ssam , V(ieee80211_csa_event)->iev_flags 399178698Ssam , V(ieee80211_csa_event)->iev_mode 400178698Ssam , V(ieee80211_csa_event)->iev_count 401178698Ssam ); 402178698Ssam break; 403178698Ssam case RTM_IEEE80211_CAC: 404178698Ssam printf("channel availability check " 405178698Ssam "(channel %u, %u MHz flags 0x%x) " 406178698Ssam , V(ieee80211_cac_event)->iev_ieee 407178698Ssam , V(ieee80211_cac_event)->iev_freq 408178698Ssam , V(ieee80211_cac_event)->iev_flags 409178698Ssam ); 410178698Ssam switch (V(ieee80211_cac_event)->iev_type) { 411178698Ssam case IEEE80211_NOTIFY_CAC_START: 412178698Ssam printf("start timer"); 413178698Ssam break; 414178698Ssam case IEEE80211_NOTIFY_CAC_STOP: 415178698Ssam printf("stop timer"); 416178698Ssam break; 417178698Ssam case IEEE80211_NOTIFY_CAC_EXPIRE: 418178698Ssam printf("timer expired"); 419178698Ssam break; 420178698Ssam case IEEE80211_NOTIFY_CAC_RADAR: 421178698Ssam printf("radar detected"); 422178698Ssam break; 423178698Ssam default: 424178698Ssam printf("unknown type %d", 425178698Ssam V(ieee80211_cac_event)->iev_type); 426178698Ssam break; 427178698Ssam } 428178698Ssam break; 429178698Ssam case RTM_IEEE80211_DEAUTH: 430178698Ssam printf("%s wds deauth", 431178698Ssam ether_sprintf(V(ieee80211_deauth_event)->iev_addr)); 432178698Ssam break; 433178698Ssam case RTM_IEEE80211_AUTH: 434178698Ssam printf("%s node authenticate", 435178698Ssam ether_sprintf(V(ieee80211_auth_event)->iev_addr)); 436178698Ssam break; 437178698Ssam case RTM_IEEE80211_COUNTRY: 438178698Ssam printf("%s adopt country code '%c%c'", 439178698Ssam ether_sprintf(V(ieee80211_country_event)->iev_addr), 440178698Ssam V(ieee80211_country_event)->iev_cc[0], 441178698Ssam V(ieee80211_country_event)->iev_cc[1]); 442178698Ssam break; 443178698Ssam case RTM_IEEE80211_RADIO: 444178698Ssam printf("radio %s", 445178698Ssam V(ieee80211_radio_event)->iev_state ? "ON" : "OFF"); 446178698Ssam break; 447153317Ssam default: 448177504Ssam printf("what: #%d", ifan->ifan_what); 449153317Ssam break; 450153317Ssam } 451153317Ssam printf("\n"); 452179122Sthompsa fflush(stdout); 453153317Ssam break; 454153317Ssam#undef V 455153317Ssam } 456153317Ssam} 457