event.c revision 7988b229c39959236718e61cd13f62f25736ec17
1#include <stdint.h>
2#include <stdbool.h>
3#include <net/if.h>
4#include <errno.h>
5#include "iw.h"
6
7static int no_seq_check(struct nl_msg *msg, void *arg)
8{
9	return NL_OK;
10}
11
12struct ieee80211_beacon_channel {
13	__u16 center_freq;
14	bool passive_scan;
15	bool no_ibss;
16};
17
18static 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_PASSIVE_SCAN] = { .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_PASSIVE_SCAN])
37		chan->passive_scan = true;
38	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
39		chan->no_ibss = true;
40
41	return 0;
42}
43
44static 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		break;
84	case 0xa0: /* disassoc */
85	case 0xc0: /* deauth */
86		/* reason */
87		tmp = (frame[25] << 8) + frame[24];
88		printf(" reason %d: %s", tmp, get_reason_str(tmp));
89		break;
90	}
91
92	if (!args->frame)
93		return;
94
95	printf(" [frame:");
96
97 print_frame:
98	for (i = 0; i < len; i++)
99		printf(" %.02x", frame[i]);
100	printf("]");
101}
102
103static void parse_cqm_event(struct nlattr *tb)
104{
105	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
106		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
107		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
108		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
109	};
110	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
111
112	printf("connection quality monitor event: ");
113
114	if (!tb || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb, cqm_policy)) {
115		printf("missing data!\n");
116		return;
117	}
118
119	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) {
120		enum nl80211_cqm_rssi_threshold_event rssi_event;
121		rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
122		if (rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)
123			printf("RSSI went above threshold");
124		else
125			printf("RSSI went below threshold");
126	}
127	printf("\n");
128}
129
130
131static int print_event(struct nl_msg *msg, void *arg)
132{
133#define PARSE_BEACON_CHAN(_attr, _chan) do { \
134	r = parse_beacon_hint_chan(tb[_attr], \
135				   &_chan); \
136	if (r) \
137		return NL_SKIP; \
138} while (0)
139	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
140	struct nlattr *tb[NL80211_ATTR_MAX + 1], *nst;
141	struct print_event_args *args = arg;
142	char ifname[100];
143	char macbuf[6*3];
144	__u8 reg_type;
145	struct ieee80211_beacon_channel chan_before_beacon,  chan_after_beacon;
146	__u32 wiphy_idx = 0;
147	int r;
148	int rem_nst;
149	__u16 status;
150
151	if (args->time) {
152		struct timeval tv;
153		gettimeofday(&tv, NULL);
154		printf("%ld.%06u: ", (long) tv.tv_sec, (unsigned int) tv.tv_usec);
155	}
156
157	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
158		  genlmsg_attrlen(gnlh, 0), NULL);
159
160	if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) {
161		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
162		printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY]));
163	} else if (tb[NL80211_ATTR_IFINDEX]) {
164		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
165		printf("%s: ", ifname);
166	} else if (tb[NL80211_ATTR_WIPHY]) {
167		printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
168	}
169
170	switch (gnlh->cmd) {
171	case NL80211_CMD_NEW_WIPHY:
172		printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]));
173		break;
174	case NL80211_CMD_TRIGGER_SCAN:
175		printf("scan started\n");
176		break;
177	case NL80211_CMD_NEW_SCAN_RESULTS:
178		printf("scan finished:");
179	case NL80211_CMD_SCAN_ABORTED:
180		if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED)
181			printf("scan aborted:");
182		if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
183			nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem_nst)
184				printf(" %d", nla_get_u32(nst));
185			printf(",");
186		}
187		if (tb[NL80211_ATTR_SCAN_SSIDS]) {
188			nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_SSIDS], rem_nst) {
189				printf(" \"");
190				print_ssid_escaped(nla_len(nst), nla_data(nst));
191				printf("\"");
192			}
193		}
194		printf("\n");
195		break;
196	case NL80211_CMD_REG_CHANGE:
197		printf("regulatory domain change: ");
198
199		reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
200
201		switch (reg_type) {
202		case NL80211_REGDOM_TYPE_COUNTRY:
203			printf("set to %s by %s request",
204			       nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
205			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
206			if (tb[NL80211_ATTR_WIPHY])
207				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
208			break;
209		case NL80211_REGDOM_TYPE_WORLD:
210			printf("set to world roaming by %s request",
211			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
212			break;
213		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
214			printf("custom world roaming rules in place on phy%d by %s request",
215			       nla_get_u32(tb[NL80211_ATTR_WIPHY]),
216			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
217			break;
218		case NL80211_REGDOM_TYPE_INTERSECTION:
219			printf("intersection used due to a request made by %s",
220			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
221			if (tb[NL80211_ATTR_WIPHY])
222				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
223			break;
224		default:
225			printf("unknown source (upgrade this utility)");
226			break;
227		}
228
229		printf("\n");
230		break;
231	case NL80211_CMD_REG_BEACON_HINT:
232
233		wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
234
235		memset(&chan_before_beacon, 0, sizeof(chan_before_beacon));
236		memset(&chan_after_beacon, 0, sizeof(chan_after_beacon));
237
238		PARSE_BEACON_CHAN(NL80211_ATTR_FREQ_BEFORE, chan_before_beacon);
239		PARSE_BEACON_CHAN(NL80211_ATTR_FREQ_AFTER, chan_after_beacon);
240
241		if (chan_before_beacon.center_freq != chan_after_beacon.center_freq)
242			break;
243
244		/* A beacon hint is sent _only_ if something _did_ change */
245		printf("beacon hint:\n");
246
247		printf("phy%d %d MHz [%d]:\n",
248		       wiphy_idx,
249		       chan_before_beacon.center_freq,
250		       ieee80211_frequency_to_channel(chan_before_beacon.center_freq));
251
252		if (chan_before_beacon.passive_scan && !chan_after_beacon.passive_scan)
253			printf("\to active scanning enabled\n");
254		if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss)
255			printf("\to beaconing enabled\n");
256
257		break;
258	case NL80211_CMD_NEW_STATION:
259		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
260		printf("new station %s\n", macbuf);
261		break;
262	case NL80211_CMD_JOIN_IBSS:
263		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
264		printf("IBSS %s joined\n", macbuf);
265		break;
266	case NL80211_CMD_AUTHENTICATE:
267		printf("auth");
268		if (tb[NL80211_ATTR_FRAME])
269			print_frame(args, tb[NL80211_ATTR_FRAME]);
270		else if (tb[NL80211_ATTR_TIMED_OUT])
271			printf(": timed out");
272		else
273			printf(": unknown event");
274		printf("\n");
275		break;
276	case NL80211_CMD_ASSOCIATE:
277		printf("assoc");
278		if (tb[NL80211_ATTR_FRAME])
279			print_frame(args, tb[NL80211_ATTR_FRAME]);
280		else if (tb[NL80211_ATTR_TIMED_OUT])
281			printf(": timed out");
282		else
283			printf(": unknown event");
284		printf("\n");
285		break;
286	case NL80211_CMD_DEAUTHENTICATE:
287		printf("deauth");
288		print_frame(args, tb[NL80211_ATTR_FRAME]);
289		printf("\n");
290		break;
291	case NL80211_CMD_DISASSOCIATE:
292		printf("disassoc");
293		print_frame(args, tb[NL80211_ATTR_FRAME]);
294		printf("\n");
295		break;
296	case NL80211_CMD_CONNECT:
297		status = 0;
298		if (!tb[NL80211_ATTR_STATUS_CODE])
299			printf("unknown connect status");
300		else if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) == 0)
301			printf("connected");
302		else {
303			status = nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]);
304			printf("failed to connect");
305		}
306		if (tb[NL80211_ATTR_MAC]) {
307			mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
308			printf(" to %s", macbuf);
309		}
310		if (status)
311			printf(", status: %d: %s", status, get_status_str(status));
312		printf("\n");
313		break;
314	case NL80211_CMD_ROAM:
315		printf("roamed");
316		if (tb[NL80211_ATTR_MAC]) {
317			mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
318			printf(" to %s", macbuf);
319		}
320		printf("\n");
321		break;
322	case NL80211_CMD_DISCONNECT:
323		printf("disconnected");
324		if (tb[NL80211_ATTR_DISCONNECTED_BY_AP])
325			printf(" (by AP)");
326		else
327			printf(" (local request)");
328		if (tb[NL80211_ATTR_REASON_CODE])
329			printf(" reason: %d: %s", nla_get_u16(tb[NL80211_ATTR_REASON_CODE]),
330				get_reason_str(nla_get_u16(tb[NL80211_ATTR_REASON_CODE])));
331		printf("\n");
332		break;
333	case NL80211_CMD_REMAIN_ON_CHANNEL:
334		printf("remain on freq %d (%dms, cookie %llx)\n",
335			nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
336			nla_get_u32(tb[NL80211_ATTR_DURATION]),
337			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
338		break;
339	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
340		printf("done with remain on freq %d (cookie %llx)\n",
341			nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
342			(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
343		break;
344	case NL80211_CMD_NOTIFY_CQM:
345		parse_cqm_event(tb[NL80211_ATTR_CQM]);
346		break;
347	default:
348		printf("unknown event %d\n", gnlh->cmd);
349		break;
350	}
351
352	return NL_SKIP;
353#undef PARSE_BEACON_CHAN
354}
355
356struct wait_event {
357	int n_cmds;
358	const __u32 *cmds;
359	__u32 cmd;
360	struct print_event_args *pargs;
361};
362
363static int wait_event(struct nl_msg *msg, void *arg)
364{
365	struct wait_event *wait = arg;
366	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
367	int i;
368
369	for (i = 0; i < wait->n_cmds; i++) {
370		if (gnlh->cmd == wait->cmds[i]) {
371			wait->cmd = gnlh->cmd;
372		if (wait->pargs)
373			print_event(msg, wait->pargs);
374		}
375	}
376
377	return NL_SKIP;
378}
379
380__u32 __listen_events(struct nl80211_state *state,
381		      const int n_waits, const __u32 *waits,
382		      struct print_event_args *args)
383{
384	int mcid, ret;
385	struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
386	struct wait_event wait_ev;
387
388	if (!cb) {
389		fprintf(stderr, "failed to allocate netlink callbacks\n");
390		return -ENOMEM;
391	}
392
393	/* Configuration multicast group */
394	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config");
395	if (mcid < 0)
396		return mcid;
397
398	ret = nl_socket_add_membership(state->nl_sock, mcid);
399	if (ret)
400		return ret;
401
402	/* Scan multicast group */
403	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan");
404	if (mcid >= 0) {
405		ret = nl_socket_add_membership(state->nl_sock, mcid);
406		if (ret)
407			return ret;
408	}
409
410	/* Regulatory multicast group */
411	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory");
412	if (mcid >= 0) {
413		ret = nl_socket_add_membership(state->nl_sock, mcid);
414		if (ret)
415			return ret;
416	}
417
418	/* MLME multicast group */
419	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme");
420	if (mcid >= 0) {
421		ret = nl_socket_add_membership(state->nl_sock, mcid);
422		if (ret)
423			return ret;
424	}
425
426	/* no sequence checking for multicast messages */
427	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
428
429	if (n_waits && waits) {
430		wait_ev.cmds = waits;
431		wait_ev.n_cmds = n_waits;
432		wait_ev.pargs = args;
433		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev);
434	} else
435		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args);
436
437	wait_ev.cmd = 0;
438
439	while (!wait_ev.cmd)
440		nl_recvmsgs(state->nl_sock, cb);
441
442	nl_cb_put(cb);
443
444	return wait_ev.cmd;
445}
446
447__u32 listen_events(struct nl80211_state *state,
448		    const int n_waits, const __u32 *waits)
449{
450	return __listen_events(state, n_waits, waits, NULL);
451}
452
453static int print_events(struct nl80211_state *state,
454			struct nl_cb *cb,
455			struct nl_msg *msg,
456			int argc, char **argv)
457{
458	struct print_event_args args;
459
460	memset(&args, 0, sizeof(args));
461
462	argc--;
463	argv++;
464
465	while (argc > 0) {
466		if (strcmp(argv[0], "-f") == 0)
467			args.frame = true;
468		else if (strcmp(argv[0], "-t") == 0)
469			args.time = true;
470		else
471			return 1;
472		argc--;
473		argv++;
474	}
475
476	if (argc)
477		return 1;
478
479	return __listen_events(state, 0, NULL, &args);
480}
481TOPLEVEL(event, "[-t] [-f]", 0, 0, CIB_NONE, print_events,
482	"Monitor events from the kernel.\n"
483	"-t - print timestamp\n"
484	"-f - print full frame for auth/assoc etc.");
485