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> 48153317Ssam#include <netatalk/at.h> 49160053Ssam#ifdef __NetBSD__ 50160053Ssam#include <net80211/ieee80211_netbsd.h> 51160053Ssam#elif __FreeBSD__ 52153317Ssam#include <net80211/ieee80211_freebsd.h> 53160053Ssam#else 54160053Ssam#error "No support for your operating system!" 55160053Ssam#endif 56153317Ssam#include <arpa/inet.h> 57153317Ssam#include <netdb.h> 58153317Ssam 59153317Ssam#include <ctype.h> 60153317Ssam#include <err.h> 61153317Ssam#include <errno.h> 62153317Ssam#include <paths.h> 63153317Ssam#include <stdio.h> 64153317Ssam#include <stdlib.h> 65153317Ssam#include <string.h> 66153317Ssam#include <sysexits.h> 67153317Ssam#include <unistd.h> 68153317Ssam#include <ifaddrs.h> 69153317Ssam 70178698Ssam/* XXX */ 71178698Ssamenum ieee80211_notify_cac_event { 72178698Ssam IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */ 73178698Ssam IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */ 74178698Ssam IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */ 75178698Ssam IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */ 76178698Ssam}; 77178698Ssam 78153317Ssamstatic void print_rtmsg(struct rt_msghdr *rtm, int msglen); 79153317Ssam 80153317Ssamint nflag = 0; 81153317Ssam 82153317Ssamint 83153317Ssammain(int argc, char *argv[]) 84153317Ssam{ 85153317Ssam int n, s; 86153317Ssam char msg[2048]; 87153317Ssam 88153317Ssam s = socket(PF_ROUTE, SOCK_RAW, 0); 89153317Ssam if (s < 0) 90153317Ssam err(EX_OSERR, "socket"); 91153317Ssam for(;;) { 92153317Ssam n = read(s, msg, 2048); 93153317Ssam print_rtmsg((struct rt_msghdr *)msg, n); 94153317Ssam } 95153317Ssam return 0; 96153317Ssam} 97153317Ssam 98153317Ssamstatic void 99177504Ssambprintf(FILE *fp, int b, char *s) 100153317Ssam{ 101153317Ssam int i; 102153317Ssam int gotsome = 0; 103153317Ssam 104153317Ssam if (b == 0) 105153317Ssam return; 106153317Ssam while ((i = *s++) != 0) { 107153317Ssam if (b & (1 << (i-1))) { 108153317Ssam if (gotsome == 0) 109153317Ssam i = '<'; 110153317Ssam else 111153317Ssam i = ','; 112153317Ssam (void) putc(i, fp); 113153317Ssam gotsome = 1; 114153317Ssam for (; (i = *s) > 32; s++) 115153317Ssam (void) putc(i, fp); 116153317Ssam } else 117153317Ssam while (*s > 32) 118153317Ssam s++; 119153317Ssam } 120153317Ssam if (gotsome) 121153317Ssam putc('>', fp); 122153317Ssam} 123153317Ssam 124153317Ssamchar metricnames[] = 125153317Ssam"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" 126153317Ssam"\1mtu"; 127153317Ssamchar routeflags[] = 128153317Ssam"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" 129153317Ssam"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016" 130153317Ssam"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE" 131153317Ssam"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST"; 132153317Ssamchar ifnetflags[] = 133153317Ssam"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" 134153317Ssam"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" 135153317Ssam"\017LINK2\020MULTICAST"; 136153317Ssamchar addrnames[] = 137153317Ssam"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; 138153317Ssam 139177504Ssamstatic const char * 140177504Ssamroutename(struct sockaddr *sa) 141153317Ssam{ 142153317Ssam char *cp; 143153317Ssam static char line[MAXHOSTNAMELEN + 1]; 144153317Ssam struct hostent *hp; 145153317Ssam static char domain[MAXHOSTNAMELEN + 1]; 146153317Ssam static int first = 1, n; 147153317Ssam 148153317Ssam if (first) { 149153317Ssam first = 0; 150153317Ssam if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 151153317Ssam (cp = strchr(domain, '.'))) { 152153317Ssam domain[MAXHOSTNAMELEN] = '\0'; 153153317Ssam (void) strcpy(domain, cp + 1); 154153317Ssam } else 155153317Ssam domain[0] = 0; 156153317Ssam } 157153317Ssam 158153317Ssam if (sa->sa_len == 0) 159153317Ssam strcpy(line, "default"); 160153317Ssam else switch (sa->sa_family) { 161153317Ssam 162153317Ssam case AF_INET: 163153317Ssam { struct in_addr in; 164153317Ssam in = ((struct sockaddr_in *)sa)->sin_addr; 165153317Ssam 166153317Ssam cp = 0; 167153317Ssam if (in.s_addr == INADDR_ANY || sa->sa_len < 4) 168153317Ssam cp = "default"; 169153317Ssam if (cp == 0 && !nflag) { 170153317Ssam hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), 171153317Ssam AF_INET); 172153317Ssam if (hp) { 173153317Ssam if ((cp = strchr(hp->h_name, '.')) && 174153317Ssam !strcmp(cp + 1, domain)) 175153317Ssam *cp = 0; 176153317Ssam cp = hp->h_name; 177153317Ssam } 178153317Ssam } 179153317Ssam if (cp) { 180153317Ssam strncpy(line, cp, sizeof(line) - 1); 181153317Ssam line[sizeof(line) - 1] = '\0'; 182153317Ssam } else 183153317Ssam (void) sprintf(line, "%s", inet_ntoa(in)); 184153317Ssam break; 185153317Ssam } 186153317Ssam 187153317Ssam#ifdef INET6 188153317Ssam case AF_INET6: 189153317Ssam { 190153317Ssam struct sockaddr_in6 sin6; /* use static var for safety */ 191153317Ssam int niflags = 0; 192153317Ssam#ifdef NI_WITHSCOPEID 193153317Ssam niflags = NI_WITHSCOPEID; 194153317Ssam#endif 195153317Ssam 196153317Ssam memset(&sin6, 0, sizeof(sin6)); 197153317Ssam memcpy(&sin6, sa, sa->sa_len); 198153317Ssam sin6.sin6_len = sizeof(struct sockaddr_in6); 199153317Ssam sin6.sin6_family = AF_INET6; 200153317Ssam#ifdef __KAME__ 201153317Ssam if (sa->sa_len == sizeof(struct sockaddr_in6) && 202153317Ssam (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 203153317Ssam IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && 204153317Ssam sin6.sin6_scope_id == 0) { 205153317Ssam sin6.sin6_scope_id = 206153317Ssam ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 207153317Ssam sin6.sin6_addr.s6_addr[2] = 0; 208153317Ssam sin6.sin6_addr.s6_addr[3] = 0; 209153317Ssam } 210153317Ssam#endif 211153317Ssam if (nflag) 212153317Ssam niflags |= NI_NUMERICHOST; 213153317Ssam if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 214153317Ssam line, sizeof(line), NULL, 0, niflags) != 0) 215153317Ssam strncpy(line, "invalid", sizeof(line)); 216153317Ssam 217153317Ssam return(line); 218153317Ssam } 219153317Ssam#endif 220153317Ssam 221153317Ssam case AF_LINK: 222153317Ssam return (link_ntoa((struct sockaddr_dl *)sa)); 223153317Ssam 224153317Ssam default: 225153317Ssam { u_short *s = (u_short *)sa; 226153317Ssam u_short *slim = s + ((sa->sa_len + 1) >> 1); 227153317Ssam char *cp = line + sprintf(line, "(%d)", sa->sa_family); 228153317Ssam char *cpe = line + sizeof(line); 229153317Ssam 230153317Ssam while (++s < slim && cp < cpe) /* start with sa->sa_data */ 231153317Ssam if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0) 232153317Ssam cp += n; 233153317Ssam else 234153317Ssam *cp = '\0'; 235153317Ssam break; 236153317Ssam } 237153317Ssam } 238153317Ssam return (line); 239153317Ssam} 240153317Ssam 241160053Ssam#ifndef SA_SIZE 242160053Ssam/* 243160053Ssam * This macro returns the size of a struct sockaddr when passed 244160053Ssam * through a routing socket. Basically we round up sa_len to 245160053Ssam * a multiple of sizeof(long), with a minimum of sizeof(long). 246160053Ssam * The check for a NULL pointer is just a convenience, probably never used. 247160053Ssam * The case sa_len == 0 should only apply to empty structures. 248160053Ssam */ 249160053Ssam#define SA_SIZE(sa) \ 250160053Ssam ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 251160053Ssam sizeof(long) : \ 252160053Ssam 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) 253160053Ssam#endif 254153317Ssam 255153317Ssamstatic void 256153317Ssampmsg_addrs(char *cp, int addrs) 257153317Ssam{ 258153317Ssam struct sockaddr *sa; 259153317Ssam int i; 260153317Ssam 261153317Ssam if (addrs == 0) { 262153317Ssam (void) putchar('\n'); 263153317Ssam return; 264153317Ssam } 265153317Ssam printf("\nsockaddrs: "); 266153317Ssam bprintf(stdout, addrs, addrnames); 267153317Ssam putchar('\n'); 268153317Ssam for (i = 1; i; i <<= 1) 269153317Ssam if (i & addrs) { 270153317Ssam sa = (struct sockaddr *)cp; 271153317Ssam printf(" %s", routename(sa)); 272153317Ssam cp += SA_SIZE(sa); 273153317Ssam } 274153317Ssam putchar('\n'); 275153317Ssam} 276153317Ssam 277153317Ssamstatic const char * 278153317Ssamether_sprintf(const uint8_t mac[6]) 279153317Ssam{ 280153317Ssam static char buf[32]; 281153317Ssam 282153317Ssam snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 283153317Ssam mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 284153317Ssam return buf; 285153317Ssam} 286153317Ssam 287153317Ssamstatic void 288153317Ssamprint_rtmsg(struct rt_msghdr *rtm, int msglen) 289153317Ssam{ 290153317Ssam struct if_msghdr *ifm; 291153317Ssam struct if_announcemsghdr *ifan; 292153317Ssam time_t now = time(NULL); 293153317Ssam char *cnow = ctime(&now); 294153317Ssam 295153317Ssam if (rtm->rtm_version != RTM_VERSION) { 296153317Ssam (void) printf("routing message version %d not understood\n", 297153317Ssam rtm->rtm_version); 298153317Ssam return; 299153317Ssam } 300153317Ssam switch (rtm->rtm_type) { 301153317Ssam case RTM_IFINFO: 302153317Ssam ifm = (struct if_msghdr *)rtm; 303153317Ssam printf("%.19s RTM_IFINFO: if# %d, ", 304153317Ssam cnow, ifm->ifm_index); 305153317Ssam switch (ifm->ifm_data.ifi_link_state) { 306153317Ssam case LINK_STATE_DOWN: 307177504Ssam printf("link: down, flags:"); 308153317Ssam break; 309153317Ssam case LINK_STATE_UP: 310177504Ssam printf("link: up, flags:"); 311153317Ssam break; 312153317Ssam default: 313177504Ssam printf("link: unknown<%d>, flags:", 314177504Ssam ifm->ifm_data.ifi_link_state); 315153317Ssam break; 316153317Ssam } 317153317Ssam bprintf(stdout, ifm->ifm_flags, ifnetflags); 318153317Ssam pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 319179122Sthompsa fflush(stdout); 320153317Ssam break; 321153317Ssam case RTM_IFANNOUNCE: 322153317Ssam ifan = (struct if_announcemsghdr *)rtm; 323153317Ssam printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 324153317Ssam cnow, ifan->ifan_index); 325153317Ssam switch (ifan->ifan_what) { 326153317Ssam case IFAN_ARRIVAL: 327153317Ssam printf("arrival"); 328153317Ssam break; 329153317Ssam case IFAN_DEPARTURE: 330153317Ssam printf("departure"); 331153317Ssam break; 332153317Ssam default: 333153317Ssam printf("#%d", ifan->ifan_what); 334153317Ssam break; 335153317Ssam } 336153317Ssam printf("\n"); 337179122Sthompsa fflush(stdout); 338153317Ssam break; 339153317Ssam case RTM_IEEE80211: 340153317Ssam#define V(type) ((struct type *)(&ifan[1])) 341153317Ssam ifan = (struct if_announcemsghdr *)rtm; 342177504Ssam printf("%.19s RTM_IEEE80211: if# %d, ", cnow, ifan->ifan_index); 343153317Ssam switch (ifan->ifan_what) { 344153317Ssam case RTM_IEEE80211_ASSOC: 345153317Ssam printf("associate with %s", 346153317Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr)); 347153317Ssam break; 348153317Ssam case RTM_IEEE80211_REASSOC: 349153317Ssam printf("reassociate with %s", 350153317Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr)); 351153317Ssam break; 352153317Ssam case RTM_IEEE80211_DISASSOC: 353153317Ssam printf("disassociate"); 354153317Ssam break; 355153317Ssam case RTM_IEEE80211_JOIN: 356153317Ssam case RTM_IEEE80211_REJOIN: 357153317Ssam printf("%s station %sjoin", 358160053Ssam ether_sprintf(V(ieee80211_join_event)->iev_addr), 359160053Ssam ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "" 360160053Ssam ); 361153317Ssam break; 362153317Ssam case RTM_IEEE80211_LEAVE: 363153317Ssam printf("%s station leave", 364153317Ssam ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 365153317Ssam break; 366153317Ssam case RTM_IEEE80211_SCAN: 367153317Ssam printf("scan complete"); 368153317Ssam break; 369153317Ssam case RTM_IEEE80211_REPLAY: 370153317Ssam printf("replay failure: src %s " 371153317Ssam , ether_sprintf(V(ieee80211_replay_event)->iev_src) 372153317Ssam ); 373153317Ssam printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu" 374153317Ssam , ether_sprintf(V(ieee80211_replay_event)->iev_dst) 375153317Ssam , V(ieee80211_replay_event)->iev_cipher 376153317Ssam , V(ieee80211_replay_event)->iev_keyix 377153317Ssam , V(ieee80211_replay_event)->iev_keyrsc 378153317Ssam , V(ieee80211_replay_event)->iev_rsc 379153317Ssam ); 380153317Ssam break; 381153317Ssam case RTM_IEEE80211_MICHAEL: 382153317Ssam printf("michael failure: src %s " 383153317Ssam , ether_sprintf(V(ieee80211_michael_event)->iev_src) 384153317Ssam ); 385153317Ssam printf("dst %s cipher %u keyix %u" 386153317Ssam , ether_sprintf(V(ieee80211_michael_event)->iev_dst) 387153317Ssam , V(ieee80211_michael_event)->iev_cipher 388153317Ssam , V(ieee80211_michael_event)->iev_keyix 389153317Ssam ); 390153317Ssam break; 391178698Ssam case RTM_IEEE80211_WDS: 392178698Ssam printf("%s wds discovery", 393178698Ssam ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 394178698Ssam break; 395178698Ssam case RTM_IEEE80211_CSA: 396178698Ssam printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d" 397178698Ssam , V(ieee80211_csa_event)->iev_ieee 398178698Ssam , V(ieee80211_csa_event)->iev_freq 399178698Ssam , V(ieee80211_csa_event)->iev_flags 400178698Ssam , V(ieee80211_csa_event)->iev_mode 401178698Ssam , V(ieee80211_csa_event)->iev_count 402178698Ssam ); 403178698Ssam break; 404178698Ssam case RTM_IEEE80211_CAC: 405178698Ssam printf("channel availability check " 406178698Ssam "(channel %u, %u MHz flags 0x%x) " 407178698Ssam , V(ieee80211_cac_event)->iev_ieee 408178698Ssam , V(ieee80211_cac_event)->iev_freq 409178698Ssam , V(ieee80211_cac_event)->iev_flags 410178698Ssam ); 411178698Ssam switch (V(ieee80211_cac_event)->iev_type) { 412178698Ssam case IEEE80211_NOTIFY_CAC_START: 413178698Ssam printf("start timer"); 414178698Ssam break; 415178698Ssam case IEEE80211_NOTIFY_CAC_STOP: 416178698Ssam printf("stop timer"); 417178698Ssam break; 418178698Ssam case IEEE80211_NOTIFY_CAC_EXPIRE: 419178698Ssam printf("timer expired"); 420178698Ssam break; 421178698Ssam case IEEE80211_NOTIFY_CAC_RADAR: 422178698Ssam printf("radar detected"); 423178698Ssam break; 424178698Ssam default: 425178698Ssam printf("unknown type %d", 426178698Ssam V(ieee80211_cac_event)->iev_type); 427178698Ssam break; 428178698Ssam } 429178698Ssam break; 430178698Ssam case RTM_IEEE80211_DEAUTH: 431178698Ssam printf("%s wds deauth", 432178698Ssam ether_sprintf(V(ieee80211_deauth_event)->iev_addr)); 433178698Ssam break; 434178698Ssam case RTM_IEEE80211_AUTH: 435178698Ssam printf("%s node authenticate", 436178698Ssam ether_sprintf(V(ieee80211_auth_event)->iev_addr)); 437178698Ssam break; 438178698Ssam case RTM_IEEE80211_COUNTRY: 439178698Ssam printf("%s adopt country code '%c%c'", 440178698Ssam ether_sprintf(V(ieee80211_country_event)->iev_addr), 441178698Ssam V(ieee80211_country_event)->iev_cc[0], 442178698Ssam V(ieee80211_country_event)->iev_cc[1]); 443178698Ssam break; 444178698Ssam case RTM_IEEE80211_RADIO: 445178698Ssam printf("radio %s", 446178698Ssam V(ieee80211_radio_event)->iev_state ? "ON" : "OFF"); 447178698Ssam break; 448153317Ssam default: 449177504Ssam printf("what: #%d", ifan->ifan_what); 450153317Ssam break; 451153317Ssam } 452153317Ssam printf("\n"); 453179122Sthompsa fflush(stdout); 454153317Ssam break; 455153317Ssam#undef V 456153317Ssam } 457153317Ssam} 458