155714Skris/*- 255714Skris * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8280304Sjkim * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer, 1055714Skris * without modification. 1155714Skris * 2. Redistributions in binary form must reproduce at minimum a disclaimer 1255714Skris * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 1355714Skris * redistribution must be conditioned upon including a substantially 1455714Skris * similar Disclaimer requirement for further binary redistribution. 15280304Sjkim * 1655714Skris * NO WARRANTY 1755714Skris * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1855714Skris * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1955714Skris * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 2055714Skris * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 2155714Skris * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22280304Sjkim * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2355714Skris * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2455714Skris * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 2555714Skris * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2655714Skris * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 2755714Skris * THE POSSIBILITY OF SUCH DAMAGES. 2855714Skris * 2955714Skris * $FreeBSD$ 3055714Skris */ 3155714Skris 3255714Skris/* 3355714Skris * Monitor 802.11 events using a routing socket. 3455714Skris * Code liberaly swiped from route(8). 3555714Skris */ 3655714Skris#include <sys/param.h> 37280304Sjkim#include <sys/file.h> 3855714Skris#include <sys/socket.h> 3955714Skris#include <sys/ioctl.h> 40280304Sjkim#include <sys/sysctl.h> 4155714Skris#include <sys/types.h> 4255714Skris 4355714Skris#include <net/if.h> 4455714Skris#include <net/route.h> 4555714Skris#include <net/if_dl.h> 4655714Skris#include <netinet/in.h> 4755714Skris#include <netinet/if_ether.h> 4855714Skris#include <netatalk/at.h> 4955714Skris#ifdef __NetBSD__ 5055714Skris#include <net80211/ieee80211_netbsd.h> 5155714Skris#elif __FreeBSD__ 52280304Sjkim#include <net80211/ieee80211_freebsd.h> 5355714Skris#else 5455714Skris#error "No support for your operating system!" 5555714Skris#endif 5655714Skris#include <arpa/inet.h> 5755714Skris#include <netdb.h> 5855714Skris 5955714Skris#include <ctype.h> 60101613Snectar#include <err.h> 6155714Skris#include <errno.h> 6255714Skris#include <paths.h> 6355714Skris#include <stdio.h> 6455714Skris#include <stdlib.h> 65280304Sjkim#include <string.h> 66298999Sjkim#include <sysexits.h> 6755714Skris#include <unistd.h> 68280304Sjkim#include <ifaddrs.h> 6955714Skris 70160814Ssimon/* XXX */ 71280304Sjkimenum ieee80211_notify_cac_event { 72280304Sjkim IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */ 73280304Sjkim IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */ 74280304Sjkim IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */ 75280304Sjkim IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */ 76280304Sjkim}; 77280304Sjkim 78280304Sjkimstatic void print_rtmsg(struct rt_msghdr *rtm, int msglen); 79280304Sjkim 80280304Sjkimint nflag = 0; 81280304Sjkim 82280304Sjkimint 8355714Skrismain(int argc, char *argv[]) 84160814Ssimon{ 85280304Sjkim int n, s; 86280304Sjkim char msg[2048]; 87280304Sjkim 8855714Skris s = socket(PF_ROUTE, SOCK_RAW, 0); 89160814Ssimon if (s < 0) 90280304Sjkim err(EX_OSERR, "socket"); 91280304Sjkim for(;;) { 92280304Sjkim n = read(s, msg, 2048); 93160814Ssimon print_rtmsg((struct rt_msghdr *)msg, n); 94160814Ssimon } 95280304Sjkim return 0; 96280304Sjkim} 97280304Sjkim 98280304Sjkimstatic void 99280304Sjkimbprintf(FILE *fp, int b, char *s) 100280304Sjkim{ 101280304Sjkim int i; 10255714Skris int gotsome = 0; 103280304Sjkim 104280304Sjkim if (b == 0) 105280304Sjkim return; 106280304Sjkim while ((i = *s++) != 0) { 107280304Sjkim if (b & (1 << (i-1))) { 108280304Sjkim if (gotsome == 0) 109280304Sjkim i = '<'; 110280304Sjkim else 111280304Sjkim i = ','; 112280304Sjkim (void) putc(i, fp); 113280304Sjkim gotsome = 1; 114280304Sjkim for (; (i = *s) > 32; s++) 115280304Sjkim (void) putc(i, fp); 116280304Sjkim } else 117280304Sjkim while (*s > 32) 118280304Sjkim s++; 119280304Sjkim } 120280304Sjkim if (gotsome) 121280304Sjkim putc('>', fp); 122280304Sjkim} 123280304Sjkim 124280304Sjkimchar metricnames[] = 125280304Sjkim"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" 126280304Sjkim"\1mtu"; 127280304Sjkimchar routeflags[] = 128280304Sjkim"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" 129280304Sjkim"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016" 130280304Sjkim"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE" 131280304Sjkim"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST"; 132280304Sjkimchar ifnetflags[] = 133280304Sjkim"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" 134298999Sjkim"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" 135280304Sjkim"\017LINK2\020MULTICAST"; 13655714Skrischar addrnames[] = 137280304Sjkim"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; 138280304Sjkim 139269686Sjkimstatic const char * 14055714Skrisroutename(struct sockaddr *sa) 141280304Sjkim{ 142280304Sjkim char *cp; 143280304Sjkim static char line[MAXHOSTNAMELEN + 1]; 14455714Skris struct hostent *hp; 14555714Skris static char domain[MAXHOSTNAMELEN + 1]; 146280304Sjkim static int first = 1, n; 147280304Sjkim 148280304Sjkim if (first) { 149280304Sjkim first = 0; 150280304Sjkim if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 151280304Sjkim (cp = strchr(domain, '.'))) { 152280304Sjkim domain[MAXHOSTNAMELEN] = '\0'; 153280304Sjkim (void) strcpy(domain, cp + 1); 154280304Sjkim } else 155280304Sjkim domain[0] = 0; 156280304Sjkim } 157280304Sjkim 158280304Sjkim if (sa->sa_len == 0) 159280304Sjkim strcpy(line, "default"); 16055714Skris else switch (sa->sa_family) { 161280304Sjkim 162298999Sjkim case AF_INET: 163280304Sjkim { struct in_addr in; 164280304Sjkim in = ((struct sockaddr_in *)sa)->sin_addr; 165280304Sjkim 166298999Sjkim cp = 0; 16755714Skris if (in.s_addr == INADDR_ANY || sa->sa_len < 4) 168280304Sjkim cp = "default"; 169298999Sjkim if (cp == 0 && !nflag) { 170280304Sjkim hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), 171280304Sjkim AF_INET); 172280304Sjkim if (hp) { 173280304Sjkim if ((cp = strchr(hp->h_name, '.')) && 174280304Sjkim !strcmp(cp + 1, domain)) 175280304Sjkim *cp = 0; 176280304Sjkim cp = hp->h_name; 177280304Sjkim } 178298999Sjkim } 179280304Sjkim if (cp) { 180280304Sjkim strncpy(line, cp, sizeof(line) - 1); 181280304Sjkim line[sizeof(line) - 1] = '\0'; 182280304Sjkim } else 183280304Sjkim (void) sprintf(line, "%s", inet_ntoa(in)); 184280304Sjkim break; 185280304Sjkim } 186280304Sjkim 187280304Sjkim#ifdef INET6 188280304Sjkim case AF_INET6: 189280304Sjkim { 190280304Sjkim struct sockaddr_in6 sin6; /* use static var for safety */ 191298999Sjkim int niflags = 0; 192280304Sjkim#ifdef NI_WITHSCOPEID 19355714Skris niflags = NI_WITHSCOPEID; 194280304Sjkim#endif 195280304Sjkim 196280304Sjkim memset(&sin6, 0, sizeof(sin6)); 19755714Skris memcpy(&sin6, sa, sa->sa_len); 198280304Sjkim sin6.sin6_len = sizeof(struct sockaddr_in6); 199280304Sjkim sin6.sin6_family = AF_INET6; 200280304Sjkim#ifdef __KAME__ 201280304Sjkim if (sa->sa_len == sizeof(struct sockaddr_in6) && 20255714Skris (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 203280304Sjkim IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && 204280304Sjkim sin6.sin6_scope_id == 0) { 205280304Sjkim sin6.sin6_scope_id = 206280304Sjkim ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 207280304Sjkim sin6.sin6_addr.s6_addr[2] = 0; 208280304Sjkim sin6.sin6_addr.s6_addr[3] = 0; 209280304Sjkim } 210280304Sjkim#endif 211280304Sjkim if (nflag) 212280304Sjkim niflags |= NI_NUMERICHOST; 213280304Sjkim if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 214280304Sjkim line, sizeof(line), NULL, 0, niflags) != 0) 215280304Sjkim strncpy(line, "invalid", sizeof(line)); 216280304Sjkim 217280304Sjkim return(line); 218280304Sjkim } 219280304Sjkim#endif 220280304Sjkim 221280304Sjkim case AF_LINK: 222280304Sjkim return (link_ntoa((struct sockaddr_dl *)sa)); 223280304Sjkim 224280304Sjkim default: 225280304Sjkim { u_short *s = (u_short *)sa; 22655714Skris u_short *slim = s + ((sa->sa_len + 1) >> 1); 227160814Ssimon char *cp = line + sprintf(line, "(%d)", sa->sa_family); 228280304Sjkim char *cpe = line + sizeof(line); 229280304Sjkim 230280304Sjkim while (++s < slim && cp < cpe) /* start with sa->sa_data */ 231280304Sjkim if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0) 232280304Sjkim cp += n; 233280304Sjkim else 234280304Sjkim *cp = '\0'; 235160814Ssimon break; 23655714Skris } 237280304Sjkim } 238280304Sjkim return (line); 239280304Sjkim} 240280304Sjkim 241280304Sjkim#ifndef SA_SIZE 242280304Sjkim/* 243280304Sjkim * This macro returns the size of a struct sockaddr when passed 244280304Sjkim * through a routing socket. Basically we round up sa_len to 245280304Sjkim * a multiple of sizeof(long), with a minimum of sizeof(long). 246280304Sjkim * The check for a NULL pointer is just a convenience, probably never used. 247280304Sjkim * The case sa_len == 0 should only apply to empty structures. 248280304Sjkim */ 249280304Sjkim#define SA_SIZE(sa) \ 250280304Sjkim ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 251280304Sjkim sizeof(long) : \ 252280304Sjkim 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) 253280304Sjkim#endif 254280304Sjkim 255280304Sjkimstatic void 25655714Skrispmsg_addrs(char *cp, int addrs) 25755714Skris{ 258280304Sjkim struct sockaddr *sa; 259306196Sjkim int i; 260306196Sjkim 261306196Sjkim if (addrs == 0) { 262280304Sjkim (void) putchar('\n'); 263280304Sjkim return; 264280304Sjkim } 265280304Sjkim printf("\nsockaddrs: "); 266280304Sjkim bprintf(stdout, addrs, addrnames); 267280304Sjkim putchar('\n'); 268306196Sjkim for (i = 1; i; i <<= 1) 269306196Sjkim if (i & addrs) { 270306196Sjkim sa = (struct sockaddr *)cp; 271306196Sjkim printf(" %s", routename(sa)); 272306196Sjkim cp += SA_SIZE(sa); 273306196Sjkim } 274306196Sjkim putchar('\n'); 275306196Sjkim} 276306196Sjkim 277306196Sjkimstatic const char * 278280304Sjkimether_sprintf(const uint8_t mac[6]) 279280304Sjkim{ 280306196Sjkim static char buf[32]; 281306196Sjkim 282306196Sjkim snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 283280304Sjkim mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 28455714Skris return buf; 285160814Ssimon} 286280304Sjkim 287280304Sjkimstatic void 288280304Sjkimprint_rtmsg(struct rt_msghdr *rtm, int msglen) 289280304Sjkim{ 290280304Sjkim struct if_msghdr *ifm; 291280304Sjkim struct if_announcemsghdr *ifan; 292280304Sjkim time_t now = time(NULL); 293280304Sjkim char *cnow = ctime(&now); 294280304Sjkim 295280304Sjkim if (rtm->rtm_version != RTM_VERSION) { 296280304Sjkim (void) printf("routing message version %d not understood\n", 297280304Sjkim rtm->rtm_version); 298280304Sjkim return; 29955714Skris } 300160814Ssimon switch (rtm->rtm_type) { 301280304Sjkim case RTM_IFINFO: 302280304Sjkim ifm = (struct if_msghdr *)rtm; 303280304Sjkim printf("%.19s RTM_IFINFO: if# %d, ", 30455714Skris cnow, ifm->ifm_index); 305160814Ssimon switch (ifm->ifm_data.ifi_link_state) { 306280304Sjkim case LINK_STATE_DOWN: 307280304Sjkim printf("link: down, flags:"); 308280304Sjkim break; 309160814Ssimon case LINK_STATE_UP: 310160814Ssimon printf("link: up, flags:"); 311280304Sjkim break; 312280304Sjkim default: 313160814Ssimon printf("link: unknown<%d>, flags:", 314280304Sjkim ifm->ifm_data.ifi_link_state); 315280304Sjkim break; 316280304Sjkim } 317280304Sjkim bprintf(stdout, ifm->ifm_flags, ifnetflags); 318280304Sjkim pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 319280304Sjkim fflush(stdout); 320280304Sjkim break; 321280304Sjkim case RTM_IFANNOUNCE: 322280304Sjkim ifan = (struct if_announcemsghdr *)rtm; 323280304Sjkim printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 324280304Sjkim cnow, ifan->ifan_index); 325280304Sjkim switch (ifan->ifan_what) { 326280304Sjkim case IFAN_ARRIVAL: 327280304Sjkim printf("arrival"); 328280304Sjkim break; 329280304Sjkim case IFAN_DEPARTURE: 330280304Sjkim printf("departure"); 331306196Sjkim break; 332280304Sjkim default: 333280304Sjkim printf("#%d", ifan->ifan_what); 334280304Sjkim break; 33555714Skris } 336238405Sjkim printf("\n"); 337280304Sjkim fflush(stdout); 338280304Sjkim break; 339280304Sjkim case RTM_IEEE80211: 340280304Sjkim#define V(type) ((struct type *)(&ifan[1])) 341280304Sjkim ifan = (struct if_announcemsghdr *)rtm; 342280304Sjkim printf("%.19s RTM_IEEE80211: if# %d, ", cnow, ifan->ifan_index); 343280304Sjkim switch (ifan->ifan_what) { 344280304Sjkim case RTM_IEEE80211_ASSOC: 345280304Sjkim printf("associate with %s", 346238405Sjkim ether_sprintf(V(ieee80211_join_event)->iev_addr)); 347238405Sjkim break; 348280304Sjkim case RTM_IEEE80211_REASSOC: 349280304Sjkim printf("reassociate with %s", 350280304Sjkim ether_sprintf(V(ieee80211_join_event)->iev_addr)); 351280304Sjkim break; 352280304Sjkim case RTM_IEEE80211_DISASSOC: 353280304Sjkim printf("disassociate"); 354280304Sjkim break; 355280304Sjkim case RTM_IEEE80211_JOIN: 356280304Sjkim case RTM_IEEE80211_REJOIN: 357280304Sjkim printf("%s station %sjoin", 358280304Sjkim ether_sprintf(V(ieee80211_join_event)->iev_addr), 359280304Sjkim ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "" 360280304Sjkim ); 36155714Skris break; 36255714Skris case RTM_IEEE80211_LEAVE: 363280304Sjkim printf("%s station leave", 364280304Sjkim ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 365280304Sjkim break; 36655714Skris case RTM_IEEE80211_SCAN: 367280304Sjkim printf("scan complete"); 368280304Sjkim break; 369280304Sjkim case RTM_IEEE80211_REPLAY: 370280304Sjkim printf("replay failure: src %s " 371280304Sjkim , ether_sprintf(V(ieee80211_replay_event)->iev_src) 372280304Sjkim ); 373306196Sjkim printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu" 374280304Sjkim , ether_sprintf(V(ieee80211_replay_event)->iev_dst) 375280304Sjkim , V(ieee80211_replay_event)->iev_cipher 376280304Sjkim , V(ieee80211_replay_event)->iev_keyix 377280304Sjkim , V(ieee80211_replay_event)->iev_keyrsc 378280304Sjkim , V(ieee80211_replay_event)->iev_rsc 37955714Skris ); 380280304Sjkim break; 381280304Sjkim case RTM_IEEE80211_MICHAEL: 382280304Sjkim printf("michael failure: src %s " 383280304Sjkim , ether_sprintf(V(ieee80211_michael_event)->iev_src) 384280304Sjkim ); 385280304Sjkim printf("dst %s cipher %u keyix %u" 386280304Sjkim , ether_sprintf(V(ieee80211_michael_event)->iev_dst) 387280304Sjkim , V(ieee80211_michael_event)->iev_cipher 388280304Sjkim , V(ieee80211_michael_event)->iev_keyix 389280304Sjkim ); 390280304Sjkim break; 391280304Sjkim case RTM_IEEE80211_WDS: 392280304Sjkim printf("%s wds discovery", 393280304Sjkim ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 39455714Skris break; 395194206Ssimon case RTM_IEEE80211_CSA: 396280304Sjkim printf("channel switch announcement: channel %u (%u MHz flags 0x%x) mode %d count %d" 397280304Sjkim , V(ieee80211_csa_event)->iev_ieee 398280304Sjkim , V(ieee80211_csa_event)->iev_freq 399280304Sjkim , V(ieee80211_csa_event)->iev_flags 400280304Sjkim , V(ieee80211_csa_event)->iev_mode 401280304Sjkim , V(ieee80211_csa_event)->iev_count 402194206Ssimon ); 40355714Skris break; 404280304Sjkim case RTM_IEEE80211_CAC: 405280304Sjkim printf("channel availability check " 406280304Sjkim "(channel %u, %u MHz flags 0x%x) " 40755714Skris , V(ieee80211_cac_event)->iev_ieee 40855714Skris , V(ieee80211_cac_event)->iev_freq 409280304Sjkim , V(ieee80211_cac_event)->iev_flags 410280304Sjkim ); 41155714Skris switch (V(ieee80211_cac_event)->iev_type) { 412280304Sjkim case IEEE80211_NOTIFY_CAC_START: 413280304Sjkim printf("start timer"); 414280304Sjkim break; 415280304Sjkim case IEEE80211_NOTIFY_CAC_STOP: 416280304Sjkim printf("stop timer"); 417280304Sjkim break; 418280304Sjkim case IEEE80211_NOTIFY_CAC_EXPIRE: 419280304Sjkim printf("timer expired"); 420280304Sjkim break; 421280304Sjkim case IEEE80211_NOTIFY_CAC_RADAR: 422280304Sjkim printf("radar detected"); 42355714Skris break; 42455714Skris default: 425280304Sjkim printf("unknown type %d", 426280304Sjkim V(ieee80211_cac_event)->iev_type); 427280304Sjkim break; 428280304Sjkim } 429280304Sjkim break; 430280304Sjkim case RTM_IEEE80211_DEAUTH: 431280304Sjkim printf("%s wds deauth", 43255714Skris ether_sprintf(V(ieee80211_deauth_event)->iev_addr)); 433280304Sjkim break; 434280304Sjkim case RTM_IEEE80211_AUTH: 435280304Sjkim printf("%s node authenticate", 436280304Sjkim ether_sprintf(V(ieee80211_auth_event)->iev_addr)); 437280304Sjkim break; 438280304Sjkim case RTM_IEEE80211_COUNTRY: 439280304Sjkim printf("%s adopt country code '%c%c'", 440238405Sjkim ether_sprintf(V(ieee80211_country_event)->iev_addr), 441280304Sjkim V(ieee80211_country_event)->iev_cc[0], 442280304Sjkim V(ieee80211_country_event)->iev_cc[1]); 44355714Skris break; 444280304Sjkim case RTM_IEEE80211_RADIO: 445280304Sjkim printf("radio %s", 446280304Sjkim V(ieee80211_radio_event)->iev_state ? "ON" : "OFF"); 447280304Sjkim break; 448280304Sjkim default: 449280304Sjkim printf("what: #%d", ifan->ifan_what); 450280304Sjkim break; 451280304Sjkim } 452280304Sjkim printf("\n"); 453280304Sjkim fflush(stdout); 45455714Skris break; 455160814Ssimon#undef V 456280304Sjkim } 457280304Sjkim} 45855714Skris