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) 30 static inline struct nl_handle *nl_socket_alloc(void) 31 { 32 return nl_handle_alloc(); 33 } 34 35 static inline void nl_socket_free(struct nl_sock *h) 36 { 37 nl_handle_destroy(h); 38 } 39 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 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 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 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 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 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 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 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 251 static void version(void) 252 { 253 printf("iw version %s\n", sdrctl_version); 254 } 255 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 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 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 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 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 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 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