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