Home | History | Annotate | Download | only in iw
      1 #include <stdint.h>
      2 #include <stdbool.h>
      3 #include <net/if.h>
      4 #include <errno.h>
      5 #include "iw.h"
      6 
      7 static int no_seq_check(struct nl_msg *msg, void *arg)
      8 {
      9 	return NL_OK;
     10 }
     11 
     12 struct ieee80211_beacon_channel {
     13 	__u16 center_freq;
     14 	bool no_ir;
     15 	bool no_ibss;
     16 };
     17 
     18 static int parse_beacon_hint_chan(struct nlattr *tb,
     19 				  struct ieee80211_beacon_channel *chan)
     20 {
     21 	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
     22 	static struct nla_policy beacon_freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
     23 		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
     24 		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
     25 		[__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
     26 	};
     27 
     28 	if (nla_parse_nested(tb_freq,
     29 			     NL80211_FREQUENCY_ATTR_MAX,
     30 			     tb,
     31 			     beacon_freq_policy))
     32 		return -EINVAL;
     33 
     34 	chan->center_freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
     35 
     36 	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
     37 		chan->no_ir = true;
     38 	if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS])
     39 		chan->no_ibss = true;
     40 
     41 	return 0;
     42 }
     43 
     44 static void print_frame(struct print_event_args *args, struct nlattr *attr)
     45 {
     46 	uint8_t *frame;
     47 	size_t len;
     48 	int i;
     49 	char macbuf[6*3];
     50 	uint16_t tmp;
     51 
     52 	if (!attr)
     53 		printf(" [no frame]");
     54 
     55 	frame = nla_data(attr);
     56 	len = nla_len(attr);
     57 
     58 	if (len < 26) {
     59 		printf(" [invalid frame: ");
     60 		goto print_frame;
     61 	}
     62 
     63 	mac_addr_n2a(macbuf, frame + 10);
     64 	printf(" %s -> ", macbuf);
     65 	mac_addr_n2a(macbuf, frame + 4);
     66 	printf("%s", macbuf);
     67 
     68 	switch (frame[0] & 0xfc) {
     69 	case 0x10: /* assoc resp */
     70 	case 0x30: /* reassoc resp */
     71 		/* status */
     72 		tmp = (frame[27] << 8) + frame[26];
     73 		printf(" status: %d: %s", tmp, get_status_str(tmp));
     74 		break;
     75 	case 0x00: /* assoc req */
     76 	case 0x20: /* reassoc req */
     77 		break;
     78 	case 0xb0: /* auth */
     79 		/* status */
     80 		tmp = (frame[29] << 8) + frame[28];
     81 		printf(" status: %d: %s", tmp, get_status_str(tmp));
     82 		break;
     83 	case 0xa0: /* disassoc */
     84 	case 0xc0: /* deauth */
     85 		/* reason */
     86 		tmp = (frame[25] << 8) + frame[24];
     87 		printf(" reason %d: %s", tmp, get_reason_str(tmp));
     88 		break;
     89 	}
     90 
     91 	if (!args->frame)
     92 		return;
     93 
     94 	printf(" [frame:");
     95 
     96  print_frame:
     97 	for (i = 0; i < len; i++)
     98 		printf(" %.02x", frame[i]);
     99 	printf("]");
    100 }
    101 
    102 static void parse_cqm_event(struct nlattr **attrs)
    103 {
    104 	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
    105 		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
    106 		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
    107 		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
    108 	};
    109 	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
    110 	struct nlattr *cqm_attr = attrs[NL80211_ATTR_CQM];
    111 
    112 	printf("CQM event: ");
    113 
    114 	if (!cqm_attr ||
    115 	    nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, cqm_attr, cqm_policy)) {
    116 		printf("missing data!\n");
    117 		return;
    118 	}
    119 
    120 	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) {
    121 		enum nl80211_cqm_rssi_threshold_event rssi_event;
    122 		bool found_one = false;
    123 
    124 		rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
    125 
    126 		switch (rssi_event) {
    127 		case NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH:
    128 			printf("RSSI went above threshold\n");
    129 			found_one = true;
    130 			break;
    131 		case NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW:
    132 			printf("RSSI went below threshold\n");
    133 			found_one = true;
    134 			break;
    135 		case NL80211_CQM_RSSI_BEACON_LOSS_EVENT:
    136 			printf("Beacon loss detected\n");
    137 			found_one = true;
    138 			break;
    139 		}
    140 
    141 		if (!found_one)
    142 			printf("Unknown event type: %i\n", rssi_event);
    143 	} else if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
    144 		if (attrs[NL80211_ATTR_MAC]) {
    145 			uint32_t frames;
    146 			char buf[3*6];
    147 
    148 			frames = nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
    149 			mac_addr_n2a(buf, nla_data(attrs[NL80211_ATTR_MAC]));
    150 			printf("peer %s didn't ACK %d packets\n", buf, frames);
    151 		} else {
    152 			printf("PKT-LOSS-EVENT did not have MAC attribute!\n");
    153 		}
    154 	} else if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
    155 		printf("beacon loss\n");
    156 	} else {
    157 		printf("unknown event\n");
    158 	}
    159 }
    160 
    161 static const char * key_type_str(enum nl80211_key_type key_type)
    162 {
    163 	static char buf[30];
    164 	switch (key_type) {
    165 	case NL80211_KEYTYPE_GROUP:
    166 		return "Group";
    167 	case NL80211_KEYTYPE_PAIRWISE:
    168 		return "Pairwise";
    169 	case NL80211_KEYTYPE_PEERKEY:
    170 		return "PeerKey";
    171 	default:
    172 		snprintf(buf, sizeof(buf), "unknown(%d)", key_type);
    173 		return buf;
    174 	}
    175 }
    176 
    177 static void parse_mic_failure(struct nlattr **attrs)
    178 {
    179 	printf("Michael MIC failure event:");
    180 
    181 	if (attrs[NL80211_ATTR_MAC]) {
    182 		char addr[3 * ETH_ALEN];
    183 		mac_addr_n2a(addr, nla_data(attrs[NL80211_ATTR_MAC]));
    184 		printf(" source MAC address %s", addr);
    185 	}
    186 
    187 	if (attrs[NL80211_ATTR_KEY_SEQ] &&
    188 	    nla_len(attrs[NL80211_ATTR_KEY_SEQ]) == 6) {
    189 		unsigned char *seq = nla_data(attrs[NL80211_ATTR_KEY_SEQ]);
    190 		printf(" seq=%02x%02x%02x%02x%02x%02x",
    191 		       seq[0], seq[1], seq[2], seq[3], seq[4], seq[5]);
    192 	}
    193 	if (attrs[NL80211_ATTR_KEY_TYPE]) {
    194 		enum nl80211_key_type key_type =
    195 			nla_get_u32(attrs[NL80211_ATTR_KEY_TYPE]);
    196 		printf(" Key Type %s", key_type_str(key_type));
    197 	}
    198 
    199 	if (attrs[NL80211_ATTR_KEY_IDX]) {
    200 		__u8 key_id = nla_get_u8(attrs[NL80211_ATTR_KEY_IDX]);
    201 		printf(" Key Id %d", key_id);
    202 	}
    203 
    204 	printf("\n");
    205 }
    206 
    207 static void parse_wowlan_wake_event(struct nlattr **attrs)
    208 {
    209 	struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG],
    210 		*tb_match[NUM_NL80211_ATTR];
    211 
    212 	printf("WoWLAN wakeup\n");
    213 	if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
    214 		printf("\twakeup not due to WoWLAN\n");
    215 		return;
    216 	}
    217 
    218 	nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
    219 		  nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
    220 		  nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), NULL);
    221 
    222 	if (tb[NL80211_WOWLAN_TRIG_DISCONNECT])
    223 		printf("\t* was disconnected\n");
    224 	if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT])
    225 		printf("\t* magic packet received\n");
    226 	if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN])
    227 		printf("\t* pattern index: %u\n",
    228 		       nla_get_u32(tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]));
    229 	if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
    230 		printf("\t* GTK rekey failure\n");
    231 	if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
    232 		printf("\t* EAP identity request\n");
    233 	if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
    234 		printf("\t* 4-way handshake\n");
    235 	if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
    236 		printf("\t* RF-kill released\n");
    237 	if (tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS]) {
    238 		struct nlattr *match, *freq;
    239 		int rem_nst, rem_nst2;
    240 
    241 		printf("\t* network detected\n");
    242 		nla_for_each_nested(match,
    243 				    tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS],
    244 				    rem_nst) {
    245 			nla_parse(tb_match, NUM_NL80211_ATTR, nla_data(match),
    246 				  nla_len(match),
    247 				  NULL);
    248 			printf("\t\tSSID: \"");
    249 			print_ssid_escaped(nla_len(tb_match[NL80211_ATTR_SSID]),
    250 					   nla_data(tb_match[NL80211_ATTR_SSID]));
    251 			printf("\"");
    252 			if (tb_match[NL80211_ATTR_SCAN_FREQUENCIES]) {
    253 				printf(" freq(s):");
    254 				nla_for_each_nested(freq,
    255 						    tb_match[NL80211_ATTR_SCAN_FREQUENCIES],
    256 						    rem_nst2)
    257 					printf(" %d", nla_get_u32(freq));
    258 			}
    259 			printf("\n");
    260 		}
    261 	}
    262 	if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]) {
    263 		uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
    264 		int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
    265 		int i;
    266 		printf("\t* packet (might be truncated): ");
    267 		for (i = 0; i < l; i++) {
    268 			if (i > 0)
    269 				printf(":");
    270 			printf("%.2x", d[i]);
    271 		}
    272 		printf("\n");
    273 	}
    274 	if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]) {
    275 		uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
    276 		int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
    277 		int i;
    278 		printf("\t* packet (might be truncated): ");
    279 		for (i = 0; i < l; i++) {
    280 			if (i > 0)
    281 				printf(":");
    282 			printf("%.2x", d[i]);
    283 		}
    284 		printf("\n");
    285 	}
    286 	if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH])
    287 		printf("\t* TCP connection wakeup received\n");
    288 	if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST])
    289 		printf("\t* TCP connection lost\n");
    290 	if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS])
    291 		printf("\t* TCP connection ran out of tokens\n");
    292 }
    293 
    294 static int print_event(struct nl_msg *msg, void *arg)
    295 {
    296 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
    297 	struct nlattr *tb[NL80211_ATTR_MAX + 1], *nst;
    298 	struct print_event_args *args = arg;
    299 	char ifname[100];
    300 	char macbuf[6*3];
    301 	__u8 reg_type;
    302 	struct ieee80211_beacon_channel chan_before_beacon,  chan_after_beacon;
    303 	__u32 wiphy_idx = 0;
    304 	int rem_nst;
    305 	__u16 status;
    306 
    307 	if (args->time || args->reltime) {
    308 		unsigned long long usecs, previous;
    309 
    310 		previous = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec;
    311 		gettimeofday(&args->ts, NULL);
    312 		usecs = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec;
    313 		if (args->reltime) {
    314 			if (!args->have_ts) {
    315 				usecs = 0;
    316 				args->have_ts = true;
    317 			} else
    318 				usecs -= previous;
    319 		}
    320 		printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000);
    321 	}
    322 
    323 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
    324 		  genlmsg_attrlen(gnlh, 0), NULL);
    325 
    326 	if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) {
    327 		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
    328 		printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY]));
    329 	} else if (tb[NL80211_ATTR_WDEV] && tb[NL80211_ATTR_WIPHY]) {
    330 		printf("wdev 0x%llx (phy #%d): ",
    331 			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV]),
    332 			nla_get_u32(tb[NL80211_ATTR_WIPHY]));
    333 	} else if (tb[NL80211_ATTR_IFINDEX]) {
    334 		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
    335 		printf("%s: ", ifname);
    336 	} else if (tb[NL80211_ATTR_WDEV]) {
    337 		printf("wdev 0x%llx: ", (unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV]));
    338 	} else if (tb[NL80211_ATTR_WIPHY]) {
    339 		printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
    340 	}
    341 
    342 	switch (gnlh->cmd) {
    343 	case NL80211_CMD_NEW_WIPHY:
    344 		printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]));
    345 		break;
    346 	case NL80211_CMD_TRIGGER_SCAN:
    347 		printf("scan started\n");
    348 		break;
    349 	case NL80211_CMD_NEW_SCAN_RESULTS:
    350 		printf("scan finished:");
    351 	case NL80211_CMD_SCAN_ABORTED:
    352 		if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED)
    353 			printf("scan aborted:");
    354 		if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
    355 			nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem_nst)
    356 				printf(" %d", nla_get_u32(nst));
    357 			printf(",");
    358 		}
    359 		if (tb[NL80211_ATTR_SCAN_SSIDS]) {
    360 			nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_SSIDS], rem_nst) {
    361 				printf(" \"");
    362 				print_ssid_escaped(nla_len(nst), nla_data(nst));
    363 				printf("\"");
    364 			}
    365 		}
    366 		printf("\n");
    367 		break;
    368 	case NL80211_CMD_START_SCHED_SCAN:
    369 		printf("scheduled scan started\n");
    370 		break;
    371 	case NL80211_CMD_SCHED_SCAN_STOPPED:
    372 		printf("sched scan stopped\n");
    373 		break;
    374 	case NL80211_CMD_SCHED_SCAN_RESULTS:
    375 		printf("got scheduled scan results\n");
    376 		break;
    377 	case NL80211_CMD_REG_CHANGE:
    378 		printf("regulatory domain change: ");
    379 
    380 		reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
    381 
    382 		switch (reg_type) {
    383 		case NL80211_REGDOM_TYPE_COUNTRY:
    384 			printf("set to %s by %s request",
    385 			       nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
    386 			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
    387 			if (tb[NL80211_ATTR_WIPHY])
    388 				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
    389 			break;
    390 		case NL80211_REGDOM_TYPE_WORLD:
    391 			printf("set to world roaming by %s request",
    392 			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
    393 			break;
    394 		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
    395 			printf("custom world roaming rules in place on phy%d by %s request",
    396 			       nla_get_u32(tb[NL80211_ATTR_WIPHY]),
    397 			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
    398 			break;
    399 		case NL80211_REGDOM_TYPE_INTERSECTION:
    400 			printf("intersection used due to a request made by %s",
    401 			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
    402 			if (tb[NL80211_ATTR_WIPHY])
    403 				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
    404 			break;
    405 		default:
    406 			printf("unknown source (upgrade this utility)");
    407 			break;
    408 		}
    409 
    410 		printf("\n");
    411 		break;
    412 	case NL80211_CMD_REG_BEACON_HINT:
    413 
    414 		wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
    415 
    416 		memset(&chan_before_beacon, 0, sizeof(chan_before_beacon));
    417 		memset(&chan_after_beacon, 0, sizeof(chan_after_beacon));
    418 
    419 		if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_BEFORE],
    420 					   &chan_before_beacon))
    421 			break;
    422 		if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_AFTER],
    423 					   &chan_after_beacon))
    424 			break;
    425 
    426 		if (chan_before_beacon.center_freq != chan_after_beacon.center_freq)
    427 			break;
    428 
    429 		/* A beacon hint is sent _only_ if something _did_ change */
    430 		printf("beacon hint:\n");
    431 
    432 		printf("phy%d %d MHz [%d]:\n",
    433 		       wiphy_idx,
    434 		       chan_before_beacon.center_freq,
    435 		       ieee80211_frequency_to_channel(chan_before_beacon.center_freq));
    436 
    437 		if (chan_before_beacon.no_ir && !chan_after_beacon.no_ir) {
    438 			if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss)
    439 				printf("\to Initiating radiation enabled\n");
    440 			else
    441 				printf("\to active scan enabled\n");
    442 		} else if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss) {
    443 			printf("\to ibss enabled\n");
    444 		}
    445 
    446 		break;
    447 	case NL80211_CMD_NEW_STATION:
    448 		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    449 		printf("new station %s\n", macbuf);
    450 		break;
    451 	case NL80211_CMD_DEL_STATION:
    452 		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    453 		printf("del station %s\n", macbuf);
    454 		break;
    455 	case NL80211_CMD_JOIN_IBSS:
    456 		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    457 		printf("IBSS %s joined\n", macbuf);
    458 		break;
    459 	case NL80211_CMD_AUTHENTICATE:
    460 		printf("auth");
    461 		if (tb[NL80211_ATTR_FRAME])
    462 			print_frame(args, tb[NL80211_ATTR_FRAME]);
    463 		else if (tb[NL80211_ATTR_TIMED_OUT])
    464 			printf(": timed out");
    465 		else
    466 			printf(": unknown event");
    467 		printf("\n");
    468 		break;
    469 	case NL80211_CMD_ASSOCIATE:
    470 		printf("assoc");
    471 		if (tb[NL80211_ATTR_FRAME])
    472 			print_frame(args, tb[NL80211_ATTR_FRAME]);
    473 		else if (tb[NL80211_ATTR_TIMED_OUT])
    474 			printf(": timed out");
    475 		else
    476 			printf(": unknown event");
    477 		printf("\n");
    478 		break;
    479 	case NL80211_CMD_DEAUTHENTICATE:
    480 		printf("deauth");
    481 		print_frame(args, tb[NL80211_ATTR_FRAME]);
    482 		printf("\n");
    483 		break;
    484 	case NL80211_CMD_DISASSOCIATE:
    485 		printf("disassoc");
    486 		print_frame(args, tb[NL80211_ATTR_FRAME]);
    487 		printf("\n");
    488 		break;
    489 	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
    490 		printf("unprotected deauth");
    491 		print_frame(args, tb[NL80211_ATTR_FRAME]);
    492 		printf("\n");
    493 		break;
    494 	case NL80211_CMD_UNPROT_DISASSOCIATE:
    495 		printf("unprotected disassoc");
    496 		print_frame(args, tb[NL80211_ATTR_FRAME]);
    497 		printf("\n");
    498 		break;
    499 	case NL80211_CMD_CONNECT:
    500 		status = 0;
    501 		if (!tb[NL80211_ATTR_STATUS_CODE])
    502 			printf("unknown connect status");
    503 		else if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) == 0)
    504 			printf("connected");
    505 		else {
    506 			status = nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]);
    507 			printf("failed to connect");
    508 		}
    509 		if (tb[NL80211_ATTR_MAC]) {
    510 			mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    511 			printf(" to %s", macbuf);
    512 		}
    513 		if (status)
    514 			printf(", status: %d: %s", status, get_status_str(status));
    515 		printf("\n");
    516 		break;
    517 	case NL80211_CMD_ROAM:
    518 		printf("roamed");
    519 		if (tb[NL80211_ATTR_MAC]) {
    520 			mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    521 			printf(" to %s", macbuf);
    522 		}
    523 		printf("\n");
    524 		break;
    525 	case NL80211_CMD_DISCONNECT:
    526 		printf("disconnected");
    527 		if (tb[NL80211_ATTR_DISCONNECTED_BY_AP])
    528 			printf(" (by AP)");
    529 		else
    530 			printf(" (local request)");
    531 		if (tb[NL80211_ATTR_REASON_CODE])
    532 			printf(" reason: %d: %s", nla_get_u16(tb[NL80211_ATTR_REASON_CODE]),
    533 				get_reason_str(nla_get_u16(tb[NL80211_ATTR_REASON_CODE])));
    534 		printf("\n");
    535 		break;
    536 	case NL80211_CMD_REMAIN_ON_CHANNEL:
    537 		printf("remain on freq %d (%dms, cookie %llx)\n",
    538 			nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
    539 			nla_get_u32(tb[NL80211_ATTR_DURATION]),
    540 			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
    541 		break;
    542 	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
    543 		printf("done with remain on freq %d (cookie %llx)\n",
    544 			nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
    545 			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
    546 		break;
    547 	case NL80211_CMD_NOTIFY_CQM:
    548 		parse_cqm_event(tb);
    549 		break;
    550 	case NL80211_CMD_MICHAEL_MIC_FAILURE:
    551 		parse_mic_failure(tb);
    552 		break;
    553 	case NL80211_CMD_FRAME_TX_STATUS:
    554 		printf("mgmt TX status (cookie %llx): %s\n",
    555 			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]),
    556 			tb[NL80211_ATTR_ACK] ? "acked" : "no ack");
    557 		break;
    558 	case NL80211_CMD_PMKSA_CANDIDATE:
    559 		printf("PMKSA candidate found\n");
    560 		break;
    561 	case NL80211_CMD_SET_WOWLAN:
    562 		parse_wowlan_wake_event(tb);
    563 		break;
    564 	case NL80211_CMD_PROBE_CLIENT:
    565 		if (tb[NL80211_ATTR_MAC])
    566 			mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
    567 		else
    568 			strcpy(macbuf, "??");
    569 		printf("probe client %s (cookie %llx): %s\n",
    570 		       macbuf,
    571 		       (unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]),
    572 		       tb[NL80211_ATTR_ACK] ? "acked" : "no ack");
    573 		break;
    574 	case NL80211_CMD_VENDOR:
    575 		printf("vendor event %.6x:%d\n",
    576 			nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]),
    577 			nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]));
    578 		if (args->frame && tb[NL80211_ATTR_VENDOR_DATA])
    579 			iw_hexdump("vendor event",
    580 				   nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
    581 				   nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
    582 		break;
    583 	case NL80211_CMD_RADAR_DETECT:
    584 		printf("radar event ");
    585 		if (tb[NL80211_ATTR_RADAR_EVENT]) {
    586 			switch (nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT])) {
    587 				case NL80211_RADAR_DETECTED:
    588 					printf("(radar detected)");
    589 					break;
    590 				case NL80211_RADAR_CAC_FINISHED:
    591 					printf("(cac finished)");
    592 					break;
    593 				case NL80211_RADAR_CAC_ABORTED:
    594 					printf("(cac aborted)");
    595 					break;
    596 				case NL80211_RADAR_NOP_FINISHED:
    597 					printf("(nop finished)");
    598 					break;
    599 				default:
    600 					printf("(unknown)");
    601 					break;
    602 			};
    603 		} else {
    604 			printf("(unknown)");
    605 		}
    606 		printf("\n");
    607 		break;
    608 	case NL80211_CMD_DEL_WIPHY:
    609 		printf("delete wiphy\n");
    610 		break;
    611 	default:
    612 		printf("unknown event %d (%s)\n",
    613 		       gnlh->cmd, command_name(gnlh->cmd));
    614 		break;
    615 	}
    616 
    617 	fflush(stdout);
    618 	return NL_SKIP;
    619 }
    620 
    621 struct wait_event {
    622 	int n_cmds;
    623 	const __u32 *cmds;
    624 	__u32 cmd;
    625 	struct print_event_args *pargs;
    626 };
    627 
    628 static int wait_event(struct nl_msg *msg, void *arg)
    629 {
    630 	struct wait_event *wait = arg;
    631 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
    632 	int i;
    633 
    634 	for (i = 0; i < wait->n_cmds; i++) {
    635 		if (gnlh->cmd == wait->cmds[i]) {
    636 			wait->cmd = gnlh->cmd;
    637 			if (wait->pargs)
    638 				print_event(msg, wait->pargs);
    639 		}
    640 	}
    641 
    642 	return NL_SKIP;
    643 }
    644 
    645 int __prepare_listen_events(struct nl80211_state *state)
    646 {
    647 	int mcid, ret;
    648 
    649 	/* Configuration multicast group */
    650 	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config");
    651 	if (mcid < 0)
    652 		return mcid;
    653 
    654 	ret = nl_socket_add_membership(state->nl_sock, mcid);
    655 	if (ret)
    656 		return ret;
    657 
    658 	/* Scan multicast group */
    659 	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan");
    660 	if (mcid >= 0) {
    661 		ret = nl_socket_add_membership(state->nl_sock, mcid);
    662 		if (ret)
    663 			return ret;
    664 	}
    665 
    666 	/* Regulatory multicast group */
    667 	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory");
    668 	if (mcid >= 0) {
    669 		ret = nl_socket_add_membership(state->nl_sock, mcid);
    670 		if (ret)
    671 			return ret;
    672 	}
    673 
    674 	/* MLME multicast group */
    675 	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme");
    676 	if (mcid >= 0) {
    677 		ret = nl_socket_add_membership(state->nl_sock, mcid);
    678 		if (ret)
    679 			return ret;
    680 	}
    681 
    682 	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "vendor");
    683 	if (mcid >= 0) {
    684 		ret = nl_socket_add_membership(state->nl_sock, mcid);
    685 		if (ret)
    686 			return ret;
    687 	}
    688 
    689 	return 0;
    690 }
    691 
    692 __u32 __do_listen_events(struct nl80211_state *state,
    693 			 const int n_waits, const __u32 *waits,
    694 			 struct print_event_args *args)
    695 {
    696 	struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
    697 	struct wait_event wait_ev;
    698 
    699 	if (!cb) {
    700 		fprintf(stderr, "failed to allocate netlink callbacks\n");
    701 		return -ENOMEM;
    702 	}
    703 
    704 	/* no sequence checking for multicast messages */
    705 	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
    706 
    707 	if (n_waits && waits) {
    708 		wait_ev.cmds = waits;
    709 		wait_ev.n_cmds = n_waits;
    710 		wait_ev.pargs = args;
    711 		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev);
    712 	} else
    713 		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args);
    714 
    715 	wait_ev.cmd = 0;
    716 
    717 	while (!wait_ev.cmd)
    718 		nl_recvmsgs(state->nl_sock, cb);
    719 
    720 	nl_cb_put(cb);
    721 
    722 	return wait_ev.cmd;
    723 }
    724 
    725 __u32 listen_events(struct nl80211_state *state,
    726 		    const int n_waits, const __u32 *waits)
    727 {
    728 	int ret;
    729 
    730 	ret = __prepare_listen_events(state);
    731 	if (ret)
    732 		return ret;
    733 
    734 	return __do_listen_events(state, n_waits, waits, NULL);
    735 }
    736 
    737 static int print_events(struct nl80211_state *state,
    738 			struct nl_cb *cb,
    739 			struct nl_msg *msg,
    740 			int argc, char **argv,
    741 			enum id_input id)
    742 {
    743 	struct print_event_args args;
    744 	int ret;
    745 
    746 	memset(&args, 0, sizeof(args));
    747 
    748 	argc--;
    749 	argv++;
    750 
    751 	while (argc > 0) {
    752 		if (strcmp(argv[0], "-f") == 0)
    753 			args.frame = true;
    754 		else if (strcmp(argv[0], "-t") == 0)
    755 			args.time = true;
    756 		else if (strcmp(argv[0], "-r") == 0)
    757 			args.reltime = true;
    758 		else
    759 			return 1;
    760 		argc--;
    761 		argv++;
    762 	}
    763 
    764 	if (args.time && args.reltime)
    765 		return 1;
    766 
    767 	if (argc)
    768 		return 1;
    769 
    770 	ret = __prepare_listen_events(state);
    771 	if (ret)
    772 		return ret;
    773 
    774 	return __do_listen_events(state, 0, NULL, &args);
    775 }
    776 TOPLEVEL(event, "[-t] [-r] [-f]", 0, 0, CIB_NONE, print_events,
    777 	"Monitor events from the kernel.\n"
    778 	"-t - print timestamp\n"
    779 	"-r - print relative timstamp\n"
    780 	"-f - print full frame for auth/assoc etc.");
    781