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
59 static 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
85 struct ath_ratestats {
86 int s;
87 struct ath_rateioctl re;
88 };
89
90 static inline int
dot11rate(struct ath_rateioctl_rt * rt,int rix)91 dot11rate(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
100 static const char *
dot11str(struct ath_rateioctl_rt * rt,int rix)101 dot11str(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
111 static void
ath_sample_stats(struct ath_ratestats * r,struct ath_rateioctl_rt * rt,struct sample_node * sn)112 ath_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
202 static void
ath_setifname(struct ath_ratestats * r,const char * ifname)203 ath_setifname(struct ath_ratestats *r, const char *ifname)
204 {
205
206 strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
207 }
208
209 static void
ath_setsta(struct ath_ratestats * r,uint8_t * mac)210 ath_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
216 static void
ath_rate_ioctl(struct ath_ratestats * r)217 ath_rate_ioctl(struct ath_ratestats *r)
218 {
219
220 if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
221 err(1, "ioctl");
222 }
223
224 static int
rate_node_stats(struct ath_ratestats * r,struct ether_addr * e)225 rate_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
279 static void
fetch_and_print_stats(struct ath_ratestats * r,struct ether_addr * e,uint8_t * buf)280 fetch_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
303 int
main(int argc,char * argv[])304 main(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