1 /*- 2 * Copyright (c) 2012, Adrian Chadd. 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$ 30 */ 31 32 #include "opt_ah.h" 33 34 #include <sys/types.h> 35 #include <sys/file.h> 36 #include <sys/sockio.h> 37 #include <sys/socket.h> 38 #include <net/ethernet.h> 39 #include <net/if.h> 40 #include <net/if_media.h> 41 #include <net/if_var.h> 42 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <stdint.h> 46 #include <signal.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <err.h> 50 51 #include <curses.h> 52 53 #include "ah.h" 54 #include "ah_desc.h" 55 #include "net80211/ieee80211_ioctl.h" 56 #include "net80211/ieee80211_radiotap.h" 57 #include "if_athioctl.h" 58 #include "if_athrate.h" 59 60 #include "ath_rate/sample/sample.h" 61 62 static int do_loop = 0; 63 64 /* 65 * This needs to be big enough to fit the two TLVs, the rate table 66 * and the rate statistics table for a single node. 67 */ 68 #define STATS_BUF_SIZE 8192 69 70 #define PRINTMSG(...) do { \ 71 if (do_loop == 0) \ 72 printf(__VA_ARGS__); \ 73 else \ 74 printw(__VA_ARGS__); \ 75 } while (0) 76 77 #define PRINTATTR_ON(_x) do { \ 78 if (do_loop) \ 79 attron(_x); \ 80 } while(0) 81 82 83 #define PRINTATTR_OFF(_x) do { \ 84 if (do_loop) \ 85 attroff(_x); \ 86 } while(0) 87 88 struct ath_ratestats { 89 int s; 90 struct ath_rateioctl re; 91 }; 92 93 static inline int 94 dot11rate(struct ath_rateioctl_rt *rt, int rix) 95 { 96 97 if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 98 return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS); 99 else 100 return (rt->ratecode[rix] / 2); 101 } 102 103 static const char * 104 dot11str(struct ath_rateioctl_rt *rt, int rix) 105 { 106 if (rix == -1) 107 return ""; 108 else if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 109 return "MCS"; 110 else 111 return " Mb"; 112 } 113 114 static void 115 ath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt, 116 struct sample_node *sn) 117 { 118 uint32_t mask; 119 int rix, y; 120 121 PRINTMSG("static_rix (%d) ratemask 0x%llx\n", 122 sn->static_rix, 123 (long long) sn->ratemask); 124 125 for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 126 PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 127 PRINTMSG("[%4u] cur rate %d %s since switch: " 128 "packets %d ticks %u\n", 129 bin_to_size(y), 130 dot11rate(rt, sn->current_rix[y]), 131 dot11str(rt, sn->current_rix[y]), 132 sn->packets_since_switch[y], 133 sn->ticks_since_switch[y]); 134 135 PRINTMSG("[%4u] last sample (%d %s) cur sample (%d %s) " 136 "packets sent %d\n", 137 bin_to_size(y), 138 dot11rate(rt, sn->last_sample_rix[y]), 139 dot11str(rt, sn->last_sample_rix[y]), 140 dot11rate(rt, sn->current_sample_rix[y]), 141 dot11str(rt, sn->current_sample_rix[y]), 142 sn->packets_sent[y]); 143 PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 144 145 PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 146 PRINTMSG("[%4u] packets since sample %d sample tt %u\n", 147 bin_to_size(y), 148 sn->packets_since_sample[y], 149 sn->sample_tt[y]); 150 PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 151 PRINTMSG("\n"); 152 } 153 PRINTMSG(" TX Rate TXTOTAL:TXOK EWMA T/ F" 154 " avg last xmit\n"); 155 for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { 156 if ((mask & 1) == 0) 157 continue; 158 for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 159 if (sn->stats[y][rix].total_packets == 0) 160 continue; 161 if (rix == sn->current_rix[y]) 162 PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 163 else if (rix == sn->last_sample_rix[y]) 164 PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 165 #if 0 166 else if (sn->stats[y][rix].ewma_pct / 10 < 50) 167 PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD); 168 else if (sn->stats[y][rix].ewma_pct / 10 < 75) 169 PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD); 170 #endif 171 PRINTMSG("[%2u %s:%4u] %8ju:%-8ju " 172 "(%3d.%1d%%) %8ju/%4d %5uuS %u\n", 173 dot11rate(rt, rix), 174 dot11str(rt, rix), 175 bin_to_size(y), 176 (uintmax_t) sn->stats[y][rix].total_packets, 177 (uintmax_t) sn->stats[y][rix].packets_acked, 178 sn->stats[y][rix].ewma_pct / 10, 179 sn->stats[y][rix].ewma_pct % 10, 180 (uintmax_t) sn->stats[y][rix].tries, 181 sn->stats[y][rix].successive_failures, 182 sn->stats[y][rix].average_tx_time, 183 sn->stats[y][rix].last_tx); 184 if (rix == sn->current_rix[y]) 185 PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 186 else if (rix == sn->last_sample_rix[y]) 187 PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 188 #if 0 189 else if (sn->stats[y][rix].ewma_pct / 10 < 50) 190 PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD); 191 else if (sn->stats[y][rix].ewma_pct / 10 < 75) 192 PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD); 193 #endif 194 } 195 } 196 } 197 198 static void 199 ath_setifname(struct ath_ratestats *r, const char *ifname) 200 { 201 202 strncpy(r->re.if_name, ifname, sizeof (r->re.if_name)); 203 } 204 205 static void 206 ath_setsta(struct ath_ratestats *r, uint8_t *mac) 207 { 208 209 memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr)); 210 } 211 212 static void 213 ath_rate_ioctl(struct ath_ratestats *r) 214 { 215 216 if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0) 217 err(1, "ioctl"); 218 } 219 220 static int 221 rate_node_stats(struct ath_ratestats *r, struct ether_addr *e) 222 { 223 struct ath_rateioctl_tlv *av; 224 struct sample_node *sn = NULL; 225 struct ath_rateioctl_rt *rt = NULL; 226 int error = 0; 227 uint8_t *buf = (uint8_t *) r->re.buf; 228 229 /* 230 * For now, hard-code the TLV order and contents. Ew! 231 */ 232 av = (struct ath_rateioctl_tlv *) buf; 233 if (av->tlv_id != ATH_RATE_TLV_RATETABLE) { 234 fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 235 "expected 0x%x\n", 236 av->tlv_id, 237 ATH_RATE_TLV_RATETABLE); 238 exit(127); 239 } 240 if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) { 241 fprintf(stderr, "unexpected TLV len (got %d bytes, " 242 "expected %d bytes\n", 243 av->tlv_len, 244 (int) sizeof(struct ath_rateioctl_rt)); 245 exit(127); 246 } 247 rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv)); 248 249 /* Next */ 250 av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 251 sizeof(struct ath_rateioctl_rt)); 252 if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) { 253 fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 254 "expected 0x%x\n", 255 av->tlv_id, 256 ATH_RATE_TLV_SAMPLENODE); 257 exit(127); 258 } 259 if (av->tlv_len != sizeof(struct sample_node)) { 260 fprintf(stderr, "unexpected TLV len (got %d bytes, " 261 "expected %d bytes\n", 262 av->tlv_len, 263 (int) sizeof(struct sample_node)); 264 exit(127); 265 } 266 sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 267 sizeof(struct ath_rateioctl_rt) + 268 sizeof(struct ath_rateioctl_tlv)); 269 270 ath_sample_stats(r, rt, sn); 271 272 return (0); 273 } 274 275 static void 276 fetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e, 277 uint8_t *buf) 278 { 279 280 /* Zero the buffer before it's passed in */ 281 memset(buf, '\0', STATS_BUF_SIZE); 282 283 /* 284 * Set the station address for this lookup. 285 */ 286 ath_setsta(r, e->octet); 287 288 /* 289 * Fetch the data from the driver. 290 */ 291 ath_rate_ioctl(r); 292 293 /* 294 * Decode and parse statistics. 295 */ 296 rate_node_stats(r, e); 297 } 298 299 int 300 main(int argc, char *argv[]) 301 { 302 char const *ifname = NULL, *macaddr = NULL; 303 int c; 304 int do_all = 0; 305 struct ether_addr *e; 306 struct ath_ratestats r; 307 uint8_t *buf; 308 useconds_t sleep_period; 309 float f; 310 short cf, cb; 311 312 ifname = getenv("ATH"); 313 if (ifname == NULL) 314 ifname = "ath0"; 315 316 while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) { 317 switch (c) { 318 case 'a': 319 do_all = 1; 320 break; 321 case 'i': 322 ifname = optarg; 323 break; 324 case 'm': 325 macaddr = optarg; 326 break; 327 case 's': 328 sscanf(optarg, "%f", &f); 329 do_loop = 1; 330 sleep_period = (useconds_t) (f * 1000000.0); 331 break; 332 default: 333 errx(1, 334 "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n", 335 argv[0]); 336 /* NOTREACHED */ 337 } 338 } 339 340 if (macaddr == NULL) { 341 errx(1, "%s: macaddress wasn't supplied and no -a given\n", 342 argv[0]); 343 /* NOTREACHED */ 344 } 345 e = ether_aton(macaddr); 346 if (e == NULL) 347 err(1, "ether_aton"); 348 349 bzero(&r, sizeof(r)); 350 351 /* 352 * Persistent buffer for each lookup 353 */ 354 buf = malloc(STATS_BUF_SIZE); 355 if (buf == NULL) 356 err(1, "calloc"); 357 358 r.re.buf = (char *) buf; 359 r.re.len = STATS_BUF_SIZE; 360 361 r.s = socket(AF_INET, SOCK_DGRAM, 0); 362 if (r.s < 0) { 363 err(1, "socket"); 364 } 365 366 /* XXX error check */ 367 ath_setifname(&r, ifname); 368 369 if (do_loop) { 370 initscr(); 371 start_color(); 372 use_default_colors(); 373 pair_content(0, &cf, &cb); 374 /* Error - medium */ 375 init_pair(1, COLOR_YELLOW, cb); 376 /* Error - high */ 377 init_pair(2, COLOR_RED, cb); 378 /* Sample */ 379 init_pair(3, COLOR_CYAN, cb); 380 /* 250 byte frames */ 381 init_pair(4, COLOR_BLUE, cb); 382 /* 1600 byte frames */ 383 init_pair(5, COLOR_MAGENTA, cb); 384 cbreak(); 385 noecho(); 386 nonl(); 387 nodelay(stdscr, 1); 388 intrflush(stdscr, FALSE); 389 keypad(stdscr, TRUE); 390 391 while (1) { 392 clear(); 393 move(0, 0); 394 fetch_and_print_stats(&r, e, buf); 395 refresh(); 396 usleep(sleep_period); 397 } 398 } else 399 fetch_and_print_stats(&r, e, buf); 400 401 exit(0); 402 } 403