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