diff options
Diffstat (limited to 'event.c')
-rw-r--r-- | event.c | 829 |
1 files changed, 769 insertions, 60 deletions
@@ -2,6 +2,8 @@ #include <stdbool.h> #include <net/if.h> #include <errno.h> +#include <inttypes.h> +#include <time.h> #include "iw.h" static int no_seq_check(struct nl_msg *msg, void *arg) @@ -45,12 +47,14 @@ static void print_frame(struct print_event_args *args, struct nlattr *attr) { uint8_t *frame; size_t len; - int i; + unsigned int i; char macbuf[6*3]; uint16_t tmp; - if (!attr) + if (!attr) { printf(" [no frame]"); + return; + } frame = nla_data(attr); len = nla_len(attr); @@ -119,17 +123,20 @@ static void parse_cqm_event(struct nlattr **attrs) if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) { enum nl80211_cqm_rssi_threshold_event rssi_event; + int32_t rssi_level = -1; bool found_one = false; rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + if (cqm[NL80211_ATTR_CQM_RSSI_LEVEL]) + rssi_level = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_LEVEL]); switch (rssi_event) { case NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: - printf("RSSI went above threshold\n"); + printf("RSSI (%i dBm) went above threshold\n", rssi_level); found_one = true; break; case NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: - printf("RSSI went below threshold\n"); + printf("RSSI (%i dBm) went below threshold\n", rssi_level); found_one = true; break; case NL80211_CQM_RSSI_BEACON_LOSS_EVENT: @@ -242,9 +249,8 @@ static void parse_wowlan_wake_event(struct nlattr **attrs) nla_for_each_nested(match, tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS], rem_nst) { - nla_parse(tb_match, NUM_NL80211_ATTR, nla_data(match), - nla_len(match), - NULL); + nla_parse_nested(tb_match, NL80211_ATTR_MAX, match, + NULL); printf("\t\tSSID: \""); print_ssid_escaped(nla_len(tb_match[NL80211_ATTR_SSID]), nla_data(tb_match[NL80211_ATTR_SSID])); @@ -291,6 +297,617 @@ static void parse_wowlan_wake_event(struct nlattr **attrs) printf("\t* TCP connection ran out of tokens\n"); } +extern struct vendor_event *__start_vendor_event[]; +extern struct vendor_event *__stop_vendor_event; + +// Dummy to force the section to exist +VENDOR_EVENT(0xffffffff, 0xffffffff, NULL); + +static void parse_vendor_event(struct nlattr **attrs, bool dump) +{ + __u32 vendor_id, subcmd; + unsigned int i; + + if (!attrs[NL80211_ATTR_VENDOR_ID] || + !attrs[NL80211_ATTR_VENDOR_SUBCMD]) + return; + + vendor_id = nla_get_u32(attrs[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(attrs[NL80211_ATTR_VENDOR_SUBCMD]); + + printf("vendor event %.6x:%d", vendor_id, subcmd); + + for (i = 0; i < &__stop_vendor_event - __start_vendor_event; i++) { + struct vendor_event *ev = __start_vendor_event[i]; + + if (!ev) + continue; + + if (ev->vendor_id != vendor_id) + continue; + if (ev->subcmd != subcmd) + continue; + if (!ev->callback) + continue; + + ev->callback(vendor_id, subcmd, attrs[NL80211_ATTR_VENDOR_DATA]); + goto out; + } + + if (dump && attrs[NL80211_ATTR_VENDOR_DATA]) + iw_hexdump("vendor event", + nla_data(attrs[NL80211_ATTR_VENDOR_DATA]), + nla_len(attrs[NL80211_ATTR_VENDOR_DATA])); +out: + printf("\n"); +} + +static void parse_nan_term(struct nlattr **attrs) +{ + struct nlattr *func[NL80211_NAN_FUNC_ATTR_MAX + 1]; + + static struct nla_policy + nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = { + [NL80211_NAN_FUNC_TYPE] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_SERVICE_ID] = { }, + [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = { }, + [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 }, + [NL80211_NAN_FUNC_SERVICE_INFO] = { }, + [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8}, + }; + + if (!attrs[NL80211_ATTR_COOKIE]) { + printf("Bad NAN func termination format - cookie is missing\n"); + return; + } + + if (nla_parse_nested(func, NL80211_NAN_FUNC_ATTR_MAX, + attrs[NL80211_ATTR_NAN_FUNC], + nan_func_policy)) { + printf("NAN: failed to parse nan func\n"); + return; + } + + if (!func[NL80211_NAN_FUNC_INSTANCE_ID]) { + printf("Bad NAN func termination format-instance id missing\n"); + return; + } + + if (!func[NL80211_NAN_FUNC_TERM_REASON]) { + printf("Bad NAN func termination format - reason is missing\n"); + return; + } + printf("NAN(cookie=0x%llx): Termination event: id = %d, reason = ", + (long long int)nla_get_u64(attrs[NL80211_ATTR_COOKIE]), + nla_get_u8(func[NL80211_NAN_FUNC_INSTANCE_ID])); + switch (nla_get_u8(func[NL80211_NAN_FUNC_TERM_REASON])) { + case NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: + printf("user request\n"); + break; + case NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: + printf("expired\n"); + break; + case NL80211_NAN_FUNC_TERM_REASON_ERROR: + printf("error\n"); + break; + default: + printf("unknown\n"); + } +} + +static const char *ftm_fail_reason(unsigned int reason) +{ +#define FTM_FAIL_REASON(x) case NL80211_PMSR_FTM_FAILURE_##x: return #x + switch (reason) { + FTM_FAIL_REASON(UNSPECIFIED); + FTM_FAIL_REASON(NO_RESPONSE); + FTM_FAIL_REASON(REJECTED); + FTM_FAIL_REASON(WRONG_CHANNEL); + FTM_FAIL_REASON(PEER_NOT_CAPABLE); + FTM_FAIL_REASON(INVALID_TIMESTAMP); + FTM_FAIL_REASON(PEER_BUSY); + FTM_FAIL_REASON(BAD_CHANGED_PARAMS); + default: + return "unknown"; + } +} + +static void parse_pmsr_ftm_data(struct nlattr *data) +{ + struct nlattr *ftm[NL80211_PMSR_FTM_RESP_ATTR_MAX + 1]; + + printf(" FTM"); + nla_parse_nested(ftm, NL80211_PMSR_FTM_RESP_ATTR_MAX, data, NULL); + + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON]) { + printf(" failed: %s (%d)", + ftm_fail_reason(nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON])), + nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON])); + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME]) + printf(" retry after %us", + nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME])); + printf("\n"); + return; + } + + printf("\n"); + +#define PFTM(tp, attr, sign) \ + do { \ + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_##attr]) \ + printf(" " #attr ": %lld\n", \ + (sign long long)nla_get_##tp( \ + ftm[NL80211_PMSR_FTM_RESP_ATTR_##attr])); \ + } while (0) + + PFTM(u32, BURST_INDEX, unsigned); + PFTM(u32, NUM_FTMR_ATTEMPTS, unsigned); + PFTM(u32, NUM_FTMR_SUCCESSES, unsigned); + PFTM(u8, NUM_BURSTS_EXP, unsigned); + PFTM(u8, BURST_DURATION, unsigned); + PFTM(u8, FTMS_PER_BURST, unsigned); + PFTM(u32, RSSI_AVG, signed); + PFTM(u32, RSSI_SPREAD, unsigned); + PFTM(u64, RTT_AVG, signed); + PFTM(u64, RTT_VARIANCE, unsigned); + PFTM(u64, RTT_SPREAD, unsigned); + PFTM(u64, DIST_AVG, signed); + PFTM(u64, DIST_VARIANCE, unsigned); + PFTM(u64, DIST_SPREAD, unsigned); + + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_TX_RATE]) { + char buf[100]; + + parse_bitrate(ftm[NL80211_PMSR_FTM_RESP_ATTR_TX_RATE], + buf, sizeof(buf)); + printf(" TX bitrate: %s\n", buf); + } + + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_RX_RATE]) { + char buf[100]; + + parse_bitrate(ftm[NL80211_PMSR_FTM_RESP_ATTR_RX_RATE], + buf, sizeof(buf)); + printf(" RX bitrate: %s\n", buf); + } + + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_LCI]) + iw_hexdump(" LCI", + nla_data(ftm[NL80211_PMSR_FTM_RESP_ATTR_LCI]), + nla_len(ftm[NL80211_PMSR_FTM_RESP_ATTR_LCI])); + + if (ftm[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]) + iw_hexdump(" civic location", + nla_data(ftm[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]), + nla_len(ftm[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC])); +} + +static const char *pmsr_status(unsigned int status) +{ +#define PMSR_STATUS(x) case NL80211_PMSR_STATUS_##x: return #x + switch (status) { + PMSR_STATUS(SUCCESS); + PMSR_STATUS(REFUSED); + PMSR_STATUS(TIMEOUT); + PMSR_STATUS(FAILURE); + default: + return "unknown"; + } +#undef PMSR_STATUS +} + +static void parse_pmsr_peer(struct nlattr *peer) +{ + struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; + struct nlattr *resp[NL80211_PMSR_RESP_ATTR_MAX + 1]; + struct nlattr *data[NL80211_PMSR_TYPE_MAX + 1]; + char macbuf[6*3]; + int err; + + err = nla_parse_nested(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, NULL); + if (err) { + printf(" Peer: failed to parse!\n"); + return; + } + + if (!tb[NL80211_PMSR_PEER_ATTR_ADDR]) { + printf(" Peer: no MAC address\n"); + return; + } + + mac_addr_n2a(macbuf, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR])); + printf(" Peer %s:", macbuf); + + if (!tb[NL80211_PMSR_PEER_ATTR_RESP]) { + printf(" no response!\n"); + return; + } + + err = nla_parse_nested(resp, NL80211_PMSR_RESP_ATTR_MAX, + tb[NL80211_PMSR_PEER_ATTR_RESP], NULL); + if (err) { + printf(" failed to parse response!\n"); + return; + } + + if (resp[NL80211_PMSR_RESP_ATTR_STATUS]) + printf(" status=%d (%s)", + nla_get_u32(resp[NL80211_PMSR_RESP_ATTR_STATUS]), + pmsr_status(nla_get_u32(resp[NL80211_PMSR_RESP_ATTR_STATUS]))); + if (resp[NL80211_PMSR_RESP_ATTR_HOST_TIME]) + printf(" @%llu", + (unsigned long long)nla_get_u64(resp[NL80211_PMSR_RESP_ATTR_HOST_TIME])); + if (resp[NL80211_PMSR_RESP_ATTR_AP_TSF]) + printf(" tsf=%llu", + (unsigned long long)nla_get_u64(resp[NL80211_PMSR_RESP_ATTR_AP_TSF])); + if (resp[NL80211_PMSR_RESP_ATTR_FINAL]) + printf(" (final)"); + + if (!resp[NL80211_PMSR_RESP_ATTR_DATA]) { + printf(" - no data!\n"); + return; + } + + printf("\n"); + + nla_parse_nested(data, NL80211_PMSR_TYPE_MAX, + resp[NL80211_PMSR_RESP_ATTR_DATA], NULL); + + if (data[NL80211_PMSR_TYPE_FTM]) + parse_pmsr_ftm_data(data[NL80211_PMSR_TYPE_FTM]); +} + +static void parse_pmsr_result(struct nlattr **tb, + struct print_event_args *pargs) +{ + struct nlattr *pmsr[NL80211_PMSR_ATTR_MAX + 1]; + struct nlattr *peer; + unsigned long long cookie; + int err, i; + + if (!tb[NL80211_ATTR_COOKIE]) { + printf("Peer measurements: no cookie!\n"); + return; + } + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + + if (!tb[NL80211_ATTR_PEER_MEASUREMENTS]) { + printf("Peer measurements: no measurement data!\n"); + return; + } + + err = nla_parse_nested(pmsr, NL80211_PMSR_ATTR_MAX, + tb[NL80211_ATTR_PEER_MEASUREMENTS], NULL); + if (err) { + printf("Peer measurements: failed to parse measurement data!\n"); + return; + } + + if (!pmsr[NL80211_PMSR_ATTR_PEERS]) { + printf("Peer measurements: no peer data!\n"); + return; + } + + printf("Peer measurements (cookie %llu):\n", cookie); + + nla_for_each_nested(peer, pmsr[NL80211_PMSR_ATTR_PEERS], i) + parse_pmsr_peer(peer); +} + +static void parse_nan_match(struct nlattr **attrs) +{ + char macbuf[6*3]; + __u64 cookie; + struct nlattr *match[NL80211_NAN_MATCH_ATTR_MAX + 1]; + struct nlattr *local_func[NL80211_NAN_FUNC_ATTR_MAX + 1]; + struct nlattr *peer_func[NL80211_NAN_FUNC_ATTR_MAX + 1]; + + static struct nla_policy + nan_match_policy[NL80211_NAN_MATCH_ATTR_MAX + 1] = { + [NL80211_NAN_MATCH_FUNC_LOCAL] = { .type = NLA_NESTED }, + [NL80211_NAN_MATCH_FUNC_PEER] = { .type = NLA_NESTED }, + }; + + static struct nla_policy + nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = { + [NL80211_NAN_FUNC_TYPE] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_SERVICE_ID] = { }, + [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 }, + [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = { }, + [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG }, + [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 }, + [NL80211_NAN_FUNC_SERVICE_INFO] = { }, + [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED }, + [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8}, + }; + + cookie = nla_get_u64(attrs[NL80211_ATTR_COOKIE]); + mac_addr_n2a(macbuf, nla_data(attrs[NL80211_ATTR_MAC])); + + if (nla_parse_nested(match, NL80211_NAN_MATCH_ATTR_MAX, + attrs[NL80211_ATTR_NAN_MATCH], + nan_match_policy)) { + printf("NAN: failed to parse nan match event\n"); + return; + } + + if (nla_parse_nested(local_func, NL80211_NAN_FUNC_ATTR_MAX, + match[NL80211_NAN_MATCH_FUNC_LOCAL], + nan_func_policy)) { + printf("NAN: failed to parse nan local func\n"); + return; + } + + if (nla_parse_nested(peer_func, NL80211_NAN_FUNC_ATTR_MAX, + match[NL80211_NAN_MATCH_FUNC_PEER], + nan_func_policy)) { + printf("NAN: failed to parse nan local func\n"); + return; + } + + if (nla_get_u8(peer_func[NL80211_NAN_FUNC_TYPE]) == + NL80211_NAN_FUNC_PUBLISH) { + printf( + "NAN(cookie=0x%llx): DiscoveryResult, peer_id=%d, local_id=%d, peer_mac=%s", + cookie, + nla_get_u8(peer_func[NL80211_NAN_FUNC_INSTANCE_ID]), + nla_get_u8(local_func[NL80211_NAN_FUNC_INSTANCE_ID]), + macbuf); + if (peer_func[NL80211_NAN_FUNC_SERVICE_INFO]) + printf(", info=%.*s", + nla_len(peer_func[NL80211_NAN_FUNC_SERVICE_INFO]), + (char *)nla_data(peer_func[NL80211_NAN_FUNC_SERVICE_INFO])); + } else if (nla_get_u8(peer_func[NL80211_NAN_FUNC_TYPE]) == + NL80211_NAN_FUNC_SUBSCRIBE) { + printf( + "NAN(cookie=0x%llx): Replied, peer_id=%d, local_id=%d, peer_mac=%s", + cookie, + nla_get_u8(peer_func[NL80211_NAN_FUNC_INSTANCE_ID]), + nla_get_u8(local_func[NL80211_NAN_FUNC_INSTANCE_ID]), + macbuf); + } else if (nla_get_u8(peer_func[NL80211_NAN_FUNC_TYPE]) == + NL80211_NAN_FUNC_FOLLOW_UP) { + printf( + "NAN(cookie=0x%llx): FollowUpReceive, peer_id=%d, local_id=%d, peer_mac=%s", + cookie, + nla_get_u8(peer_func[NL80211_NAN_FUNC_INSTANCE_ID]), + nla_get_u8(local_func[NL80211_NAN_FUNC_INSTANCE_ID]), + macbuf); + if (peer_func[NL80211_NAN_FUNC_SERVICE_INFO]) + printf(", info=%.*s", + nla_len(peer_func[NL80211_NAN_FUNC_SERVICE_INFO]), + (char *)nla_data(peer_func[NL80211_NAN_FUNC_SERVICE_INFO])); + } else { + printf("NaN: Malformed event"); + } + + printf("\n"); +} + +static void parse_new_peer_candidate(struct nlattr **attrs) +{ + char macbuf[ETH_ALEN * 3]; + int32_t sig_dbm; + + printf("new peer candidate"); + if (attrs[NL80211_ATTR_MAC]) { + mac_addr_n2a(macbuf, nla_data(attrs[NL80211_ATTR_MAC])); + printf(" %s", macbuf); + } + if (attrs[NL80211_ATTR_RX_SIGNAL_DBM]) { + sig_dbm = nla_get_u32(attrs[NL80211_ATTR_RX_SIGNAL_DBM]); + printf(" %d dBm", sig_dbm); + } + + printf("\n"); +} + +static void parse_recv_interface(struct nlattr **attrs, int command) +{ + switch (command) { + case NL80211_CMD_NEW_INTERFACE: + printf("new interface"); + break; + case NL80211_CMD_DEL_INTERFACE: + printf("del interface"); + break; + case NL80211_CMD_SET_INTERFACE: + printf("set interface"); + break; + default: + printf("unknown interface command (%i) received\n", command); + return; + } + + if (attrs[NL80211_ATTR_IFTYPE]) { + printf(" type "); + switch (nla_get_u32(attrs[NL80211_ATTR_IFTYPE])) { + case NL80211_IFTYPE_STATION: + printf("station"); + break; + case NL80211_IFTYPE_AP: + printf("access point"); + break; + case NL80211_IFTYPE_MESH_POINT: + printf("mesh point"); + break; + case NL80211_IFTYPE_ADHOC: + printf("IBSS"); + break; + case NL80211_IFTYPE_MONITOR: + printf("monitor"); + break; + case NL80211_IFTYPE_AP_VLAN: + printf("AP-VLAN"); + break; + case NL80211_IFTYPE_WDS: + printf("WDS"); + break; + case NL80211_IFTYPE_P2P_CLIENT: + printf("P2P-client"); + break; + case NL80211_IFTYPE_P2P_GO: + printf("P2P-GO"); + break; + case NL80211_IFTYPE_P2P_DEVICE: + printf("P2P-Device"); + break; + case NL80211_IFTYPE_OCB: + printf("OCB"); + break; + case NL80211_IFTYPE_NAN: + printf("NAN"); + break; + default: + printf("unknown (%d)", + nla_get_u32(attrs[NL80211_ATTR_IFTYPE])); + break; + } + } + + if (attrs[NL80211_ATTR_MESH_ID]) { + printf(" meshid "); + print_ssid_escaped(nla_len(attrs[NL80211_ATTR_MESH_ID]), + nla_data(attrs[NL80211_ATTR_MESH_ID])); + } + + if (attrs[NL80211_ATTR_4ADDR]) { + printf(" use 4addr %d", nla_get_u8(attrs[NL80211_ATTR_4ADDR])); + } + + printf("\n"); +} + +static void parse_sta_opmode_changed(struct nlattr **attrs) +{ + char macbuf[ETH_ALEN*3]; + + printf("sta opmode changed"); + + if (attrs[NL80211_ATTR_MAC]) { + mac_addr_n2a(macbuf, nla_data(attrs[NL80211_ATTR_MAC])); + printf(" %s", macbuf); + } + + if (attrs[NL80211_ATTR_SMPS_MODE]) + printf(" smps mode %d", nla_get_u8(attrs[NL80211_ATTR_SMPS_MODE])); + + if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) + printf(" chan width %d", nla_get_u8(attrs[NL80211_ATTR_CHANNEL_WIDTH])); + + if (attrs[NL80211_ATTR_NSS]) + printf(" nss %d", nla_get_u8(attrs[NL80211_ATTR_NSS])); + + printf("\n"); +} + +static void parse_ch_switch_notify(struct nlattr **attrs, int command) +{ + switch (command) { + case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: + printf("channel switch started"); + break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + printf("channel switch"); + break; + default: + printf("unknown channel switch command (%i) received\n", command); + return; + } + + if (attrs[NL80211_ATTR_CH_SWITCH_COUNT]) + printf(" (count=%d)", nla_get_u32(attrs[NL80211_ATTR_CH_SWITCH_COUNT])); + + if (attrs[NL80211_ATTR_WIPHY_FREQ]) + printf(" freq=%d", nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ])); + + if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) { + printf(" width="); + switch(nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH])) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + printf("\"20 MHz\""); + break; + case NL80211_CHAN_WIDTH_40: + printf("\"40 MHz\""); + break; + case NL80211_CHAN_WIDTH_80: + printf("\"80 MHz\""); + break; + case NL80211_CHAN_WIDTH_80P80: + printf("\"80+80 MHz\""); + break; + case NL80211_CHAN_WIDTH_160: + printf("\"160 MHz\""); + break; + case NL80211_CHAN_WIDTH_5: + printf("\"5 MHz\""); + break; + case NL80211_CHAN_WIDTH_10: + printf("\"10 MHz\""); + break; + default: + printf("\"unknown\""); + } + } + + if (attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + printf(" type="); + switch(nla_get_u32(attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + printf("\"No HT\""); + break; + case NL80211_CHAN_HT20: + printf("\"HT20\""); + break; + case NL80211_CHAN_HT40MINUS: + printf("\"HT40-\""); + break; + case NL80211_CHAN_HT40PLUS: + printf("\"HT40+\""); + break; + } + } + + if (attrs[NL80211_ATTR_CENTER_FREQ1]) + printf(" freq1=%d", nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1])); + + if (attrs[NL80211_ATTR_CENTER_FREQ2]) + printf(" freq2=%d", nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2])); + + printf("\n"); +} + +static void parse_assoc_comeback(struct nlattr **attrs, int command) +{ + __u32 timeout = 0; + char macbuf[6 * 3] = "<unset>"; + + if (attrs[NL80211_ATTR_MAC]) + mac_addr_n2a(macbuf, nla_data(attrs[NL80211_ATTR_MAC])); + + if (attrs[NL80211_ATTR_TIMEOUT]) + timeout = nla_get_u32(attrs[NL80211_ATTR_TIMEOUT]); + + printf("assoc comeback bssid %s timeout %d\n", + macbuf, timeout); +} + static int print_event(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -304,12 +921,13 @@ static int print_event(struct nl_msg *msg, void *arg) int rem_nst; __u16 status; - if (args->time || args->reltime) { + if (args->time || args->reltime || args->ctime) { unsigned long long usecs, previous; previous = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec; gettimeofday(&args->ts, NULL); usecs = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec; + if (args->reltime) { if (!args->have_ts) { usecs = 0; @@ -317,15 +935,28 @@ static int print_event(struct nl_msg *msg, void *arg) } else usecs -= previous; } - printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000); + + if (args->ctime) { + struct tm *tm = localtime(&args->ts.tv_sec); + char buf[255]; + + memset(buf, 0, 255); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); + printf("[%s.%06lu]: ", buf, args->ts.tv_usec); + } else { + printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000); + } } 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])); + /* if_indextoname may fails on delete interface/wiphy event */ + if (if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname)) + printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY])); + else + printf("phy #%d: ", 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]), @@ -348,6 +979,7 @@ static int print_event(struct nl_msg *msg, void *arg) break; case NL80211_CMD_NEW_SCAN_RESULTS: printf("scan finished:"); + /* fall through */ case NL80211_CMD_SCAN_ABORTED: if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED) printf("scan aborted:"); @@ -374,8 +1006,12 @@ static int print_event(struct nl_msg *msg, void *arg) case NL80211_CMD_SCHED_SCAN_RESULTS: printf("got scheduled scan results\n"); break; + case NL80211_CMD_WIPHY_REG_CHANGE: case NL80211_CMD_REG_CHANGE: - printf("regulatory domain change: "); + if (gnlh->cmd == NL80211_CMD_WIPHY_REG_CHANGE) + printf("regulatory domain change (phy): "); + else + printf("regulatory domain change: "); reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); @@ -498,7 +1134,9 @@ static int print_event(struct nl_msg *msg, void *arg) break; case NL80211_CMD_CONNECT: status = 0; - if (!tb[NL80211_ATTR_STATUS_CODE]) + if (tb[NL80211_ATTR_TIMED_OUT]) + printf("timed out"); + else if (!tb[NL80211_ATTR_STATUS_CODE]) printf("unknown connect status"); else if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) == 0) printf("connected"); @@ -544,6 +1182,11 @@ static int print_event(struct nl_msg *msg, void *arg) nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]), (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE])); break; + case NL80211_CMD_FRAME_WAIT_CANCEL: + printf("frame wait cancel on freq %d (cookie %llx)\n", + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]), + (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE])); + break; case NL80211_CMD_NOTIFY_CQM: parse_cqm_event(tb); break; @@ -555,6 +1198,11 @@ static int print_event(struct nl_msg *msg, void *arg) (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]), tb[NL80211_ATTR_ACK] ? "acked" : "no ack"); break; + case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: + printf("ctrl. port TX status (cookie %llx): %s\n", + (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]), + tb[NL80211_ATTR_ACK] ? "acked" : "no ack"); + break; case NL80211_CMD_PMKSA_CANDIDATE: printf("PMKSA candidate found\n"); break; @@ -572,42 +1220,81 @@ static int print_event(struct nl_msg *msg, void *arg) tb[NL80211_ATTR_ACK] ? "acked" : "no ack"); break; case NL80211_CMD_VENDOR: - printf("vendor event %.6x:%d\n", - nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]), - nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD])); - if (args->frame && tb[NL80211_ATTR_VENDOR_DATA]) - iw_hexdump("vendor event", - nla_data(tb[NL80211_ATTR_VENDOR_DATA]), - nla_len(tb[NL80211_ATTR_VENDOR_DATA])); - break; - case NL80211_CMD_RADAR_DETECT: - printf("radar event "); - if (tb[NL80211_ATTR_RADAR_EVENT]) { - switch (nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT])) { - case NL80211_RADAR_DETECTED: - printf("(radar detected)"); - break; - case NL80211_RADAR_CAC_FINISHED: - printf("(cac finished)"); - break; - case NL80211_RADAR_CAC_ABORTED: - printf("(cac aborted)"); - break; - case NL80211_RADAR_NOP_FINISHED: - printf("(nop finished)"); - break; - default: - printf("(unknown)"); - break; - }; - } else { - printf("(unknown)"); + parse_vendor_event(tb, args->frame); + break; + case NL80211_CMD_RADAR_DETECT: { + enum nl80211_radar_event event_type; + uint32_t freq; + + if (!tb[NL80211_ATTR_RADAR_EVENT] || + !tb[NL80211_ATTR_WIPHY_FREQ]) { + printf("BAD radar event\n"); + break; + } + + freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + printf("%d MHz: radar detected\n", freq); + break; + case NL80211_RADAR_CAC_FINISHED: + printf("%d MHz: CAC finished\n", freq); + break; + case NL80211_RADAR_CAC_ABORTED: + printf("%d MHz: CAC was aborted\n", freq); + break; + case NL80211_RADAR_NOP_FINISHED: + printf("%d MHz: NOP finished\n", freq); + break; + case NL80211_RADAR_PRE_CAC_EXPIRED: + printf("%d MHz: PRE-CAC expired\n", freq); + break; + case NL80211_RADAR_CAC_STARTED: + printf("%d MHz: CAC started\n", freq); + break; + default: + printf("%d MHz: unknown radar event\n", freq); + } } - printf("\n"); break; case NL80211_CMD_DEL_WIPHY: printf("delete wiphy\n"); break; + case NL80211_CMD_PEER_MEASUREMENT_RESULT: + parse_pmsr_result(tb, args); + break; + case NL80211_CMD_PEER_MEASUREMENT_COMPLETE: + printf("peer measurement complete\n"); + break; + case NL80211_CMD_DEL_NAN_FUNCTION: + parse_nan_term(tb); + break; + case NL80211_CMD_NAN_MATCH: + parse_nan_match(tb); + break; + case NL80211_CMD_NEW_PEER_CANDIDATE: + parse_new_peer_candidate(tb); + break; + case NL80211_CMD_NEW_INTERFACE: + case NL80211_CMD_SET_INTERFACE: + case NL80211_CMD_DEL_INTERFACE: + parse_recv_interface(tb, gnlh->cmd); + break; + case NL80211_CMD_STA_OPMODE_CHANGED: + parse_sta_opmode_changed(tb); + break; + case NL80211_CMD_STOP_AP: + printf("stop ap\n"); + break; + case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: + case NL80211_CMD_CH_SWITCH_NOTIFY: + parse_ch_switch_notify(tb, gnlh->cmd); + break; + case NL80211_CMD_ASSOC_COMEBACK: /* 147 */ + parse_assoc_comeback(tb, gnlh->cmd); + break; default: printf("unknown event %d (%s)\n", gnlh->cmd, command_name(gnlh->cmd)); @@ -619,8 +1306,9 @@ static int print_event(struct nl_msg *msg, void *arg) } struct wait_event { - int n_cmds; + int n_cmds, n_prints; const __u32 *cmds; + const __u32 *prints; __u32 cmd; struct print_event_args *pargs; }; @@ -631,14 +1319,18 @@ static int wait_event(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); int i; - for (i = 0; i < wait->n_cmds; i++) { - if (gnlh->cmd == wait->cmds[i]) { - wait->cmd = gnlh->cmd; - if (wait->pargs) + if (wait->pargs) { + for (i = 0; i < wait->n_prints; i++) { + if (gnlh->cmd == wait->prints[i]) print_event(msg, wait->pargs); } } + for (i = 0; i < wait->n_cmds; i++) { + if (gnlh->cmd == wait->cmds[i]) + wait->cmd = gnlh->cmd; + } + return NL_SKIP; } @@ -686,11 +1378,19 @@ int __prepare_listen_events(struct nl80211_state *state) return ret; } + mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "nan"); + if (mcid >= 0) { + ret = nl_socket_add_membership(state->nl_sock, mcid); + if (ret) + return ret; + } + return 0; } __u32 __do_listen_events(struct nl80211_state *state, const int n_waits, const __u32 *waits, + const int n_prints, const __u32 *prints, struct print_event_args *args) { struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); @@ -703,14 +1403,17 @@ __u32 __do_listen_events(struct nl80211_state *state, /* 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, valid_handler, NULL); if (n_waits && waits) { wait_ev.cmds = waits; wait_ev.n_cmds = n_waits; + wait_ev.prints = prints; + wait_ev.n_prints = n_prints; wait_ev.pargs = args; - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev); + register_handler(wait_event, &wait_ev); } else - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args); + register_handler(print_event, args); wait_ev.cmd = 0; @@ -731,16 +1434,16 @@ __u32 listen_events(struct nl80211_state *state, if (ret) return ret; - return __do_listen_events(state, n_waits, waits, NULL); + return __do_listen_events(state, n_waits, waits, 0, NULL, NULL); } static int print_events(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, int argc, char **argv, enum id_input id) { struct print_event_args args; + int num_time_formats = 0; int ret; memset(&args, 0, sizeof(args)); @@ -751,17 +1454,22 @@ static int print_events(struct nl80211_state *state, while (argc > 0) { if (strcmp(argv[0], "-f") == 0) args.frame = true; - else if (strcmp(argv[0], "-t") == 0) + else if (strcmp(argv[0], "-t") == 0) { + num_time_formats++; args.time = true; - else if (strcmp(argv[0], "-r") == 0) + } else if (strcmp(argv[0], "-T") == 0) { + num_time_formats++; + args.ctime = true; + } else if (strcmp(argv[0], "-r") == 0) { + num_time_formats++; args.reltime = true; - else + } else return 1; argc--; argv++; } - if (args.time && args.reltime) + if (num_time_formats > 1) return 1; if (argc) @@ -771,10 +1479,11 @@ static int print_events(struct nl80211_state *state, if (ret) return ret; - return __do_listen_events(state, 0, NULL, &args); + return __do_listen_events(state, 0, NULL, 0, NULL, &args); } -TOPLEVEL(event, "[-t] [-r] [-f]", 0, 0, CIB_NONE, print_events, +TOPLEVEL(event, "[-t|-T|-r] [-f]", 0, 0, CIB_NONE, print_events, "Monitor events from the kernel.\n" "-t - print timestamp\n" - "-r - print relative timstamp\n" + "-T - print absolute, human-readable timestamp\n" + "-r - print relative timestamp\n" "-f - print full frame for auth/assoc etc."); |