X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=iw.c;h=da71617921d8a3c0a8e7c8c05f4e76dcba4987c0;hb=b5c0c336060e59866aa704e76011cbd39b5fe1b4;hp=accf2721b5daacc84b3394a18b9228990208a59c;hpb=59bafb08326f895623c0100b9dbc6c715cb6095d;p=thirdparty%2Fiw.git diff --git a/iw.c b/iw.c index accf272..da71617 100644 --- a/iw.c +++ b/iw.c @@ -13,19 +13,19 @@ #include #include #include - +#include + #include #include -#include +#include #include #include #include "nl80211.h" #include "iw.h" -#ifndef CONFIG_LIBNL20 -/* libnl 2.0 compatibility code */ - +/* libnl 1.x compatibility code */ +#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30) static inline struct nl_handle *nl_socket_alloc(void) { return nl_handle_alloc(); @@ -36,16 +36,12 @@ static inline void nl_socket_free(struct nl_sock *h) nl_handle_destroy(h); } -static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache) +static inline int nl_socket_set_buffer_size(struct nl_sock *sk, + int rxbuf, int txbuf) { - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; + return nl_set_buffer_size(sk, rxbuf, txbuf); } -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache -#endif /* CONFIG_LIBNL20 */ +#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */ int iw_debug = 0; @@ -65,23 +61,22 @@ static int nl80211_init(struct nl80211_state *state) goto out_handle_destroy; } - if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) { - fprintf(stderr, "Failed to allocate generic netlink cache.\n"); - err = -ENOMEM; - goto out_handle_destroy; - } + nl_socket_set_buffer_size(state->nl_sock, 8192, 8192); - state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); - if (!state->nl80211) { + /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ + err = 1; + setsockopt(nl_socket_get_fd(state->nl_sock), SOL_NETLINK, + NETLINK_EXT_ACK, &err, sizeof(err)); + + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); + if (state->nl80211_id < 0) { fprintf(stderr, "nl80211 not found.\n"); err = -ENOENT; - goto out_cache_free; + goto out_handle_destroy; } return 0; - out_cache_free: - nl_cache_free(state->nl_cache); out_handle_destroy: nl_socket_free(state->nl_sock); return err; @@ -89,38 +84,75 @@ static int nl80211_init(struct nl80211_state *state) static void nl80211_cleanup(struct nl80211_state *state) { - genl_family_put(state->nl80211); - nl_cache_free(state->nl_cache); nl_socket_free(state->nl_sock); } -__COMMAND(NULL, NULL, "", NULL, 0, 0, 0, CIB_NONE, NULL, NULL); -__COMMAND(NULL, NULL, "", NULL, 1, 0, 0, CIB_NONE, NULL, NULL); - static int cmd_size; -static void __usage_cmd(struct cmd *cmd, char *indent, bool full) +extern struct cmd *__start___cmd[]; +extern struct cmd *__stop___cmd; + +#define for_each_cmd(_cmd, i) \ + for (i = 0; i < &__stop___cmd - __start___cmd; i++) \ + if ((_cmd = __start___cmd[i])) + + +static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) { const char *start, *lend, *end; - fprintf(stderr, "%s", indent); + printf("%s", indent); switch (cmd->idby) { case CIB_NONE: break; case CIB_PHY: - fprintf(stderr, "phy "); + printf("phy "); break; case CIB_NETDEV: - fprintf(stderr, "dev "); + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); break; } - if (cmd->section) - fprintf(stderr, "%s ", cmd->section); - fprintf(stderr, "%s", cmd->name); - if (cmd->args) - fprintf(stderr, " %s", cmd->args); - fprintf(stderr, "\n"); + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s", cmd->name); + + if (cmd->args) { + /* print line by line */ + start = cmd->args; + end = strchr(start, '\0'); + printf(" "); + do { + lend = strchr(start, '\n'); + if (!lend) + lend = end; + if (start != cmd->args) { + printf("\t"); + switch (cmd->idby) { + case CIB_NONE: + break; + case CIB_PHY: + printf("phy "); + break; + case CIB_NETDEV: + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); + break; + } + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s ", cmd->name); + } + printf("%.*s\n", (int)(lend - start), start); + start = lend + 1; + } while (end != lend); + } else + printf("\n"); if (!full || !cmd->help) return; @@ -129,7 +161,7 @@ static void __usage_cmd(struct cmd *cmd, char *indent, bool full) if (strlen(indent)) indent = "\t\t"; else - fprintf(stderr, "\n"); + printf("\n"); /* print line by line */ start = cmd->help; @@ -138,55 +170,84 @@ static void __usage_cmd(struct cmd *cmd, char *indent, bool full) lend = strchr(start, '\n'); if (!lend) lend = end; - fprintf(stderr, "%s", indent); - fprintf(stderr, "%.*s\n", (int)(lend - start), start); + printf("%s", indent); + printf("%.*s\n", (int)(lend - start), start); start = lend + 1; } while (end != lend); - fprintf(stderr, "\n"); + printf("\n"); } static void usage_options(void) { - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\t--debug\t\tenable netlink debugging\n"); + printf("Options:\n"); + printf("\t--debug\t\tenable netlink debugging\n"); } static const char *argv0; -static void usage(bool full) +static void usage(int argc, char **argv) { - struct cmd *cmd; + const struct cmd *section, *cmd; + bool full = argc >= 0; + const char *sect_filt = NULL; + const char *cmd_filt = NULL; + unsigned int i, j; - fprintf(stderr, "Usage:\t%s [options] command\n", argv0); + if (argc > 0) + sect_filt = argv[0]; + + if (argc > 1) + cmd_filt = argv[1]; + + printf("Usage:\t%s [options] command\n", argv0); usage_options(); - fprintf(stderr, "\t--version\tshow version (%s)\n", iw_version); - fprintf(stderr, "Commands:\n"); - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler || cmd->hidden) + printf("\t--version\tshow version (%s)\n", iw_version); + printf("Commands:\n"); + for_each_cmd(section, i) { + if (section->parent) continue; - __usage_cmd(cmd, "\t", full); + + if (sect_filt && strcmp(section->name, sect_filt)) + continue; + + if (section->handler && !section->hidden) + __usage_cmd(section, "\t", full); + + for_each_cmd(cmd, j) { + if (section != cmd->parent) + continue; + if (!cmd->handler || cmd->hidden) + continue; + if (cmd_filt && strcmp(cmd->name, cmd_filt)) + continue; + __usage_cmd(cmd, "\t", full); + } } - fprintf(stderr, "\nYou can omit the 'phy' or 'dev' if " + printf("\nCommands that use the netdev ('dev') can also be given the\n" + "'wdev' instead to identify the device.\n"); + printf("\nYou can omit the 'phy' or 'dev' if " "the identification is unique,\n" "e.g. \"iw wlan0 info\" or \"iw phy0 info\". " - "(Don't when scripting.)\n\n"); + "(Don't when scripting.)\n\n" + "Do NOT screenscrape this tool, we don't " + "consider its output stable.\n\n"); } static int print_help(struct nl80211_state *state, - struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { exit(3); } -TOPLEVEL(help, NULL, 0, 0, CIB_NONE, print_help, - "Print usage for each command."); +TOPLEVEL(help, "[command]", 0, 0, CIB_NONE, print_help, + "Print usage for all or a specific command, e.g.\n" + "\"help wowlan\" or \"help wowlan enable\"."); -static void usage_cmd(struct cmd *cmd) +static void usage_cmd(const struct cmd *cmd) { - fprintf(stderr, "Usage:\t%s [options] ", argv0); + printf("Usage:\t%s [options] ", argv0); __usage_cmd(cmd, "", true); usage_options(); } @@ -207,17 +268,47 @@ static int phy_lookup(char *name) if (fd < 0) return -1; pos = read(fd, buf, sizeof(buf) - 1); - if (pos < 0) + if (pos < 0) { + close(fd); return -1; + } buf[pos] = '\0'; + close(fd); return atoi(buf); } static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; + int len = nlh->nlmsg_len; + struct nlattr *attrs; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; int *ret = arg; + int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); + *ret = err->error; + + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return NL_STOP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + ack_len += err->msg.nlmsg_len - sizeof(*nlh); + + if (len <= ack_len) + return NL_STOP; + + attrs = (void *)((unsigned char *)nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) { + len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]), + nla_len(tb[NLMSGERR_ATTR_MSG])); + fprintf(stderr, "kernel reports: %*s\n", len, + (char *)nla_data(tb[NLMSGERR_ATTR_MSG])); + } + return NL_STOP; } @@ -235,14 +326,32 @@ static int ack_handler(struct nl_msg *msg, void *arg) return NL_STOP; } +static int (*registered_handler)(struct nl_msg *, void *); +static void *registered_handler_data; + +void register_handler(int (*handler)(struct nl_msg *, void *), void *data) +{ + registered_handler = handler; + registered_handler_data = data; +} + +int valid_handler(struct nl_msg *msg, void *arg) +{ + if (registered_handler) + return registered_handler(msg, registered_handler_data); + + return NL_OK; +} + static int __handle_cmd(struct nl80211_state *state, enum id_input idby, - int argc, char **argv, struct cmd **cmdout) + int argc, char **argv, const struct cmd **cmdout) { - struct cmd *cmd, *match = NULL; + const struct cmd *cmd, *match = NULL, *sectcmd; struct nl_cb *cb; + struct nl_cb *s_cb; struct nl_msg *msg; - int devidx = 0; - int err, o_argc; + signed long long devidx = 0; + int err, o_argc, i; const char *command, *section; char *tmp, **o_argv; enum command_identify_by command_idby = CIB_NONE; @@ -276,6 +385,13 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, argc--; argv++; break; + case II_WDEV: + command_idby = CIB_WDEV; + devidx = strtoll(*argv, &tmp, 0); + if (*tmp != '\0') + return 1; + argc--; + argv++; default: break; } @@ -283,44 +399,74 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, if (devidx < 0) return -errno; - section = command = *argv; + section = *argv; argc--; argv++; - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler) + for_each_cmd(sectcmd, i) { + if (sectcmd->parent) continue; - if (cmd->idby != command_idby) + /* ok ... bit of a hack for the dupe 'info' section */ + if (match && sectcmd->idby != command_idby) continue; - if (cmd->section) { - if (strcmp(cmd->section, section)) + if (strcmp(sectcmd->name, section) == 0) + match = sectcmd; + } + + sectcmd = match; + match = NULL; + if (!sectcmd) + return 1; + + if (argc > 0) { + command = *argv; + + for_each_cmd(cmd, i) { + if (!cmd->handler) continue; - /* this is a bit icky ... */ - if (command == section) { - if (argc <= 0) { - if (match) - break; - return 1; - } - command = *argv; - argc--; - argv++; - } - } else if (section != command) - continue; - if (strcmp(cmd->name, command)) - continue; - if (argc && !cmd->args) - continue; + if (cmd->parent != sectcmd) + continue; + /* + * ignore mismatch id by, but allow WDEV + * in place of NETDEV + */ + if (cmd->idby != command_idby && + !(cmd->idby == CIB_NETDEV && + command_idby == CIB_WDEV)) + continue; + if (strcmp(cmd->name, command)) + continue; + if (argc > 1 && !cmd->args) + continue; + match = cmd; + break; + } - match = cmd; + if (match) { + argc--; + argv++; + } } - cmd = match; + if (match) + cmd = match; + else { + /* Use the section itself, if possible. */ + cmd = sectcmd; + if (argc && !cmd->args) + return 1; + if (cmd->idby != command_idby && + !(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV)) + return 1; + if (!cmd->handler) + return 1; + } - if (!cmd) - return 1; + if (cmd->selector) { + cmd = cmd->selector(argc, argv); + if (!cmd) + return 1; + } if (cmdout) *cmdout = cmd; @@ -328,7 +474,7 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, if (!cmd->cmd) { argc = o_argc; argv = o_argv; - return cmd->handler(state, NULL, NULL, argc, argv); + return cmd->handler(state, NULL, argc, argv, idby); } msg = nlmsg_alloc(); @@ -338,13 +484,14 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, } cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - if (!cb) { + s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + if (!cb || !s_cb) { fprintf(stderr, "failed to allocate netlink callbacks\n"); err = 2; - goto out_free_msg; + goto out; } - genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + genlmsg_put(msg, 0, 0, state->nl80211_id, 0, cmd->nl_msg_flags, cmd->cmd, 0); switch (command_idby) { @@ -354,14 +501,19 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, case CIB_NETDEV: NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); break; + case CIB_WDEV: + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx); + break; default: break; } - err = cmd->handler(state, cb, msg, argc, argv); + err = cmd->handler(state, msg, argc, argv, idby); if (err) goto out; + nl_socket_set_cb(state->nl_sock, s_cb); + err = nl_send_auto_complete(state->nl_sock, msg); if (err < 0) goto out; @@ -371,12 +523,13 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, NULL); while (err > 0) nl_recvmsgs(state->nl_sock, cb); out: nl_cb_put(cb); - out_free_msg: + nl_cb_put(s_cb); nlmsg_free(msg); return err; nla_put_failure: @@ -390,15 +543,33 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, return __handle_cmd(state, idby, argc, argv, NULL); } +/* + * Unfortunately, I don't know how densely the linker packs the struct cmd. + * For example, if you have a 72-byte struct cmd, the linker will pad each + * out to 96 bytes before putting them together in the section. There must + * be some algorithm, but I haven't found it yet. + * + * We used to calculate this by taking the (abs value of) the difference + * between __section_get and __section_set, but if LTO is enabled then this + * stops working because the entries of the "__cmd" section get rearranged + * freely by the compiler/linker. + * + * Fix this by using yet another "__sizer" section that only contains these + * two entries - then the (abs value of) the difference between them will + * be how they get packed and that can be used to iterate the __cmd section + * as well. + */ +static struct cmd sizer1 __attribute__((section("__sizer"))) = {}; +static struct cmd sizer2 __attribute__((section("__sizer"))) = {}; + int main(int argc, char **argv) { struct nl80211_state nlstate; int err; - struct cmd *cmd = NULL; + const struct cmd *cmd = NULL; /* calculate command size including padding */ - cmd_size = abs((long)&__cmd_NULL_NULL_1_CIB_NONE_0 - - (long)&__cmd_NULL_NULL_0_CIB_NONE_0); + cmd_size = labs((long)&sizer2 - (long)&sizer1); /* strip off self */ argc--; argv0 = *argv++; @@ -416,7 +587,7 @@ int main(int argc, char **argv) /* need to treat "help" command specially so it works w/o nl80211 */ if (argc == 0 || strcmp(*argv, "help") == 0) { - usage(argc != 0); + usage(argc - 1, argv + 1); return 0; } @@ -437,10 +608,15 @@ int main(int argc, char **argv) err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); else goto detect; + } else if (strcmp(*argv, "wdev") == 0 && argc > 1) { + argc--; + argv++; + err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd); } else { int idx; - enum id_input idby = II_NONE; + enum id_input idby; detect: + idby = II_NONE; if ((idx = if_nametoindex(argv[0])) != 0) idby = II_NETDEV; else if ((idx = phy_lookup(argv[0])) >= 0) @@ -448,11 +624,13 @@ int main(int argc, char **argv) err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); } - if (err == 1) { + if (err == HANDLER_RET_USAGE) { if (cmd) usage_cmd(cmd); else - usage(false); + usage(0, NULL); + } else if (err == HANDLER_RET_DONE) { + err = 0; } else if (err < 0) fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err);