xref: /openwifi/user_space/sdrctl_src/sdrctl.c (revision 14124d7306727ee7bbae83655e3d9ecacc4c3bee)
1 /*
2  * nl80211 userspace tool
3  *
4  * Copyright 2007, 2008	Johannes Berg <[email protected]>
5  * Modified by Xianjun jiao. [email protected]; [email protected];
6  */
7 
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <net/if.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <stdbool.h>
17 
18 #include <netlink/genl/genl.h>
19 #include <netlink/genl/family.h>
20 #include <netlink/genl/ctrl.h>
21 #include <netlink/msg.h>
22 #include <netlink/attr.h>
23 
24 #include "nl80211.h"
25 #include "sdrctl.h"
26 
27 /* libnl 1.x compatibility code */
28 #if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
29 static inline struct nl_handle *nl_socket_alloc(void)
30 {
31 	return nl_handle_alloc();
32 }
33 
34 static inline void nl_socket_free(struct nl_sock *h)
35 {
36 	nl_handle_destroy(h);
37 }
38 
39 static inline int nl_socket_set_buffer_size(struct nl_sock *sk,
40 					    int rxbuf, int txbuf)
41 {
42 	return nl_set_buffer_size(sk, rxbuf, txbuf);
43 }
44 #endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */
45 
46 int iw_debug = 0;
47 
48 static int nl80211_init(struct nl80211_state *state)
49 {
50 	int err;
51 
52 	state->nl_sock = nl_socket_alloc();
53 	if (!state->nl_sock) {
54 		fprintf(stderr, "Failed to allocate netlink socket.\n");
55 		return -ENOMEM;
56 	}
57 
58 	nl_socket_set_buffer_size(state->nl_sock, 8192, 8192);
59 
60 	if (genl_connect(state->nl_sock)) {
61 		fprintf(stderr, "Failed to connect to generic netlink.\n");
62 		err = -ENOLINK;
63 		goto out_handle_destroy;
64 	}
65 
66 	state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211");
67 	if (state->nl80211_id < 0) {
68 		fprintf(stderr, "nl80211 not found.\n");
69 		err = -ENOENT;
70 		goto out_handle_destroy;
71 	}
72 
73 	return 0;
74 
75  out_handle_destroy:
76 	nl_socket_free(state->nl_sock);
77 	return err;
78 }
79 
80 static void nl80211_cleanup(struct nl80211_state *state)
81 {
82 	nl_socket_free(state->nl_sock);
83 }
84 
85 static int cmd_size;
86 
87 extern struct cmd __start___cmd;
88 extern struct cmd __stop___cmd;
89 
90 #define for_each_cmd(_cmd)					\
91 	for (_cmd = &__start___cmd; _cmd < &__stop___cmd;		\
92 	     _cmd = (const struct cmd *)((char *)_cmd + cmd_size))
93 
94 
95 static void __usage_cmd(const struct cmd *cmd, char *indent, bool full)
96 {
97 	const char *start, *lend, *end;
98 
99 	printf("%s", indent);
100 
101 	switch (cmd->idby) {
102 	case CIB_NONE:
103 		break;
104 	case CIB_PHY:
105 		printf("phy <phyname> ");
106 		break;
107 	case CIB_NETDEV:
108 		printf("dev <devname> ");
109 		break;
110 	case CIB_WDEV:
111 		printf("wdev <idx> ");
112 		break;
113 	}
114 	if (cmd->parent && cmd->parent->name)
115 		printf("%s ", cmd->parent->name);
116 	printf("%s", cmd->name);
117 
118 	if (cmd->args) {
119 		/* print line by line */
120 		start = cmd->args;
121 		end = strchr(start, '\0');
122 		printf(" ");
123 		do {
124 			lend = strchr(start, '\n');
125 			if (!lend)
126 				lend = end;
127 			if (start != cmd->args) {
128 				printf("\t");
129 				switch (cmd->idby) {
130 				case CIB_NONE:
131 					break;
132 				case CIB_PHY:
133 					printf("phy <phyname> ");
134 					break;
135 				case CIB_NETDEV:
136 					printf("dev <devname> ");
137 					break;
138 				case CIB_WDEV:
139 					printf("wdev <idx> ");
140 					break;
141 				}
142 				if (cmd->parent && cmd->parent->name)
143 					printf("%s ", cmd->parent->name);
144 				printf("%s ", cmd->name);
145 			}
146 			printf("%.*s\n", (int)(lend - start), start);
147 			start = lend + 1;
148 		} while (end != lend);
149 	} else
150 		printf("\n");
151 
152 	if (!full || !cmd->help)
153 		return;
154 
155 	/* hack */
156 	if (strlen(indent))
157 		indent = "\t\t";
158 	else
159 		printf("\n");
160 
161 	/* print line by line */
162 	start = cmd->help;
163 	end = strchr(start, '\0');
164 	do {
165 		lend = strchr(start, '\n');
166 		if (!lend)
167 			lend = end;
168 		printf("%s", indent);
169 		printf("%.*s\n", (int)(lend - start), start);
170 		start = lend + 1;
171 	} while (end != lend);
172 
173 	printf("\n");
174 }
175 
176 static void usage_options(void)
177 {
178 	printf("Options:\n");
179 	printf("\t--debug\t\tenable netlink debugging\n");
180 }
181 
182 static const char *argv0;
183 
184 static void usage(int argc, char **argv)
185 {
186 	const struct cmd *section, *cmd;
187 	bool full = argc >= 0;
188 	const char *sect_filt = NULL;
189 	const char *cmd_filt = NULL;
190 
191 	if (argc > 0)
192 		sect_filt = argv[0];
193 
194 	if (argc > 1)
195 		cmd_filt = argv[1];
196 
197 	printf("Usage:\t%s [options] command\n", argv0);
198 	usage_options();
199 	printf("\t--version\tshow version (%s)\n", sdrctl_version);
200 	printf("Commands:\n");
201 	for_each_cmd(section) {
202 		if (section->parent)
203 			continue;
204 
205 		if (sect_filt && strcmp(section->name, sect_filt))
206 			continue;
207 
208 		if (section->handler && !section->hidden)
209 			__usage_cmd(section, "\t", full);
210 
211 		for_each_cmd(cmd) {
212 			if (section != cmd->parent)
213 				continue;
214 			if (!cmd->handler || cmd->hidden)
215 				continue;
216 			if (cmd_filt && strcmp(cmd->name, cmd_filt))
217 				continue;
218 			__usage_cmd(cmd, "\t", full);
219 		}
220 	}
221 	printf("\nCommands that use the netdev ('dev') can also be given the\n"
222 	       "'wdev' instead to identify the device.\n");
223 	printf("\nYou can omit the 'phy' or 'dev' if "
224 			"the identification is unique,\n"
225 			"e.g. \"./sdrctl sdr0 get rssi_th\" or \"./sdrctl sdr0 get gap\". "
226 			"(Don't when scripting.)\n\n"
227 			"Do NOT screenscrape this tool, we don't "
228 			"consider its output stable.\n\n");
229 }
230 
231 static int print_help(struct nl80211_state *state,
232 		      struct nl_cb *cb,
233 		      struct nl_msg *msg,
234 		      int argc, char **argv,
235 		      enum id_input id)
236 {
237 	exit(3);
238 }
239 TOPLEVEL(help, "[command]", 0, 0, CIB_NONE, print_help,
240 	 "Print usage for all or a specific command, e.g.\n"
241 	 "\"help wowlan\" or \"help wowlan enable\".");
242 
243 static void usage_cmd(const struct cmd *cmd)
244 {
245 	printf("Usage:\t%s [options] ", argv0);
246 	__usage_cmd(cmd, "", true);
247 	usage_options();
248 }
249 
250 static void version(void)
251 {
252 	printf("iw version %s\n", sdrctl_version);
253 }
254 
255 static int phy_lookup(char *name)
256 {
257 	char buf[200];
258 	int fd, pos;
259 
260 	snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
261 
262 	fd = open(buf, O_RDONLY);
263 	if (fd < 0)
264 		return -1;
265 	pos = read(fd, buf, sizeof(buf) - 1);
266 	if (pos < 0) {
267 		close(fd);
268 		return -1;
269 	}
270 	buf[pos] = '\0';
271 	close(fd);
272 	return atoi(buf);
273 }
274 
275 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
276 			 void *arg)
277 {
278 	int *ret = arg;
279 	*ret = err->error;
280 	return NL_STOP;
281 }
282 
283 static int finish_handler(struct nl_msg *msg, void *arg)
284 {
285 	int *ret = arg;
286 	*ret = 0;
287 	return NL_SKIP;
288 }
289 
290 static int ack_handler(struct nl_msg *msg, void *arg)
291 {
292 	int *ret = arg;
293 	*ret = 0;
294 	return NL_STOP;
295 }
296 
297 static int __handle_cmd(struct nl80211_state *state, enum id_input idby,
298 			int argc, char **argv, const struct cmd **cmdout)
299 {
300 	const struct cmd *cmd, *match = NULL, *sectcmd;
301 	struct nl_cb *cb;
302 	struct nl_cb *s_cb;
303 	struct nl_msg *msg;
304 	signed long long devidx = 0;
305 	int err, o_argc;
306 	const char *command, *section;
307 	char *tmp, **o_argv;
308 	enum command_identify_by command_idby = CIB_NONE;
309 
310 	if (argc <= 1 && idby != II_NONE)
311 		return 1;
312 
313 	o_argc = argc;
314 	o_argv = argv;
315 
316 	switch (idby) {
317 	case II_PHY_IDX:
318 		command_idby = CIB_PHY;
319 		devidx = strtoul(*argv + 4, &tmp, 0);
320 		if (*tmp != '\0')
321 			return 1;
322 		argc--;
323 		argv++;
324 		break;
325 	case II_PHY_NAME:
326 		command_idby = CIB_PHY;
327 		devidx = phy_lookup(*argv);
328 		argc--;
329 		argv++;
330 		break;
331 	case II_NETDEV:
332 		command_idby = CIB_NETDEV;
333 		devidx = if_nametoindex(*argv);
334 		if (devidx == 0)
335 			devidx = -1;
336 		argc--;
337 		argv++;
338 		break;
339 	case II_WDEV:
340 		command_idby = CIB_WDEV;
341 		devidx = strtoll(*argv, &tmp, 0);
342 		if (*tmp != '\0')
343 			return 1;
344 		argc--;
345 		argv++;
346 	default:
347 		break;
348 	}
349 
350 	if (devidx < 0)
351 		return -errno;
352 
353 	section = *argv;
354 	argc--;
355 	argv++;
356 
357 	for_each_cmd(sectcmd) {
358 		if (sectcmd->parent)
359 			continue;
360 		/* ok ... bit of a hack for the dupe 'info' section */
361 		if (match && sectcmd->idby != command_idby)
362 			continue;
363 		if (strcmp(sectcmd->name, section) == 0)
364 			match = sectcmd;
365 	}
366 
367 	sectcmd = match;
368 	match = NULL;
369 	if (!sectcmd)
370 		return 1;
371 
372 	if (argc > 0) {
373 		command = *argv;
374 
375 		for_each_cmd(cmd) {
376 			if (!cmd->handler)
377 				continue;
378 			if (cmd->parent != sectcmd)
379 				continue;
380 			/*
381 			 * ignore mismatch id by, but allow WDEV
382 			 * in place of NETDEV
383 			 */
384 			if (cmd->idby != command_idby &&
385 			    !(cmd->idby == CIB_NETDEV &&
386 			      command_idby == CIB_WDEV))
387 				continue;
388 			if (strcmp(cmd->name, command))
389 				continue;
390 			if (argc > 1 && !cmd->args)
391 				continue;
392 			match = cmd;
393 			break;
394 		}
395 
396 		if (match) {
397 			argc--;
398 			argv++;
399 		}
400 	}
401 
402 	if (match)
403 		cmd = match;
404 	else {
405 		/* Use the section itself, if possible. */
406 		cmd = sectcmd;
407 		if (argc && !cmd->args)
408 			return 1;
409 		if (cmd->idby != command_idby &&
410 		    !(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV))
411 			return 1;
412 		if (!cmd->handler)
413 			return 1;
414 	}
415 
416 	if (cmd->selector) {
417 		cmd = cmd->selector(argc, argv);
418 		if (!cmd)
419 			return 1;
420 	}
421 
422 	if (cmdout)
423 		*cmdout = cmd;
424 
425 	if (!cmd->cmd) {
426 		argc = o_argc;
427 		argv = o_argv;
428 		return cmd->handler(state, NULL, NULL, argc, argv, idby);
429 	}
430 
431 	msg = nlmsg_alloc();
432 	if (!msg) {
433 		fprintf(stderr, "failed to allocate netlink message\n");
434 		return 2;
435 	}
436 
437 	cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
438 	s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
439 	if (!cb || !s_cb) {
440 		fprintf(stderr, "failed to allocate netlink callbacks\n");
441 		err = 2;
442 		goto out_free_msg;
443 	}
444 
445 	genlmsg_put(msg, 0, 0, state->nl80211_id, 0,
446 		    cmd->nl_msg_flags, cmd->cmd, 0);
447 
448 	switch (command_idby) {
449 	case CIB_PHY:
450 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
451 		break;
452 	case CIB_NETDEV:
453 		NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
454 		break;
455 	case CIB_WDEV:
456 		NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx);
457 		break;
458 	default:
459 		break;
460 	}
461 
462 	err = cmd->handler(state, cb, msg, argc, argv, idby);
463 	if (err)
464 		goto out;
465 
466 	nl_socket_set_cb(state->nl_sock, s_cb);
467 
468 	err = nl_send_auto_complete(state->nl_sock, msg);
469 	if (err < 0)
470 		goto out;
471 
472 	err = 1;
473 
474 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
475 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
476 	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
477 
478 	while (err > 0)
479 		nl_recvmsgs(state->nl_sock, cb);
480  out:
481 	nl_cb_put(cb);
482  out_free_msg:
483 	nlmsg_free(msg);
484 	return err;
485  nla_put_failure:
486 	fprintf(stderr, "building message failed\n");
487 	return 2;
488 }
489 
490 int handle_cmd(struct nl80211_state *state, enum id_input idby,
491 	       int argc, char **argv)
492 {
493 	return __handle_cmd(state, idby, argc, argv, NULL);
494 }
495 
496 int main(int argc, char **argv)
497 {
498 	struct nl80211_state nlstate;
499 	int err;
500 	const struct cmd *cmd = NULL;
501 
502 	/* calculate command size including padding */
503 	cmd_size = abs((long)&__section_set - (long)&__section_get);
504 	/* strip off self */
505 	argc--;
506 	argv0 = *argv++;
507 
508 	if (argc > 0 && strcmp(*argv, "--debug") == 0) {
509 		iw_debug = 1;
510 		argc--;
511 		argv++;
512 	}
513 
514 	if (argc > 0 && strcmp(*argv, "--version") == 0) {
515 		version();
516 		return 0;
517 	}
518 
519 	/* need to treat "help" command specially so it works w/o nl80211 */
520 	if (argc == 0 || strcmp(*argv, "help") == 0) {
521 		usage(argc - 1, argv + 1);
522 		return 0;
523 	}
524 
525 	err = nl80211_init(&nlstate);
526 	if (err)
527 		return 1;
528 
529 	if (strcmp(*argv, "dev") == 0 && argc > 1) {
530 		argc--;
531 		argv++;
532 		err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd);
533 	} else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) {
534 		if (strlen(*argv) == 3) {
535 			argc--;
536 			argv++;
537 			err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd);
538 		} else if (*(*argv + 3) == '#')
539 			err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd);
540 		else
541 			goto detect;
542 	} else if (strcmp(*argv, "wdev") == 0 && argc > 1) {
543 		argc--;
544 		argv++;
545 		err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd);
546 	} else {
547 		int idx;
548 		enum id_input idby = II_NONE;
549  detect:
550 		if ((idx = if_nametoindex(argv[0])) != 0)
551 			idby = II_NETDEV;
552 		else if ((idx = phy_lookup(argv[0])) >= 0)
553 			idby = II_PHY_NAME;
554 		err = __handle_cmd(&nlstate, idby, argc, argv, &cmd);
555 	}
556 
557 	if (err == 1) {
558 		if (cmd)
559 			usage_cmd(cmd);
560 		else
561 			usage(0, NULL);
562 	} else if (err < 0)
563 		fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err);
564 
565 	nl80211_cleanup(&nlstate);
566 
567 	return err;
568 }
569