#include "opt_ah.h"
#include <sys/types.h>
#include <sys/file.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_media.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <curses.h>
#include "ah.h"
#include "ah_desc.h"
#include "net80211/ieee80211_ioctl.h"
#include "net80211/ieee80211_radiotap.h"
#include "if_athioctl.h"
#include "if_athrate.h"
#include "ath_rate/sample/sample.h"
static int do_loop = 0;
#define STATS_BUF_SIZE 65536
#define PRINTMSG(...) do { \
if (do_loop == 0) \
printf(__VA_ARGS__); \
else \
printw(__VA_ARGS__); \
} while (0)
#define PRINTATTR_ON(_x) do { \
if (do_loop) \
attron(_x); \
} while(0)
#define PRINTATTR_OFF(_x) do { \
if (do_loop) \
attroff(_x); \
} while(0)
struct ath_ratestats {
int s;
struct ath_rateioctl re;
};
static inline int
dot11rate(struct ath_rateioctl_rt *rt, int rix)
{
if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
else
return (rt->ratecode[rix] / 2);
}
static const char *
dot11str(struct ath_rateioctl_rt *rt, int rix)
{
if (rix == -1)
return "";
else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
return "MCS";
else
return " Mb";
}
static void
ath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
struct sample_node *sn)
{
uint64_t mask;
int rix, y;
PRINTMSG("static_rix (%d) ratemask 0x%llx\n",
sn->static_rix,
(long long) sn->ratemask);
for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
PRINTMSG("[%4u] cur rate %d %s since switch: "
"packets %d ticks %u ",
bin_to_size(y),
dot11rate(rt, sn->current_rix[y]),
dot11str(rt, sn->current_rix[y]),
sn->packets_since_switch[y],
sn->ticks_since_switch[y]);
PRINTMSG("last sample (%d %s) cur sample (%d %s) "
"packets sent %d ",
dot11rate(rt, sn->last_sample_rix[y]),
dot11str(rt, sn->last_sample_rix[y]),
dot11rate(rt, sn->current_sample_rix[y]),
dot11str(rt, sn->current_sample_rix[y]),
sn->packets_sent[y]);
PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
PRINTMSG("packets since sample %d sample tt %u\n",
sn->packets_since_sample[y],
sn->sample_tt[y]);
PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
}
PRINTMSG(" TX Rate TXTOTAL:TXOK EWMA T/ F"
" avg last xmit ");
PRINTMSG(" TX Rate TXTOTAL:TXOK EWMA T/ F"
" avg last xmit\n");
for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
int c = 0;
if ((mask & 1) == 0)
continue;
for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
if (sn->stats[y][rix].total_packets == 0)
continue;
if (rix == sn->current_rix[y])
PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
else if (rix == sn->last_sample_rix[y])
PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
#if 0
else if (sn->stats[y][rix].ewma_pct / 10 < 50)
PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD);
else if (sn->stats[y][rix].ewma_pct / 10 < 75)
PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
#endif
PRINTMSG("[%2u %s:%5u] %8ju:%-8ju "
"(%3d.%1d%%) %8ju/%4d %5uuS %u ",
dot11rate(rt, rix),
dot11str(rt, rix),
bin_to_size(y),
(uintmax_t) sn->stats[y][rix].total_packets,
(uintmax_t) sn->stats[y][rix].packets_acked,
sn->stats[y][rix].ewma_pct / 10,
sn->stats[y][rix].ewma_pct % 10,
(uintmax_t) sn->stats[y][rix].tries,
sn->stats[y][rix].successive_failures,
sn->stats[y][rix].average_tx_time,
sn->stats[y][rix].last_tx);
if (rix == sn->current_rix[y])
PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
else if (rix == sn->last_sample_rix[y])
PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
#if 0
else if (sn->stats[y][rix].ewma_pct / 10 < 50)
PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD);
else if (sn->stats[y][rix].ewma_pct / 10 < 75)
PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
#endif
c++;
if (c == 2) {
PRINTMSG("\n");
c = 0;
}
}
if (c != 0)
PRINTMSG("\n");
}
}
static void
ath_setifname(struct ath_ratestats *r, const char *ifname)
{
strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
}
static void
ath_setsta(struct ath_ratestats *r, uint8_t *mac)
{
memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
}
static void
ath_rate_ioctl(struct ath_ratestats *r)
{
if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
err(1, "ioctl");
}
static int
rate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
{
struct ath_rateioctl_tlv *av;
struct sample_node *sn = NULL;
struct ath_rateioctl_rt *rt = NULL;
int error = 0;
uint8_t *buf = (uint8_t *) r->re.buf;
av = (struct ath_rateioctl_tlv *) buf;
if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
"expected 0x%x\n",
av->tlv_id,
ATH_RATE_TLV_RATETABLE);
exit(127);
}
if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
fprintf(stderr, "unexpected TLV len (got %d bytes, "
"expected %d bytes\n",
av->tlv_len,
(int) sizeof(struct ath_rateioctl_rt));
exit(127);
}
rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));
av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
sizeof(struct ath_rateioctl_rt));
if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
"expected 0x%x\n",
av->tlv_id,
ATH_RATE_TLV_SAMPLENODE);
exit(127);
}
if (av->tlv_len != sizeof(struct sample_node)) {
fprintf(stderr, "unexpected TLV len (got %d bytes, "
"expected %d bytes\n",
av->tlv_len,
(int) sizeof(struct sample_node));
exit(127);
}
sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
sizeof(struct ath_rateioctl_rt) +
sizeof(struct ath_rateioctl_tlv));
ath_sample_stats(r, rt, sn);
return (0);
}
static void
fetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e,
uint8_t *buf)
{
memset(buf, '\0', STATS_BUF_SIZE);
ath_setsta(r, e->octet);
ath_rate_ioctl(r);
rate_node_stats(r, e);
}
int
main(int argc, char *argv[])
{
char const *ifname = NULL, *macaddr = NULL;
int c;
int do_all = 0;
struct ether_addr *e;
struct ath_ratestats r;
uint8_t *buf;
useconds_t sleep_period;
float f;
short cf, cb;
ifname = getenv("ATH");
if (ifname == NULL)
ifname = ATH_DEFAULT;
while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'i':
ifname = optarg;
break;
case 'm':
macaddr = optarg;
break;
case 's':
sscanf(optarg, "%f", &f);
do_loop = 1;
sleep_period = (useconds_t) (f * 1000000.0);
break;
default:
errx(1,
"usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n",
argv[0]);
}
}
if (macaddr == NULL) {
errx(1, "%s: macaddress wasn't supplied and no -a given\n",
argv[0]);
}
e = ether_aton(macaddr);
if (e == NULL)
err(1, "ether_aton");
bzero(&r, sizeof(r));
buf = malloc(STATS_BUF_SIZE);
if (buf == NULL)
err(1, "calloc");
r.re.buf = (char *) buf;
r.re.len = STATS_BUF_SIZE;
r.s = socket(AF_INET, SOCK_DGRAM, 0);
if (r.s < 0) {
err(1, "socket");
}
ath_setifname(&r, ifname);
if (do_loop) {
initscr();
start_color();
use_default_colors();
pair_content(0, &cf, &cb);
init_pair(1, COLOR_YELLOW, cb);
init_pair(2, COLOR_RED, cb);
init_pair(3, COLOR_CYAN, cb);
init_pair(4, COLOR_BLUE, cb);
init_pair(5, COLOR_MAGENTA, cb);
cbreak();
noecho();
nonl();
nodelay(stdscr, 1);
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
while (1) {
clear();
move(0, 0);
fetch_and_print_stats(&r, e, buf);
refresh();
usleep(sleep_period);
}
} else
fetch_and_print_stats(&r, e, buf);
exit(0);
}