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