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