diff --git a/ratestats.c b/ratestats.c new file mode 100644 index 000000000000..fd54092bfef2 --- /dev/null +++ b/ratestats.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include "iw.h" + +SECTION(ratestats); + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int print_event(struct nl_msg *msg, void *_dummy) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *r[NUM_NL80211_ATTR_RATE_STATS]; + char ifname[100]; + char macbuf[6*3]; + struct nlattr *a; + int rem; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) { + if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); + printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY])); + } else if (tb[NL80211_ATTR_WDEV] && tb[NL80211_ATTR_WIPHY]) { + printf("wdev 0x%llx (phy #%d): ", + (unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV]), + nla_get_u32(tb[NL80211_ATTR_WIPHY])); + } else if (tb[NL80211_ATTR_IFINDEX]) { + if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); + printf("%s: ", ifname); + } else if (tb[NL80211_ATTR_WDEV]) { + printf("wdev 0x%llx: ", (unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV])); + } else if (tb[NL80211_ATTR_WIPHY]) { + printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY])); + } + + if (gnlh->cmd != NL80211_CMD_GET_RATE_STATISTICS) + return NL_SKIP; + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_RATE_STATS]) + return NL_SKIP; + + mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); + nla_parse(r, NL80211_ATTR_RATE_STATS_MAX, + nla_data(tb[NL80211_ATTR_RATE_STATS]), + nla_len(tb[NL80211_ATTR_RATE_STATS]), + NULL); + if (!r[NL80211_ATTR_RATE_STATS_RATE]) + return NL_SKIP; + if (!r[NL80211_ATTR_RATE_STATS_STATS]) + return NL_SKIP; + + printf("data for %s\n", macbuf); + nla_for_each_nested(a, r[NL80211_ATTR_RATE_STATS_RATE], rem) { + if (a->nla_type == NL80211_RATE_INFO_BITRATE32) + printf("\trate = %u\n", nla_get_u32(a)); + } + nla_for_each_nested(a, r[NL80211_ATTR_RATE_STATS_STATS], rem) { + if (a->nla_type == NL80211_TID_STATS_RX_MSDU) + printf("\trx_msdu = %lu\n", nla_get_u64(a)); + } + + fflush(stdout); + return NL_SKIP; +} + +static int __prepare_listen_ratestats(struct nl80211_state *state) +{ + int mcid; + + mcid = nl_get_multicast_id(state->nl_sock, "nl80211", NL80211_MULTICAST_GROUP_RATESTATS); + if (mcid < 0) + return mcid; + + return nl_socket_add_membership(state->nl_sock, mcid); +} + +static __u32 __do_listen_ratestats(struct nl80211_state *state) +{ + struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + return -ENOMEM; + } + + /* no sequence checking for multicast messages */ + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, NULL); + + while (true) + nl_recvmsgs(state->nl_sock, cb); + + nl_cb_put(cb); + + return 0; +} + +static int print_events(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + int ret; + + if (argc != 2) + return 1; + + ret = __prepare_listen_ratestats(state); + if (ret) + return ret; + + return __do_listen_ratestats(state); +} +COMMAND(ratestats, monitor, "", 0, 0, CIB_NONE, print_events, + "Monitor ratestats events from the kernel.\n"); + +static int get_ratestats(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + return 0; +} +COMMAND(ratestats, dump, "", NL80211_CMD_GET_RATE_STATISTICS, 0, CIB_NONE, get_ratestats, + "Monitor ratestats events from the kernel.\n");