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