1 /*- 2 * Copyright (c) 2002, 2003 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 * 3. Neither the names of the above-listed copyright holders nor the names 16 * of any contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * Alternatively, this software may be distributed under the terms of the 20 * GNU General Public License ("GPL") version 2 as published by the Free 21 * Software Foundation. 22 * 23 * NO WARRANTY 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 28 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 29 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 32 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 34 * THE POSSIBILITY OF SUCH DAMAGES. 35 * 36 * $FreeBSD$ 37 */ 38 39 /* 40 * Monitor 802.11 events using a routing socket. 41 * Code liberaly swiped from route(8). 42 */ 43 #include <sys/param.h> 44 #include <sys/file.h> 45 #include <sys/socket.h> 46 #include <sys/ioctl.h> 47 #include <sys/sysctl.h> 48 #include <sys/types.h> 49 50 #include <net/if.h> 51 #include <net/route.h> 52 #include <net/if_dl.h> 53 #include <netinet/in.h> 54 #include <netinet/if_ether.h> 55 #include <netatalk/at.h> 56 #ifdef __NetBSD__ 57 #include <net80211/ieee80211_netbsd.h> 58 #elif __FreeBSD__ 59 #include <net80211/ieee80211_freebsd.h> 60 #else 61 #error "No support for your operating system!" 62 #endif 63 #include <arpa/inet.h> 64 #include <netdb.h> 65 66 #include <ctype.h> 67 #include <err.h> 68 #include <errno.h> 69 #include <paths.h> 70 #include <stdio.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <sysexits.h> 74 #include <unistd.h> 75 #include <ifaddrs.h> 76 77 static void print_rtmsg(struct rt_msghdr *rtm, int msglen); 78 79 int nflag = 0; 80 81 int 82 main(int argc, char *argv[]) 83 { 84 int n, s; 85 char msg[2048]; 86 87 s = socket(PF_ROUTE, SOCK_RAW, 0); 88 if (s < 0) 89 err(EX_OSERR, "socket"); 90 for(;;) { 91 n = read(s, msg, 2048); 92 print_rtmsg((struct rt_msghdr *)msg, n); 93 } 94 return 0; 95 } 96 97 static void 98 bprintf(fp, b, s) 99 FILE *fp; 100 int b; 101 u_char *s; 102 { 103 int i; 104 int gotsome = 0; 105 106 if (b == 0) 107 return; 108 while ((i = *s++) != 0) { 109 if (b & (1 << (i-1))) { 110 if (gotsome == 0) 111 i = '<'; 112 else 113 i = ','; 114 (void) putc(i, fp); 115 gotsome = 1; 116 for (; (i = *s) > 32; s++) 117 (void) putc(i, fp); 118 } else 119 while (*s > 32) 120 s++; 121 } 122 if (gotsome) 123 putc('>', fp); 124 } 125 126 char metricnames[] = 127 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" 128 "\1mtu"; 129 char routeflags[] = 130 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" 131 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016" 132 "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE" 133 "\025PINNED\026LOCAL\027BROADCAST\030MULTICAST"; 134 char ifnetflags[] = 135 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" 136 "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" 137 "\017LINK2\020MULTICAST"; 138 char addrnames[] = 139 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; 140 141 const char * 142 routename(sa) 143 struct sockaddr *sa; 144 { 145 char *cp; 146 static char line[MAXHOSTNAMELEN + 1]; 147 struct hostent *hp; 148 static char domain[MAXHOSTNAMELEN + 1]; 149 static int first = 1, n; 150 151 if (first) { 152 first = 0; 153 if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 154 (cp = strchr(domain, '.'))) { 155 domain[MAXHOSTNAMELEN] = '\0'; 156 (void) strcpy(domain, cp + 1); 157 } else 158 domain[0] = 0; 159 } 160 161 if (sa->sa_len == 0) 162 strcpy(line, "default"); 163 else switch (sa->sa_family) { 164 165 case AF_INET: 166 { struct in_addr in; 167 in = ((struct sockaddr_in *)sa)->sin_addr; 168 169 cp = 0; 170 if (in.s_addr == INADDR_ANY || sa->sa_len < 4) 171 cp = "default"; 172 if (cp == 0 && !nflag) { 173 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), 174 AF_INET); 175 if (hp) { 176 if ((cp = strchr(hp->h_name, '.')) && 177 !strcmp(cp + 1, domain)) 178 *cp = 0; 179 cp = hp->h_name; 180 } 181 } 182 if (cp) { 183 strncpy(line, cp, sizeof(line) - 1); 184 line[sizeof(line) - 1] = '\0'; 185 } else 186 (void) sprintf(line, "%s", inet_ntoa(in)); 187 break; 188 } 189 190 #ifdef INET6 191 case AF_INET6: 192 { 193 struct sockaddr_in6 sin6; /* use static var for safety */ 194 int niflags = 0; 195 #ifdef NI_WITHSCOPEID 196 niflags = NI_WITHSCOPEID; 197 #endif 198 199 memset(&sin6, 0, sizeof(sin6)); 200 memcpy(&sin6, sa, sa->sa_len); 201 sin6.sin6_len = sizeof(struct sockaddr_in6); 202 sin6.sin6_family = AF_INET6; 203 #ifdef __KAME__ 204 if (sa->sa_len == sizeof(struct sockaddr_in6) && 205 (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 206 IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && 207 sin6.sin6_scope_id == 0) { 208 sin6.sin6_scope_id = 209 ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]); 210 sin6.sin6_addr.s6_addr[2] = 0; 211 sin6.sin6_addr.s6_addr[3] = 0; 212 } 213 #endif 214 if (nflag) 215 niflags |= NI_NUMERICHOST; 216 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 217 line, sizeof(line), NULL, 0, niflags) != 0) 218 strncpy(line, "invalid", sizeof(line)); 219 220 return(line); 221 } 222 #endif 223 224 case AF_LINK: 225 return (link_ntoa((struct sockaddr_dl *)sa)); 226 227 default: 228 { u_short *s = (u_short *)sa; 229 u_short *slim = s + ((sa->sa_len + 1) >> 1); 230 char *cp = line + sprintf(line, "(%d)", sa->sa_family); 231 char *cpe = line + sizeof(line); 232 233 while (++s < slim && cp < cpe) /* start with sa->sa_data */ 234 if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0) 235 cp += n; 236 else 237 *cp = '\0'; 238 break; 239 } 240 } 241 return (line); 242 } 243 244 #ifndef SA_SIZE 245 /* 246 * This macro returns the size of a struct sockaddr when passed 247 * through a routing socket. Basically we round up sa_len to 248 * a multiple of sizeof(long), with a minimum of sizeof(long). 249 * The check for a NULL pointer is just a convenience, probably never used. 250 * The case sa_len == 0 should only apply to empty structures. 251 */ 252 #define SA_SIZE(sa) \ 253 ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ 254 sizeof(long) : \ 255 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) 256 #endif 257 258 static void 259 pmsg_addrs(char *cp, int addrs) 260 { 261 struct sockaddr *sa; 262 int i; 263 264 if (addrs == 0) { 265 (void) putchar('\n'); 266 return; 267 } 268 printf("\nsockaddrs: "); 269 bprintf(stdout, addrs, addrnames); 270 putchar('\n'); 271 for (i = 1; i; i <<= 1) 272 if (i & addrs) { 273 sa = (struct sockaddr *)cp; 274 printf(" %s", routename(sa)); 275 cp += SA_SIZE(sa); 276 } 277 putchar('\n'); 278 fflush(stdout); 279 } 280 281 static const char * 282 ether_sprintf(const uint8_t mac[6]) 283 { 284 static char buf[32]; 285 286 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 287 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 288 return buf; 289 } 290 291 static void 292 print_rtmsg(struct rt_msghdr *rtm, int msglen) 293 { 294 struct if_msghdr *ifm; 295 struct if_announcemsghdr *ifan; 296 char *state; 297 time_t now = time(NULL); 298 char *cnow = ctime(&now); 299 300 if (rtm->rtm_version != RTM_VERSION) { 301 (void) printf("routing message version %d not understood\n", 302 rtm->rtm_version); 303 return; 304 } 305 switch (rtm->rtm_type) { 306 case RTM_IFINFO: 307 ifm = (struct if_msghdr *)rtm; 308 printf("%.19s RTM_IFINFO: if# %d, ", 309 cnow, ifm->ifm_index); 310 switch (ifm->ifm_data.ifi_link_state) { 311 case LINK_STATE_DOWN: 312 state = "down"; 313 break; 314 case LINK_STATE_UP: 315 state = "up"; 316 break; 317 default: 318 state = "unknown"; 319 break; 320 } 321 printf("link: %s, flags:", state); 322 bprintf(stdout, ifm->ifm_flags, ifnetflags); 323 pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); 324 break; 325 case RTM_IFANNOUNCE: 326 ifan = (struct if_announcemsghdr *)rtm; 327 printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 328 cnow, ifan->ifan_index); 329 switch (ifan->ifan_what) { 330 case IFAN_ARRIVAL: 331 printf("arrival"); 332 break; 333 case IFAN_DEPARTURE: 334 printf("departure"); 335 break; 336 default: 337 printf("#%d", ifan->ifan_what); 338 break; 339 } 340 printf("\n"); 341 break; 342 case RTM_IEEE80211: 343 #define V(type) ((struct type *)(&ifan[1])) 344 ifan = (struct if_announcemsghdr *)rtm; 345 printf("%.19s RTM_IEEE80211: ", cnow); 346 switch (ifan->ifan_what) { 347 case RTM_IEEE80211_ASSOC: 348 printf("associate with %s", 349 ether_sprintf(V(ieee80211_join_event)->iev_addr)); 350 break; 351 case RTM_IEEE80211_REASSOC: 352 printf("reassociate with %s", 353 ether_sprintf(V(ieee80211_join_event)->iev_addr)); 354 break; 355 case RTM_IEEE80211_DISASSOC: 356 printf("disassociate"); 357 break; 358 case RTM_IEEE80211_JOIN: 359 case RTM_IEEE80211_REJOIN: 360 printf("%s station %sjoin", 361 ether_sprintf(V(ieee80211_join_event)->iev_addr), 362 ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "" 363 ); 364 break; 365 case RTM_IEEE80211_LEAVE: 366 printf("%s station leave", 367 ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 368 break; 369 case RTM_IEEE80211_SCAN: 370 printf("scan complete"); 371 break; 372 case RTM_IEEE80211_REPLAY: 373 printf("replay failure: src %s " 374 , ether_sprintf(V(ieee80211_replay_event)->iev_src) 375 ); 376 printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu" 377 , ether_sprintf(V(ieee80211_replay_event)->iev_dst) 378 , V(ieee80211_replay_event)->iev_cipher 379 , V(ieee80211_replay_event)->iev_keyix 380 , V(ieee80211_replay_event)->iev_keyrsc 381 , V(ieee80211_replay_event)->iev_rsc 382 ); 383 break; 384 case RTM_IEEE80211_MICHAEL: 385 printf("michael failure: src %s " 386 , ether_sprintf(V(ieee80211_michael_event)->iev_src) 387 ); 388 printf("dst %s cipher %u keyix %u" 389 , ether_sprintf(V(ieee80211_michael_event)->iev_dst) 390 , V(ieee80211_michael_event)->iev_cipher 391 , V(ieee80211_michael_event)->iev_keyix 392 ); 393 break; 394 default: 395 printf("if# %d, what: #%d", 396 ifan->ifan_index, ifan->ifan_what); 397 break; 398 } 399 printf("\n"); 400 break; 401 #undef V 402 } 403 } 404