X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fiw.git;a=blobdiff_plain;f=event.c;h=100f644d00d1a0ba41a30ea0c6376139ebf4a640;hp=c0475420d921fabf1ef49cdb7a42f08160e86fd2;hb=02b53ea252f51cdab7ec7341ec2477dcc51e3e81;hpb=87f7fba22ce42ca20363b860f947a3e9e07fe283 diff --git a/event.c b/event.c index c047542..100f644 100644 --- a/event.c +++ b/event.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "iw.h" static int no_seq_check(struct nl_msg *msg, void *arg) @@ -9,20 +10,50 @@ static int no_seq_check(struct nl_msg *msg, void *arg) return NL_OK; } -struct print_event_args { - bool frame, time; +struct ieee80211_beacon_channel { + __u16 center_freq; + bool no_ir; + bool no_ibss; }; +static int parse_beacon_hint_chan(struct nlattr *tb, + struct ieee80211_beacon_channel *chan) +{ + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy beacon_freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + }; + + if (nla_parse_nested(tb_freq, + NL80211_FREQUENCY_ATTR_MAX, + tb, + beacon_freq_policy)) + return -EINVAL; + + chan->center_freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) + chan->no_ir = true; + if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) + chan->no_ibss = true; + + return 0; +} + 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); @@ -52,7 +83,6 @@ static void print_frame(struct print_event_args *args, struct nlattr *attr) tmp = (frame[29] << 8) + frame[28]; printf(" status: %d: %s", tmp, get_status_str(tmp)); break; - break; case 0xa0: /* disassoc */ case 0xc0: /* deauth */ /* reason */ @@ -72,6 +102,554 @@ static void print_frame(struct print_event_args *args, struct nlattr *attr) printf("]"); } +static void parse_cqm_event(struct nlattr **attrs) +{ + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + struct nlattr *cqm_attr = attrs[NL80211_ATTR_CQM]; + + printf("CQM event: "); + + if (!cqm_attr || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, cqm_attr, cqm_policy)) { + printf("missing data!\n"); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) { + enum nl80211_cqm_rssi_threshold_event rssi_event; + bool found_one = false; + + rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + + switch (rssi_event) { + case NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: + printf("RSSI went above threshold\n"); + found_one = true; + break; + case NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: + printf("RSSI went below threshold\n"); + found_one = true; + break; + case NL80211_CQM_RSSI_BEACON_LOSS_EVENT: + printf("Beacon loss detected\n"); + found_one = true; + break; + } + + if (!found_one) + printf("Unknown event type: %i\n", rssi_event); + } else if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (attrs[NL80211_ATTR_MAC]) { + uint32_t frames; + char buf[3*6]; + + frames = nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]); + mac_addr_n2a(buf, nla_data(attrs[NL80211_ATTR_MAC])); + printf("peer %s didn't ACK %d packets\n", buf, frames); + } else { + printf("PKT-LOSS-EVENT did not have MAC attribute!\n"); + } + } else if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) { + printf("beacon loss\n"); + } else { + printf("unknown event\n"); + } +} + +static const char * key_type_str(enum nl80211_key_type key_type) +{ + static char buf[30]; + switch (key_type) { + case NL80211_KEYTYPE_GROUP: + return "Group"; + case NL80211_KEYTYPE_PAIRWISE: + return "Pairwise"; + case NL80211_KEYTYPE_PEERKEY: + return "PeerKey"; + default: + snprintf(buf, sizeof(buf), "unknown(%d)", key_type); + return buf; + } +} + +static void parse_mic_failure(struct nlattr **attrs) +{ + printf("Michael MIC failure event:"); + + if (attrs[NL80211_ATTR_MAC]) { + char addr[3 * ETH_ALEN]; + mac_addr_n2a(addr, nla_data(attrs[NL80211_ATTR_MAC])); + printf(" source MAC address %s", addr); + } + + if (attrs[NL80211_ATTR_KEY_SEQ] && + nla_len(attrs[NL80211_ATTR_KEY_SEQ]) == 6) { + unsigned char *seq = nla_data(attrs[NL80211_ATTR_KEY_SEQ]); + printf(" seq=%02x%02x%02x%02x%02x%02x", + seq[0], seq[1], seq[2], seq[3], seq[4], seq[5]); + } + if (attrs[NL80211_ATTR_KEY_TYPE]) { + enum nl80211_key_type key_type = + nla_get_u32(attrs[NL80211_ATTR_KEY_TYPE]); + printf(" Key Type %s", key_type_str(key_type)); + } + + if (attrs[NL80211_ATTR_KEY_IDX]) { + __u8 key_id = nla_get_u8(attrs[NL80211_ATTR_KEY_IDX]); + printf(" Key Id %d", key_id); + } + + printf("\n"); +} + +static void parse_wowlan_wake_event(struct nlattr **attrs) +{ + struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG], + *tb_match[NUM_NL80211_ATTR]; + + printf("WoWLAN wakeup\n"); + if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { + printf("\twakeup not due to WoWLAN\n"); + return; + } + + nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, + nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), NULL); + + if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) + printf("\t* was disconnected\n"); + if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) + printf("\t* magic packet received\n"); + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) + printf("\t* pattern index: %u\n", + nla_get_u32(tb[NL80211_WOWLAN_TRIG_PKT_PATTERN])); + if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) + printf("\t* GTK rekey failure\n"); + if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) + printf("\t* EAP identity request\n"); + if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) + printf("\t* 4-way handshake\n"); + if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) + printf("\t* RF-kill released\n"); + if (tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS]) { + struct nlattr *match, *freq; + int rem_nst, rem_nst2; + + printf("\t* network detected\n"); + nla_for_each_nested(match, + tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS], + rem_nst) { + 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])); + printf("\""); + if (tb_match[NL80211_ATTR_SCAN_FREQUENCIES]) { + printf(" freq(s):"); + nla_for_each_nested(freq, + tb_match[NL80211_ATTR_SCAN_FREQUENCIES], + rem_nst2) + printf(" %d", nla_get_u32(freq)); + } + printf("\n"); + } + } + if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]) { + uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]); + int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]); + int i; + printf("\t* packet (might be truncated): "); + for (i = 0; i < l; i++) { + if (i > 0) + printf(":"); + printf("%.2x", d[i]); + } + printf("\n"); + } + if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]) { + uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]); + int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]); + int i; + printf("\t* packet (might be truncated): "); + for (i = 0; i < l; i++) { + if (i > 0) + printf(":"); + printf("%.2x", d[i]); + } + printf("\n"); + } + if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH]) + printf("\t* TCP connection wakeup received\n"); + if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST]) + printf("\t* TCP connection lost\n"); + if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS]) + printf("\t* TCP connection ran out of tokens\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 int print_event(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -80,13 +658,25 @@ static int print_event(struct nl_msg *msg, void *arg) char ifname[100]; char macbuf[6*3]; __u8 reg_type; + struct ieee80211_beacon_channel chan_before_beacon, chan_after_beacon; + __u32 wiphy_idx = 0; int rem_nst; __u16 status; - if (args->time) { - struct timeval tv; - gettimeofday(&tv, NULL); - printf("%ld.%06u: ", (long) tv.tv_sec, (unsigned int) tv.tv_usec); + if (args->time || args->reltime) { + 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; + args->have_ts = true; + } else + usecs -= previous; + } + printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000); } nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -95,9 +685,15 @@ static int print_event(struct nl_msg *msg, void *arg) 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])); } @@ -111,6 +707,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:"); @@ -128,6 +725,15 @@ static int print_event(struct nl_msg *msg, void *arg) } printf("\n"); break; + case NL80211_CMD_START_SCHED_SCAN: + printf("scheduled scan started\n"); + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + printf("sched scan stopped\n"); + break; + case NL80211_CMD_SCHED_SCAN_RESULTS: + printf("got scheduled scan results\n"); + break; case NL80211_CMD_REG_CHANGE: printf("regulatory domain change: "); @@ -163,6 +769,49 @@ static int print_event(struct nl_msg *msg, void *arg) printf("\n"); break; + case NL80211_CMD_REG_BEACON_HINT: + + wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + memset(&chan_before_beacon, 0, sizeof(chan_before_beacon)); + memset(&chan_after_beacon, 0, sizeof(chan_after_beacon)); + + if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_BEFORE], + &chan_before_beacon)) + break; + if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_AFTER], + &chan_after_beacon)) + break; + + if (chan_before_beacon.center_freq != chan_after_beacon.center_freq) + break; + + /* A beacon hint is sent _only_ if something _did_ change */ + printf("beacon hint:\n"); + + printf("phy%d %d MHz [%d]:\n", + wiphy_idx, + chan_before_beacon.center_freq, + ieee80211_frequency_to_channel(chan_before_beacon.center_freq)); + + if (chan_before_beacon.no_ir && !chan_after_beacon.no_ir) { + if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss) + printf("\to Initiating radiation enabled\n"); + else + printf("\to active scan enabled\n"); + } else if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss) { + printf("\to ibss enabled\n"); + } + + break; + case NL80211_CMD_NEW_STATION: + mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); + printf("new station %s\n", macbuf); + break; + case NL80211_CMD_DEL_STATION: + mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); + printf("del station %s\n", macbuf); + break; case NL80211_CMD_JOIN_IBSS: mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); printf("IBSS %s joined\n", macbuf); @@ -197,9 +846,21 @@ static int print_event(struct nl_msg *msg, void *arg) print_frame(args, tb[NL80211_ATTR_FRAME]); printf("\n"); break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + printf("unprotected deauth"); + print_frame(args, tb[NL80211_ATTR_FRAME]); + printf("\n"); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + printf("unprotected disassoc"); + print_frame(args, tb[NL80211_ATTR_FRAME]); + printf("\n"); + 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"); @@ -234,18 +895,116 @@ static int print_event(struct nl_msg *msg, void *arg) get_reason_str(nla_get_u16(tb[NL80211_ATTR_REASON_CODE]))); printf("\n"); break; + case NL80211_CMD_REMAIN_ON_CHANNEL: + printf("remain on freq %d (%dms, cookie %llx)\n", + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]), + nla_get_u32(tb[NL80211_ATTR_DURATION]), + (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE])); + break; + case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: + printf("done with remain 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; + case NL80211_CMD_MICHAEL_MIC_FAILURE: + parse_mic_failure(tb); + break; + case NL80211_CMD_FRAME_TX_STATUS: + printf("mgmt 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; + case NL80211_CMD_SET_WOWLAN: + parse_wowlan_wake_event(tb); + break; + case NL80211_CMD_PROBE_CLIENT: + if (tb[NL80211_ATTR_MAC]) + mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); + else + strcpy(macbuf, "??"); + printf("probe client %s (cookie %llx): %s\n", + macbuf, + (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]), + 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: { + 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; + default: + printf("%d MHz: unknown radar event\n", freq); + } + } + 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; + } default: - printf("unknown event %d\n", gnlh->cmd); + printf("unknown event %d (%s)\n", + gnlh->cmd, command_name(gnlh->cmd)); break; } + fflush(stdout); return NL_SKIP; } struct wait_event { - int n_cmds; + int n_cmds, n_prints; const __u32 *cmds; + const __u32 *prints; __u32 cmd; + struct print_event_args *pargs; }; static int wait_event(struct nl_msg *msg, void *arg) @@ -254,27 +1013,24 @@ static int wait_event(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); int i; + 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]) { + if (gnlh->cmd == wait->cmds[i]) wait->cmd = gnlh->cmd; - } } return NL_SKIP; } -static __u32 __listen_events(struct nl80211_state *state, - const int n_waits, const __u32 *waits, - struct print_event_args *args) +int __prepare_listen_events(struct nl80211_state *state) { int mcid, ret; - struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - struct wait_event wait_ev; - - if (!cb) { - fprintf(stderr, "failed to allocate netlink callbacks\n"); - return -ENOMEM; - } /* Configuration multicast group */ mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config"); @@ -309,16 +1065,49 @@ static __u32 __listen_events(struct nl80211_state *state, return ret; } + mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "vendor"); + if (mcid >= 0) { + ret = nl_socket_add_membership(state->nl_sock, mcid); + if (ret) + 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); + struct wait_event wait_ev; + + 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, valid_handler, NULL); if (n_waits && waits) { wait_ev.cmds = waits; wait_ev.n_cmds = n_waits; - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev); - } else { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args); - } + wait_ev.prints = prints; + wait_ev.n_prints = n_prints; + wait_ev.pargs = args; + register_handler(wait_event, &wait_ev); + } else + register_handler(print_event, args); wait_ev.cmd = 0; @@ -333,15 +1122,22 @@ static __u32 __listen_events(struct nl80211_state *state, __u32 listen_events(struct nl80211_state *state, const int n_waits, const __u32 *waits) { - return __listen_events(state, n_waits, waits, NULL); + int ret; + + ret = __prepare_listen_events(state); + if (ret) + return ret; + + 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) + int argc, char **argv, + enum id_input id) { struct print_event_args args; + int ret; memset(&args, 0, sizeof(args)); @@ -353,18 +1149,28 @@ static int print_events(struct nl80211_state *state, args.frame = true; else if (strcmp(argv[0], "-t") == 0) args.time = true; + else if (strcmp(argv[0], "-r") == 0) + args.reltime = true; else return 1; argc--; argv++; } + if (args.time && args.reltime) + return 1; + if (argc) return 1; - return __listen_events(state, 0, NULL, &args); + ret = __prepare_listen_events(state); + if (ret) + return ret; + + return __do_listen_events(state, 0, NULL, 0, NULL, &args); } -TOPLEVEL(event, "[-t] [-f]", 0, 0, CIB_NONE, print_events, +TOPLEVEL(event, "[-t|-r] [-f]", 0, 0, CIB_NONE, print_events, "Monitor events from the kernel.\n" "-t - print timestamp\n" + "-r - print relative timstamp\n" "-f - print full frame for auth/assoc etc.");