event.c revision 01ae06f9e9d2bf3e7e998bcbda06fd916c92f34e
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 print_event_args {
13	bool frame, time;
14};
15
16static void print_frame(struct print_event_args *args, struct nlattr *attr)
17{
18	uint8_t *frame;
19	size_t len;
20	int i;
21	char macbuf[6*3];
22	uint16_t tmp;
23
24	if (!attr)
25		printf(" [no frame]");
26
27	frame = nla_data(attr);
28	len = nla_len(attr);
29
30	if (len < 26) {
31		printf(" [invalid frame: ");
32		goto print_frame;
33	}
34
35	mac_addr_n2a(macbuf, frame + 10);
36	printf(" %s -> ", macbuf);
37	mac_addr_n2a(macbuf, frame + 4);
38	printf("%s", macbuf);
39
40	switch (frame[0] & 0xfc) {
41	case 0x10: /* assoc resp */
42	case 0x30: /* reassoc resp */
43		/* status */
44		tmp = (frame[27] << 8) + frame[26];
45		printf(" status: %d: %s", tmp, get_status_str(tmp));
46		break;
47	case 0x00: /* assoc req */
48	case 0x20: /* reassoc req */
49		break;
50	case 0xb0: /* auth */
51		/* status */
52		tmp = (frame[29] << 8) + frame[28];
53		printf(" status: %d: %s", tmp, get_status_str(tmp));
54		break;
55		break;
56	case 0xa0: /* disassoc */
57	case 0xc0: /* deauth */
58		/* reason */
59		tmp = (frame[25] << 8) + frame[24];
60		printf(" reason %d: %s", tmp, get_reason_str(tmp));
61		break;
62	}
63
64	if (!args->frame)
65		return;
66
67	printf(" [frame:");
68
69 print_frame:
70	for (i = 0; i < len; i++)
71		printf(" %.02x", frame[i]);
72	printf("]");
73}
74
75static int print_event(struct nl_msg *msg, void *arg)
76{
77	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
78	struct nlattr *tb[NL80211_ATTR_MAX + 1];
79	struct print_event_args *args = arg;
80	char ifname[100];
81	char macbuf[6*3];
82	__u8 reg_type;
83
84	if (args->time) {
85		struct timeval tv;
86		gettimeofday(&tv, NULL);
87		printf("%ld.%06u: ", (long) tv.tv_sec, (unsigned int) tv.tv_usec);
88	}
89
90	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
91		  genlmsg_attrlen(gnlh, 0), NULL);
92
93	if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) {
94		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
95		printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY]));
96	} else if (tb[NL80211_ATTR_IFINDEX]) {
97		if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
98		printf("%s: ", ifname);
99	} else if (tb[NL80211_ATTR_WIPHY]) {
100		printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
101	}
102
103	switch (gnlh->cmd) {
104	case NL80211_CMD_NEW_WIPHY:
105		printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]));
106		break;
107	case NL80211_CMD_NEW_SCAN_RESULTS:
108		printf("scan finished\n");
109		break;
110	case NL80211_CMD_SCAN_ABORTED:
111		printf("scan aborted\n");
112		break;
113	case NL80211_CMD_REG_CHANGE:
114		printf("regulatory domain change: ");
115
116		reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
117
118		switch (reg_type) {
119		case NL80211_REGDOM_TYPE_COUNTRY:
120			printf("set to %s by %s request",
121			       nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
122			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
123			if (tb[NL80211_ATTR_WIPHY])
124				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
125			break;
126		case NL80211_REGDOM_TYPE_WORLD:
127			printf("set to world roaming by %s request",
128			       reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
129			break;
130		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
131			printf("custom world roaming rules in place on phy%d by %s request",
132			       nla_get_u32(tb[NL80211_ATTR_WIPHY]),
133			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
134			break;
135		case NL80211_REGDOM_TYPE_INTERSECTION:
136			printf("intersection used due to a request made by %s",
137			       reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
138			if (tb[NL80211_ATTR_WIPHY])
139				printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
140			break;
141		default:
142			printf("unknown source (upgrade this utility)");
143			break;
144		}
145
146		printf("\n");
147		break;
148	case NL80211_CMD_JOIN_IBSS:
149		mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
150		printf("IBSS %s joined\n", macbuf);
151		break;
152	case NL80211_CMD_AUTHENTICATE:
153		printf("auth");
154		print_frame(args, tb[NL80211_ATTR_FRAME]);
155		printf("\n");
156		break;
157	case NL80211_CMD_ASSOCIATE:
158		printf("assoc");
159		print_frame(args, tb[NL80211_ATTR_FRAME]);
160		printf("\n");
161		break;
162	case NL80211_CMD_DEAUTHENTICATE:
163		printf("deauth");
164		print_frame(args, tb[NL80211_ATTR_FRAME]);
165		printf("\n");
166		break;
167	case NL80211_CMD_DISASSOCIATE:
168		printf("disassoc");
169		print_frame(args, tb[NL80211_ATTR_FRAME]);
170		printf("\n");
171		break;
172	default:
173		printf("unknown event %d\n", gnlh->cmd);
174		break;
175	}
176
177	return NL_SKIP;
178}
179
180struct wait_event {
181	int n_cmds;
182	const __u32 *cmds;
183	__u32 cmd;
184};
185
186static int wait_event(struct nl_msg *msg, void *arg)
187{
188	struct wait_event *wait = arg;
189	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
190	int i;
191
192	for (i = 0; i < wait->n_cmds; i++) {
193		if (gnlh->cmd == wait->cmds[i]) {
194			wait->cmd = gnlh->cmd;
195		}
196	}
197
198	return NL_SKIP;
199}
200
201static __u32 __listen_events(struct nl80211_state *state,
202			     const int n_waits, const __u32 *waits,
203			     struct print_event_args *args)
204{
205	int mcid, ret;
206	struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
207	struct wait_event wait_ev;
208
209	if (!cb) {
210		fprintf(stderr, "failed to allocate netlink callbacks\n");
211		return -ENOMEM;
212	}
213
214	/* Configuration multicast group */
215	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config");
216	if (mcid < 0)
217		return mcid;
218
219	ret = nl_socket_add_membership(state->nl_sock, mcid);
220	if (ret)
221		return ret;
222
223	/* Scan multicast group */
224	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan");
225	if (mcid >= 0) {
226		ret = nl_socket_add_membership(state->nl_sock, mcid);
227		if (ret)
228			return ret;
229	}
230
231	/* Regulatory multicast group */
232	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory");
233	if (mcid >= 0) {
234		ret = nl_socket_add_membership(state->nl_sock, mcid);
235		if (ret)
236			return ret;
237	}
238
239	/* MLME multicast group */
240	mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme");
241	if (mcid >= 0) {
242		ret = nl_socket_add_membership(state->nl_sock, mcid);
243		if (ret)
244			return ret;
245	}
246
247	/* no sequence checking for multicast messages */
248	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
249
250	if (n_waits && waits) {
251		wait_ev.cmds = waits;
252		wait_ev.n_cmds = n_waits;
253		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev);
254	} else {
255		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args);
256	}
257
258	wait_ev.cmd = 0;
259
260	while (!wait_ev.cmd)
261		nl_recvmsgs(state->nl_sock, cb);
262
263	nl_cb_put(cb);
264
265	return wait_ev.cmd;
266}
267
268__u32 listen_events(struct nl80211_state *state,
269		    const int n_waits, const __u32 *waits)
270{
271	return __listen_events(state, n_waits, waits, NULL);
272}
273
274static int print_events(struct nl80211_state *state,
275			struct nl_cb *cb,
276			struct nl_msg *msg,
277			int argc, char **argv)
278{
279	struct print_event_args args;
280
281	memset(&args, 0, sizeof(args));
282
283	argc--;
284	argv++;
285
286	while (argc > 0) {
287		if (strcmp(argv[0], "-f") == 0)
288			args.frame = true;
289		else if (strcmp(argv[0], "-t") == 0)
290			args.time = true;
291		else
292			return 1;
293		argc--;
294		argv++;
295	}
296
297	if (argc)
298		return 1;
299
300	return __listen_events(state, 0, NULL, &args);
301}
302TOPLEVEL(event, "[-t] [-f]", 0, 0, CIB_NONE, print_events,
303	"Monitor events from the kernel.\n"
304	"-t - print timestamp\n"
305	"-f - print full frame for auth/assoc etc.");
306