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 30 /* 31 * wlanstats [-i interface] 32 * (default interface is wlan0). 33 */ 34 35 #include <sys/param.h> 36 #include <sys/socket.h> 37 38 #include <net/ethernet.h> 39 #include <net80211/_ieee80211.h> 40 41 #include <err.h> 42 #include <signal.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <strings.h> 46 #include <unistd.h> 47 48 #include "wlanstats.h" 49 50 static struct { 51 const char *tag; 52 const char *fmt; 53 } tags[] = { 54 { "default", 55 "input,rx_mgmt,output,rx_badkeyid,scan_active,scan_bg,bmiss,rssi,noise,rate" 56 }, 57 { "ampdu", 58 "input,output,ampdu_reorder,ampdu_oor,rx_dup,ampdu_flush,ampdu_move," 59 "ampdu_drop,ampdu_bar,ampdu_baroow,ampdu_barmove,ampdu_bartx," 60 "ampdu_bartxfail,ampdu_bartxretry,rssi,rate" 61 }, 62 { 63 "amsdu", 64 "input,output,amsdu_tooshort,amsdu_split,amsdu_decap,amsdu_encap,rx_amsdu_more,rx_amsdu_more_end,rssi,rate" 65 }, 66 }; 67 68 static const char * 69 getfmt(const char *tag) 70 { 71 unsigned int i; 72 for (i = 0; i < nitems(tags); i++) 73 if (strcasecmp(tags[i].tag, tag) == 0) 74 return tags[i].fmt; 75 return tag; 76 } 77 78 static int signalled; 79 80 static void 81 catchalarm(int signo __unused) 82 { 83 signalled = 1; 84 } 85 86 #if 0 87 static void 88 print_sta_stats(FILE *fd, const u_int8_t macaddr[IEEE80211_ADDR_LEN]) 89 { 90 #define STAT(x,fmt) \ 91 if (ns->ns_##x) { fprintf(fd, "%s" #x " " fmt, sep, ns->ns_##x); sep = " "; } 92 struct ieee80211req ireq; 93 struct ieee80211req_sta_stats stats; 94 const struct ieee80211_nodestats *ns = &stats.is_stats; 95 const char *sep; 96 97 (void) memset(&ireq, 0, sizeof(ireq)); 98 (void) strncpy(ireq.i_name, ifr.ifr_name, sizeof(ireq.i_name)); 99 ireq.i_type = IEEE80211_IOC_STA_STATS; 100 ireq.i_data = &stats; 101 ireq.i_len = sizeof(stats); 102 memcpy(stats.is_u.macaddr, macaddr, IEEE80211_ADDR_LEN); 103 if (ioctl(s, SIOCG80211, &ireq) < 0) 104 err(1, "unable to get station stats for %s", 105 ether_ntoa((const struct ether_addr*) macaddr)); 106 107 fprintf(fd, "%s:\n", ether_ntoa((const struct ether_addr*) macaddr)); 108 109 sep = "\t"; 110 STAT(rx_data, "%u"); 111 STAT(rx_mgmt, "%u"); 112 STAT(rx_ctrl, "%u"); 113 STAT(rx_beacons, "%u"); 114 STAT(rx_proberesp, "%u"); 115 STAT(rx_ucast, "%u"); 116 STAT(rx_mcast, "%u"); 117 STAT(rx_bytes, "%llu"); 118 STAT(rx_dup, "%u"); 119 STAT(rx_noprivacy, "%u"); 120 STAT(rx_wepfail, "%u"); 121 STAT(rx_demicfail, "%u"); 122 STAT(rx_decap, "%u"); 123 STAT(rx_defrag, "%u"); 124 STAT(rx_disassoc, "%u"); 125 STAT(rx_deauth, "%u"); 126 STAT(rx_decryptcrc, "%u"); 127 STAT(rx_unauth, "%u"); 128 STAT(rx_unencrypted, "%u"); 129 fprintf(fd, "\n"); 130 131 sep = "\t"; 132 STAT(tx_data, "%u"); 133 STAT(tx_mgmt, "%u"); 134 STAT(tx_probereq, "%u"); 135 STAT(tx_ucast, "%u"); 136 STAT(tx_mcast, "%u"); 137 STAT(tx_bytes, "%llu"); 138 STAT(tx_novlantag, "%u"); 139 STAT(tx_vlanmismatch, "%u"); 140 fprintf(fd, "\n"); 141 142 sep = "\t"; 143 STAT(tx_assoc, "%u"); 144 STAT(tx_assoc_fail, "%u"); 145 STAT(tx_auth, "%u"); 146 STAT(tx_auth_fail, "%u"); 147 STAT(tx_deauth, "%u"); 148 STAT(tx_deauth_code, "%llu"); 149 STAT(tx_disassoc, "%u"); 150 STAT(tx_disassoc_code, "%u"); 151 fprintf(fd, "\n"); 152 153 #undef STAT 154 } 155 #endif 156 157 static void 158 usage(void) 159 { 160 161 printf("wlanstats: [-ah] [-i ifname] [-l] [-m station MAC address] [-o fmt] [interval]\n"); 162 } 163 164 int 165 main(int argc, char *argv[]) 166 { 167 struct wlanstatfoo *wf; 168 struct ether_addr *ea; 169 const uint8_t *mac = NULL; 170 const char *ifname; 171 #if 0 172 int allnodes = 0; 173 #endif 174 int c, mode; 175 176 ifname = getenv("WLAN"); 177 if (ifname == NULL) 178 ifname = "wlan0"; 179 wf = wlanstats_new(ifname, getfmt("default")); 180 #if 0 181 while ((c = getopt(argc, argv, "ahi:lm:o:")) != -1) { 182 #else 183 while ((c = getopt(argc, argv, "hi:lm:o:")) != -1) { 184 #endif 185 switch (c) { 186 #if 0 187 case 'a': 188 allnodes++; 189 break; 190 #endif 191 case 'h': 192 usage(); 193 exit(0); 194 case 'i': 195 wf->setifname(wf, optarg); 196 break; 197 case 'l': 198 wf->print_fields(wf, stdout); 199 return 0; 200 case 'm': 201 ea = ether_aton(optarg); 202 if (!ea) 203 errx(1, "%s: invalid ethernet address", optarg); 204 mac = ea->octet; 205 break; 206 case 'o': 207 wf->setfmt(wf, getfmt(optarg)); 208 break; 209 default: 210 usage(); 211 exit(1); 212 /*NOTREACHED*/ 213 } 214 } 215 argc -= optind; 216 argv += optind; 217 218 mode = wf->getopmode(wf); 219 wf->setstamac(wf, mac); 220 221 if (argc > 0) { 222 u_long interval = strtoul(argv[0], NULL, 0); 223 int line, omask; 224 225 if (interval < 1) 226 interval = 1; 227 signal(SIGALRM, catchalarm); 228 signalled = 0; 229 alarm(interval); 230 banner: 231 wf->print_header(wf, stdout); 232 line = 0; 233 loop: 234 if (line != 0) { 235 wf->collect_cur(wf); 236 wf->print_current(wf, stdout); 237 wf->update_tot(wf); 238 } else { 239 wf->collect_tot(wf); 240 wf->print_total(wf, stdout); 241 } 242 fflush(stdout); 243 omask = sigblock(sigmask(SIGALRM)); 244 if (!signalled) 245 sigpause(0); 246 sigsetmask(omask); 247 signalled = 0; 248 alarm(interval); 249 line++; 250 /* refresh every display in case sta roams */ 251 if (mac == NULL && mode == IEEE80211_M_STA) 252 wf->setstamac(wf, NULL); 253 if (line == 21) /* XXX tty line count */ 254 goto banner; 255 else 256 goto loop; 257 /*NOTREACHED*/ 258 #if 0 259 } else if (allnodes) { 260 struct ieee80211req_sta_info *si; 261 union { 262 struct ieee80211req_sta_req req; 263 uint8_t buf[24*1024]; 264 } u; 265 uint8_t *cp; 266 struct ieee80211req ireq; 267 int len; 268 269 /* 270 * Retrieve station/neighbor table and print stats for each. 271 */ 272 (void) memset(&ireq, 0, sizeof(ireq)); 273 (void) strncpy(ireq.i_name, ifr.ifr_name, sizeof(ireq.i_name)); 274 ireq.i_type = IEEE80211_IOC_STA_INFO; 275 memset(&u.req.macaddr, 0xff, sizeof(u.req.macaddr)); 276 ireq.i_data = &u; 277 ireq.i_len = sizeof(u); 278 if (ioctl(s, SIOCG80211, &ireq) < 0) 279 err(1, "unable to get station information"); 280 len = ireq.i_len; 281 if (len >= sizeof(struct ieee80211req_sta_info)) { 282 cp = u.req.info; 283 do { 284 si = (struct ieee80211req_sta_info *) cp; 285 if (si->isi_len < sizeof(*si)) 286 break; 287 print_sta_stats(stdout, si->isi_macaddr); 288 cp += si->isi_len, len -= si->isi_len; 289 } while (len >= sizeof(struct ieee80211req_sta_info)); 290 } 291 #endif 292 } else { 293 wf->collect_tot(wf); 294 wf->print_verbose(wf, stdout); 295 } 296 return 0; 297 } 298