X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fiw.git;a=blobdiff_plain;f=iw.c;h=af55a9bfae493bfd90161798eeca22a53694d771;hp=affa0d49e9789dd1a020944fe7021791b149fc56;hb=8d5d7ba796bf89c1efd3be3fbed5d1c7bd40dec7;hpb=3bb116da8dfaee8d5265d98467112be858401864 diff --git a/iw.c b/iw.c index affa0d4..af55a9b 100644 --- a/iw.c +++ b/iw.c @@ -13,19 +13,18 @@ #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(); @@ -35,19 +34,9 @@ static inline void nl_socket_free(struct nl_sock *h) { nl_handle_destroy(h); } +#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */ -static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache) -{ - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; -} -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache -#endif /* CONFIG_LIBNL20 */ - -static int debug = 0; +int iw_debug = 0; static int nl80211_init(struct nl80211_state *state) { @@ -65,23 +54,15 @@ 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; - } - - state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); - if (!state->nl80211) { + 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,54 +70,141 @@ 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); -__COMMAND(NULL, NULL, "", NULL, 1, 0, 0, CIB_NONE, NULL); - static int cmd_size; -static void usage_cmd(struct cmd *cmd) +extern struct cmd __start___cmd; +extern struct cmd __stop___cmd; + +#define for_each_cmd(_cmd) \ + for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \ + _cmd = (const struct cmd *)((char *)_cmd + cmd_size)) + + +static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) { + const char *start, *lend, *end; + + printf("%s", indent); + switch (cmd->idby) { case CIB_NONE: - fprintf(stderr, "\t"); break; case CIB_PHY: - fprintf(stderr, "\tphy "); + printf("phy "); break; case CIB_NETDEV: - fprintf(stderr, "\tdev "); + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); break; } - if (cmd->section) - fprintf(stderr, "%s ", cmd->section); - fprintf(stderr, "%s", cmd->name); + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s", cmd->name); if (cmd->args) - fprintf(stderr, " %s", cmd->args); - fprintf(stderr, "\n"); + printf(" %s", cmd->args); + printf("\n"); + + if (!full || !cmd->help) + return; + + /* hack */ + if (strlen(indent)) + indent = "\t\t"; + else + printf("\n"); + + /* print line by line */ + start = cmd->help; + end = strchr(start, '\0'); + do { + lend = strchr(start, '\n'); + if (!lend) + lend = end; + printf("%s", indent); + printf("%.*s\n", (int)(lend - start), start); + start = lend + 1; + } while (end != lend); + + printf("\n"); +} + +static void usage_options(void) +{ + printf("Options:\n"); + printf("\t--debug\t\tenable netlink debugging\n"); } -static void usage(const char *argv0) +static const char *argv0; + +static void usage(int argc, char **argv) { - struct cmd *cmd; - - fprintf(stderr, "Usage:\t%s [options] command\n", argv0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\t--debug\t\tenable netlink debugging\n"); - fprintf(stderr, "\t--version\tshow version\n"); - fprintf(stderr, "Commands:\n"); - fprintf(stderr, "\thelp\n"); - fprintf(stderr, "\tevent [-t] [-f]\n"); - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler || cmd->hidden) + const struct cmd *section, *cmd; + bool full = argc >= 0; + const char *sect_filt = NULL; + const char *cmd_filt = NULL; + + if (argc > 0) + sect_filt = argv[0]; + + if (argc > 1) + cmd_filt = argv[1]; + + printf("Usage:\t%s [options] command\n", argv0); + usage_options(); + printf("\t--version\tshow version (%s)\n", iw_version); + printf("Commands:\n"); + for_each_cmd(section) { + if (section->parent) continue; - usage_cmd(cmd); + + if (sect_filt && strcmp(section->name, sect_filt)) + continue; + + if (section->handler && !section->hidden) + __usage_cmd(section, "\t", full); + + for_each_cmd(cmd) { + 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); + } } + 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" + "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, + enum id_input id) +{ + exit(3); +} +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(const struct cmd *cmd) +{ + printf("Usage:\t%s [options] ", argv0); + __usage_cmd(cmd, "", true); + usage_options(); } static void version(void) @@ -155,9 +223,12 @@ 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); } @@ -183,13 +254,14 @@ static int ack_handler(struct nl_msg *msg, void *arg) return NL_STOP; } -int handle_cmd(struct nl80211_state *state, enum id_input idby, - int argc, char **argv) +static int __handle_cmd(struct nl80211_state *state, enum id_input idby, + 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; + signed long long devidx = 0; int err, o_argc; const char *command, *section; char *tmp, **o_argv; @@ -224,6 +296,13 @@ 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; } @@ -231,49 +310,82 @@ 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) { + 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) { + 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; if (!cmd->cmd) { argc = o_argc; argv = o_argv; - return cmd->handler(state, NULL, NULL, argc, argv); + return cmd->handler(state, NULL, NULL, argc, argv, idby); } msg = nlmsg_alloc(); @@ -282,14 +394,15 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, return 2; } - cb = nl_cb_alloc(debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - if (!cb) { + cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + 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; } - 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) { @@ -299,14 +412,19 @@ 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, cb, 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; @@ -329,311 +447,26 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, return 2; } -static int no_seq_check(struct nl_msg *msg, void *arg) -{ - return NL_OK; -} - -struct print_event_args { - bool frame, time; -}; - -static void print_frame(struct print_event_args *args, struct nlattr *attr) -{ - uint8_t *frame; - size_t len; - int i; - char macbuf[6*3]; - uint16_t tmp; - - if (!attr) - printf(" [no frame]"); - - frame = nla_data(attr); - len = nla_len(attr); - - if (len < 26) { - printf(" [invalid frame: "); - goto print_frame; - } - - mac_addr_n2a(macbuf, frame + 10); - printf(" %s -> ", macbuf); - mac_addr_n2a(macbuf, frame + 4); - printf("%s", macbuf); - - switch (frame[0] & 0xfc) { - case 0x10: /* assoc resp */ - case 0x30: /* reassoc resp */ - /* status */ - tmp = (frame[27] << 8) + frame[26]; - printf(" status: %d: %s", tmp, get_status_str(tmp)); - break; - case 0x00: /* assoc req */ - case 0x20: /* reassoc req */ - break; - case 0xb0: /* auth */ - /* status */ - tmp = (frame[29] << 8) + frame[28]; - printf(" status: %d: %s", tmp, get_status_str(tmp)); - break; - break; - case 0xa0: /* disassoc */ - case 0xc0: /* deauth */ - /* reason */ - tmp = (frame[25] << 8) + frame[24]; - printf(" reason %d: %s", tmp, get_reason_str(tmp)); - break; - } - - if (!args->frame) - return; - - printf(" [frame:"); - - print_frame: - for (i = 0; i < len; i++) - printf(" %.02x", frame[i]); - printf("]"); -} - -static int print_event(struct nl_msg *msg, void *arg) -{ - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct print_event_args *args = arg; - char ifname[100]; - char macbuf[6*3]; - __u8 reg_type; - - if (args->time) { - struct timeval tv; - gettimeofday(&tv, NULL); - printf("%ld.%06u: ", (long) tv.tv_sec, (unsigned int) tv.tv_usec); - } - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) { - if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); - printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY])); - } else if (tb[NL80211_ATTR_IFINDEX]) { - if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); - printf("%s: ", ifname); - } else if (tb[NL80211_ATTR_WIPHY]) { - printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - } - - switch (gnlh->cmd) { - case NL80211_CMD_NEW_WIPHY: - printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME])); - break; - case NL80211_CMD_NEW_SCAN_RESULTS: - printf("scan finished\n"); - break; - case NL80211_CMD_SCAN_ABORTED: - printf("scan aborted\n"); - break; - case NL80211_CMD_REG_CHANGE: - printf("regulatory domain change: "); - - reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); - - switch (reg_type) { - case NL80211_REGDOM_TYPE_COUNTRY: - printf("set to %s by %s request", - nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), - reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]))); - if (tb[NL80211_ATTR_WIPHY]) - printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - break; - case NL80211_REGDOM_TYPE_WORLD: - printf("set to world roaming by %s request", - reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]))); - break; - case NL80211_REGDOM_TYPE_CUSTOM_WORLD: - printf("custom world roaming rules in place on phy%d by %s request", - nla_get_u32(tb[NL80211_ATTR_WIPHY]), - reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR]))); - break; - case NL80211_REGDOM_TYPE_INTERSECTION: - printf("intersection used due to a request made by %s", - reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR]))); - if (tb[NL80211_ATTR_WIPHY]) - printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - break; - default: - printf("unknown source (upgrade this utility)"); - break; - } - - printf("\n"); - break; - case NL80211_CMD_JOIN_IBSS: - mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); - printf("IBSS %s joined\n", macbuf); - break; - case NL80211_CMD_AUTHENTICATE: - printf("auth"); - print_frame(args, tb[NL80211_ATTR_FRAME]); - printf("\n"); - break; - case NL80211_CMD_ASSOCIATE: - printf("assoc"); - print_frame(args, tb[NL80211_ATTR_FRAME]); - printf("\n"); - break; - case NL80211_CMD_DEAUTHENTICATE: - printf("deauth"); - print_frame(args, tb[NL80211_ATTR_FRAME]); - printf("\n"); - break; - case NL80211_CMD_DISASSOCIATE: - printf("disassoc"); - print_frame(args, tb[NL80211_ATTR_FRAME]); - printf("\n"); - break; - default: - printf("unknown event %d\n", gnlh->cmd); - break; - } - - return NL_SKIP; -} - -struct wait_event { - int n_cmds; - const __u32 *cmds; - __u32 cmd; -}; - -static int wait_event(struct nl_msg *msg, void *arg) -{ - struct wait_event *wait = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - int i; - - for (i = 0; i < wait->n_cmds; i++) { - if (gnlh->cmd == wait->cmds[i]) { - wait->cmd = gnlh->cmd; - } - } - - return NL_SKIP; -} - -static __u32 __listen_events(struct nl80211_state *state, - const int n_waits, const __u32 *waits, - struct print_event_args *args) -{ - int mcid, ret; - struct nl_cb *cb = nl_cb_alloc(debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - struct wait_event wait_ev; - - if (!cb) { - fprintf(stderr, "failed to allocate netlink callbacks\n"); - return -ENOMEM; - } - - /* Configuration multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config"); - if (mcid < 0) - return mcid; - - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - - /* Scan multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* Regulatory multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* MLME multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* no sequence checking for multicast messages */ - nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - - if (n_waits && waits) { - wait_ev.cmds = waits; - wait_ev.n_cmds = n_waits; - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev); - } else { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args); - } - - wait_ev.cmd = 0; - - while (!wait_ev.cmd) - nl_recvmsgs(state->nl_sock, cb); - - nl_cb_put(cb); - - return wait_ev.cmd; -} - -__u32 listen_events(struct nl80211_state *state, - const int n_waits, const __u32 *waits) +int handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv) { - return __listen_events(state, n_waits, waits, NULL); -} - -static int print_events(struct nl80211_state *state, int argc, char **argv) -{ - struct print_event_args args; - - memset(&args, 0, sizeof(args)); - - while (argc > 0) { - if (strcmp(argv[0], "-f") == 0) - args.frame = true; - else if (strcmp(argv[0], "-t") == 0) - args.time = true; - else - return 1; - argc--; - argv++; - } - - if (argc) - return 1; - - return __listen_events(state, 0, NULL, &args); + return __handle_cmd(state, idby, argc, argv, NULL); } int main(int argc, char **argv) { struct nl80211_state nlstate; int err; - const char *argv0; + 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 = abs((long)&__section_set - (long)&__section_get); /* strip off self */ argc--; argv0 = *argv++; if (argc > 0 && strcmp(*argv, "--debug") == 0) { - debug = 1; + iw_debug = 1; argc--; argv++; } @@ -643,8 +476,9 @@ int main(int argc, char **argv) return 0; } + /* need to treat "help" command specially so it works w/o nl80211 */ if (argc == 0 || strcmp(*argv, "help") == 0) { - usage(argv0); + usage(argc - 1, argv + 1); return 0; } @@ -652,29 +486,40 @@ int main(int argc, char **argv) if (err) return 1; - if (strcmp(*argv, "event") == 0) { + if (strcmp(*argv, "dev") == 0 && argc > 1) { argc--; argv++; - err = print_events(&nlstate, argc, argv); - } else if (strcmp(*argv, "dev") == 0 && argc > 1) { - argc--; - argv++; - err = handle_cmd(&nlstate, II_NETDEV, argc, argv); + err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd); } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) { if (strlen(*argv) == 3) { argc--; argv++; - err = handle_cmd(&nlstate, II_PHY_NAME, argc, argv); + err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd); } else if (*(*argv + 3) == '#') - err = handle_cmd(&nlstate, II_PHY_IDX, argc, argv); + err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); else - err = 1; - } else - err = handle_cmd(&nlstate, II_NONE, argc, argv); + 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; + detect: + if ((idx = if_nametoindex(argv[0])) != 0) + idby = II_NETDEV; + else if ((idx = phy_lookup(argv[0])) >= 0) + idby = II_PHY_NAME; + err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); + } - if (err == 1) - usage(argv0); - if (err < 0) + if (err == 1) { + if (cmd) + usage_cmd(cmd); + else + usage(0, NULL); + } else if (err < 0) fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err); nl80211_cleanup(&nlstate);