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