| #include <stdbool.h> |
| #include <errno.h> |
| #include <strings.h> |
| #include <unistd.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include <netlink/genl/genl.h> |
| #include <netlink/genl/family.h> |
| #include <netlink/genl/ctrl.h> |
| #include <netlink/msg.h> |
| #include <netlink/attr.h> |
| |
| #include "nl80211.h" |
| #include "iw.h" |
| |
| struct channels_ctx { |
| int last_band; |
| bool width_40; |
| bool width_80; |
| bool width_160; |
| }; |
| |
| static char *dfs_state_name(enum nl80211_dfs_state state) |
| { |
| switch (state) { |
| case NL80211_DFS_USABLE: |
| return "usable"; |
| case NL80211_DFS_AVAILABLE: |
| return "available"; |
| case NL80211_DFS_UNAVAILABLE: |
| return "unavailable"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| static int print_channels_handler(struct nl_msg *msg, void *arg) |
| { |
| struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| struct channels_ctx *ctx = arg; |
| struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; |
| struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; |
| struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; |
| struct nlattr *nl_band; |
| struct nlattr *nl_freq; |
| int rem_band, rem_freq; |
| |
| nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); |
| |
| if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) { |
| nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { |
| if (ctx->last_band != nl_band->nla_type) { |
| printf("Band %d:\n", nl_band->nla_type + 1); |
| ctx->width_40 = false; |
| ctx->width_80 = false; |
| ctx->width_160 = false; |
| ctx->last_band = nl_band->nla_type; |
| } |
| |
| nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL); |
| |
| if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { |
| __u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]); |
| |
| if (cap & BIT(1)) |
| ctx->width_40 = true; |
| } |
| |
| if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) { |
| __u32 capa; |
| |
| ctx->width_80 = true; |
| |
| capa = nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]); |
| switch ((capa >> 2) & 3) { |
| case 2: |
| /* width_80p80 = true; */ |
| /* fall through */ |
| case 1: |
| ctx->width_160 = true; |
| break; |
| } |
| } |
| |
| if (tb_band[NL80211_BAND_ATTR_FREQS]) { |
| nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { |
| uint32_t freq; |
| |
| nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), NULL); |
| |
| if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) |
| continue; |
| freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); |
| printf("\t* %d MHz [%d] ", freq, ieee80211_frequency_to_channel(freq)); |
| |
| if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) { |
| printf("(disabled)\n"); |
| continue; |
| } |
| printf("\n"); |
| |
| if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) |
| printf("\t Maximum TX power: %.1f dBm\n", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])); |
| |
| /* If both flags are set assume an new kernel */ |
| if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) { |
| printf("\t No IR\n"); |
| } else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) { |
| printf("\t Passive scan\n"); |
| } else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){ |
| printf("\t No IBSS\n"); |
| } |
| |
| if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) |
| printf("\t Radar detection\n"); |
| |
| printf("\t Channel widths:"); |
| if (!tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ]) |
| printf(" 20MHz"); |
| if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS]) |
| printf(" HT40-"); |
| if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS]) |
| printf(" HT40+"); |
| if (ctx->width_80 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ]) |
| printf(" VHT80"); |
| if (ctx->width_160 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ]) |
| printf(" VHT160"); |
| printf("\n"); |
| |
| if (!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED] && tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { |
| enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); |
| unsigned long time; |
| |
| printf("\t DFS state: %s", dfs_state_name(state)); |
| if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) { |
| time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]); |
| printf(" (for %lu sec)", time / 1000); |
| } |
| printf("\n"); |
| if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) |
| printf("\t DFS CAC time: %u ms\n", |
| nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME])); |
| } |
| } |
| } |
| } |
| } |
| |
| return NL_SKIP; |
| } |
| |
| static int handle_channels(struct nl80211_state *state, struct nl_msg *msg, |
| int argc, char **argv, enum id_input id) |
| { |
| static struct channels_ctx ctx = { |
| .last_band = -1, |
| }; |
| |
| nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); |
| nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP; |
| |
| register_handler(print_channels_handler, &ctx); |
| |
| return 0; |
| } |
| TOPLEVEL(channels, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_channels, "Show available channels."); |
| |
| static int handle_name(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| if (argc != 1) |
| return 1; |
| |
| NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv); |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name, |
| "Rename this wireless device."); |
| |
| static int handle_freq(struct nl80211_state *state, struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| struct chandef chandef; |
| int res; |
| |
| res = parse_freqchan(&chandef, false, argc, argv, NULL); |
| if (res) |
| return res; |
| |
| return put_chandef(msg, &chandef); |
| } |
| |
| COMMAND(set, freq, |
| "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]\n" |
| "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq, |
| "Set frequency/channel the hardware is using, including HT\n" |
| "configuration."); |
| COMMAND(set, freq, |
| "<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]\n" |
| "<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL); |
| |
| static int handle_chan(struct nl80211_state *state, struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| struct chandef chandef; |
| int res; |
| |
| res = parse_freqchan(&chandef, true, argc, argv, NULL); |
| if (res) |
| return res; |
| |
| return put_chandef(msg, &chandef); |
| } |
| COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL); |
| COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz|160MHz]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL); |
| |
| |
| struct cac_event { |
| int ret; |
| uint32_t freq; |
| }; |
| |
| static int print_cac_event(struct nl_msg *msg, void *arg) |
| { |
| struct nlattr *tb[NL80211_ATTR_MAX + 1]; |
| struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| enum nl80211_radar_event event_type; |
| struct cac_event *cac_event = arg; |
| uint32_t freq; |
| |
| if (gnlh->cmd != NL80211_CMD_RADAR_DETECT) |
| return NL_SKIP; |
| |
| nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| genlmsg_attrlen(gnlh, 0), NULL); |
| |
| if (!tb[NL80211_ATTR_RADAR_EVENT] || !tb[NL80211_ATTR_WIPHY_FREQ]) |
| return NL_SKIP; |
| |
| freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); |
| event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); |
| if (freq != cac_event->freq) |
| return NL_SKIP; |
| |
| switch (event_type) { |
| case NL80211_RADAR_DETECTED: |
| printf("%d MHz: radar detected\n", freq); |
| break; |
| case NL80211_RADAR_CAC_FINISHED: |
| printf("%d MHz: CAC finished\n", freq); |
| break; |
| case NL80211_RADAR_CAC_ABORTED: |
| printf("%d MHz: CAC was aborted\n", freq); |
| break; |
| case NL80211_RADAR_NOP_FINISHED: |
| printf("%d MHz: NOP finished\n", freq); |
| break; |
| default: |
| printf("%d MHz: unknown radar event\n", freq); |
| } |
| cac_event->ret = 0; |
| |
| return NL_SKIP; |
| } |
| |
| static int handle_cac_trigger(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| struct chandef chandef; |
| int res; |
| |
| if (argc < 2) |
| return 1; |
| |
| if (strcmp(argv[0], "channel") == 0) { |
| res = parse_freqchan(&chandef, true, argc - 1, argv + 1, NULL); |
| } else if (strcmp(argv[0], "freq") == 0) { |
| res = parse_freqchan(&chandef, false, argc - 1, argv + 1, NULL); |
| } else { |
| return 1; |
| } |
| |
| if (res) |
| return res; |
| |
| return put_chandef(msg, &chandef); |
| } |
| |
| static int no_seq_check(struct nl_msg *msg, void *arg) |
| { |
| return NL_OK; |
| } |
| |
| static int handle_cac(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| int err; |
| struct nl_cb *radar_cb; |
| struct chandef chandef; |
| struct cac_event cac_event; |
| char **cac_trigger_argv = NULL; |
| |
| radar_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); |
| if (!radar_cb) |
| return 1; |
| |
| if (argc < 3) |
| return 1; |
| |
| if (strcmp(argv[2], "channel") == 0) { |
| err = parse_freqchan(&chandef, true, argc - 3, argv + 3, NULL); |
| } else if (strcmp(argv[2], "freq") == 0) { |
| err = parse_freqchan(&chandef, false, argc - 3, argv + 3, NULL); |
| } else { |
| err = 1; |
| } |
| if (err) |
| goto err_out; |
| |
| cac_trigger_argv = calloc(argc + 1, sizeof(char*)); |
| if (!cac_trigger_argv) { |
| err = -ENOMEM; |
| goto err_out; |
| } |
| |
| cac_trigger_argv[0] = argv[0]; |
| cac_trigger_argv[1] = "cac"; |
| cac_trigger_argv[2] = "trigger"; |
| memcpy(&cac_trigger_argv[3], &argv[2], (argc - 2) * sizeof(char*)); |
| |
| err = handle_cmd(state, id, argc + 1, cac_trigger_argv); |
| if (err) |
| goto err_out; |
| |
| cac_event.ret = 1; |
| cac_event.freq = chandef.control_freq; |
| |
| __prepare_listen_events(state); |
| nl_socket_set_cb(state->nl_sock, radar_cb); |
| |
| /* need to turn off sequence number checking */ |
| nl_cb_set(radar_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); |
| nl_cb_set(radar_cb, NL_CB_VALID, NL_CB_CUSTOM, print_cac_event, &cac_event); |
| while (cac_event.ret > 0) |
| nl_recvmsgs(state->nl_sock, radar_cb); |
| |
| err = 0; |
| err_out: |
| if (radar_cb) |
| nl_cb_put(radar_cb); |
| if (cac_trigger_argv) |
| free(cac_trigger_argv); |
| return err; |
| } |
| TOPLEVEL(cac, "channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n" |
| "freq <freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n" |
| "freq <control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]", |
| 0, 0, CIB_NETDEV, handle_cac, NULL); |
| COMMAND(cac, trigger, |
| "channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n" |
| "freq <frequency> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n" |
| "freq <frequency> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]", |
| NL80211_CMD_RADAR_DETECT, 0, CIB_NETDEV, handle_cac_trigger, |
| "Start or trigger a channel availability check (CAC) looking to look for\n" |
| "radars on the given channel."); |
| |
| static int handle_fragmentation(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| unsigned int frag; |
| |
| if (argc != 1) |
| return 1; |
| |
| if (strcmp("off", argv[0]) == 0) |
| frag = -1; |
| else { |
| char *end; |
| |
| if (!*argv[0]) |
| return 1; |
| frag = strtoul(argv[0], &end, 10); |
| if (*end != '\0') |
| return 1; |
| } |
| |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag); |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, frag, "<fragmentation threshold|off>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation, |
| "Set fragmentation threshold."); |
| |
| static int handle_rts(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| unsigned int rts; |
| |
| if (argc != 1) |
| return 1; |
| |
| if (strcmp("off", argv[0]) == 0) |
| rts = -1; |
| else { |
| char *end; |
| |
| if (!*argv[0]) |
| return 1; |
| rts = strtoul(argv[0], &end, 10); |
| if (*end != '\0') |
| return 1; |
| } |
| |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts); |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, rts, "<rts threshold|off>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts, |
| "Set rts threshold."); |
| |
| static int handle_retry(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, enum id_input id) |
| { |
| unsigned int retry_short = 0, retry_long = 0; |
| bool have_retry_s = false, have_retry_l = false; |
| int i; |
| enum { |
| S_NONE, |
| S_SHORT, |
| S_LONG, |
| } parser_state = S_NONE; |
| |
| if (!argc || (argc != 2 && argc != 4)) |
| return 1; |
| |
| for (i = 0; i < argc; i++) { |
| char *end; |
| unsigned int tmpul; |
| |
| if (strcmp(argv[i], "short") == 0) { |
| if (have_retry_s) |
| return 1; |
| parser_state = S_SHORT; |
| have_retry_s = true; |
| } else if (strcmp(argv[i], "long") == 0) { |
| if (have_retry_l) |
| return 1; |
| parser_state = S_LONG; |
| have_retry_l = true; |
| } else { |
| tmpul = strtoul(argv[i], &end, 10); |
| if (*end != '\0') |
| return 1; |
| if (!tmpul || tmpul > 255) |
| return -EINVAL; |
| switch (parser_state) { |
| case S_SHORT: |
| retry_short = tmpul; |
| break; |
| case S_LONG: |
| retry_long = tmpul; |
| break; |
| default: |
| return 1; |
| } |
| } |
| } |
| |
| if (!have_retry_s && !have_retry_l) |
| return 1; |
| if (have_retry_s) |
| NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short); |
| if (have_retry_l) |
| NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long); |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, retry, "[short <limit>] [long <limit>]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry, |
| "Set retry limit."); |
| |
| #ifndef NETNS_RUN_DIR |
| #define NETNS_RUN_DIR "/var/run/netns" |
| #endif |
| static int netns_get_fd(const char *name) |
| { |
| char pathbuf[MAXPATHLEN]; |
| const char *path, *ptr; |
| |
| path = name; |
| ptr = strchr(name, '/'); |
| if (!ptr) { |
| snprintf(pathbuf, sizeof(pathbuf), "%s/%s", |
| NETNS_RUN_DIR, name ); |
| path = pathbuf; |
| } |
| return open(path, O_RDONLY); |
| } |
| |
| static int handle_netns(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| char *end; |
| int fd = -1; |
| |
| if (argc < 1 || !*argv[0]) |
| return 1; |
| |
| if (argc == 1) { |
| NLA_PUT_U32(msg, NL80211_ATTR_PID, |
| strtoul(argv[0], &end, 10)); |
| if (*end != '\0') { |
| printf("Invalid parameter: pid(%s)\n", argv[0]); |
| return 1; |
| } |
| return 0; |
| } |
| |
| if (argc != 2 || strcmp(argv[0], "name")) |
| return 1; |
| |
| if ((fd = netns_get_fd(argv[1])) >= 0) { |
| NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd); |
| return 0; |
| } else { |
| printf("Invalid parameter: nsname(%s)\n", argv[0]); |
| } |
| |
| return 1; |
| |
| nla_put_failure: |
| if (fd >= 0) |
| close(fd); |
| return -ENOBUFS; |
| } |
| COMMAND(set, netns, "{ <pid> | name <nsname> }", |
| NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns, |
| "Put this wireless device into a different network namespace:\n" |
| " <pid> - change network namespace by process id\n" |
| " <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n" |
| " or by absolute path (man ip-netns)\n"); |
| |
| static int handle_coverage(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| char *end; |
| unsigned int coverage; |
| |
| if (argc != 1) |
| return 1; |
| |
| if (!*argv[0]) |
| return 1; |
| coverage = strtoul(argv[0], &end, 10); |
| if (coverage > 255) |
| return 1; |
| |
| if (*end) |
| return 1; |
| |
| NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage); |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, coverage, "<coverage class>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage, |
| "Set coverage class (1 for every 3 usec of air propagation time).\n" |
| "Valid values: 0 - 255."); |
| |
| static int handle_distance(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| if (argc != 1) |
| return 1; |
| |
| if (!*argv[0]) |
| return 1; |
| |
| if (strcmp("auto", argv[0]) == 0) { |
| NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK); |
| } else { |
| char *end; |
| unsigned int distance, coverage; |
| |
| distance = strtoul(argv[0], &end, 10); |
| |
| if (*end) |
| return 1; |
| |
| /* |
| * Divide double the distance by the speed of light |
| * in m/usec (300) to get round-trip time in microseconds |
| * and then divide the result by three to get coverage class |
| * as specified in IEEE 802.11-2007 table 7-27. |
| * Values are rounded upwards. |
| */ |
| coverage = (distance + 449) / 450; |
| if (coverage > 255) |
| return 1; |
| |
| NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage); |
| } |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, distance, "<auto|distance>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance, |
| "Enable ACK timeout estimation algorithm (dynack) or set appropriate\n" |
| "coverage class for given link distance in meters.\n" |
| "To disable dynack set valid value for coverage class.\n" |
| "Valid values: 0 - 114750"); |
| |
| static int handle_txpower(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| enum nl80211_tx_power_setting type; |
| int mbm; |
| |
| /* get the required args */ |
| if (argc != 1 && argc != 2) |
| return 1; |
| |
| if (!strcmp(argv[0], "auto")) |
| type = NL80211_TX_POWER_AUTOMATIC; |
| else if (!strcmp(argv[0], "fixed")) |
| type = NL80211_TX_POWER_FIXED; |
| else if (!strcmp(argv[0], "limit")) |
| type = NL80211_TX_POWER_LIMITED; |
| else { |
| printf("Invalid parameter: %s\n", argv[0]); |
| return 2; |
| } |
| |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type); |
| |
| if (type != NL80211_TX_POWER_AUTOMATIC) { |
| char *endptr; |
| if (argc != 2) { |
| printf("Missing TX power level argument.\n"); |
| return 2; |
| } |
| |
| mbm = strtol(argv[1], &endptr, 10); |
| if (*endptr) |
| return 2; |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm); |
| } else if (argc != 1) |
| return 1; |
| |
| return 0; |
| |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower, |
| "Specify transmit power level and setting type."); |
| COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]", |
| NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower, |
| "Specify transmit power level and setting type."); |
| |
| static int handle_antenna(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| char *end; |
| uint32_t tx_ant = 0, rx_ant = 0; |
| |
| if (argc == 1 && strcmp(argv[0], "all") == 0) { |
| tx_ant = 0xffffffff; |
| rx_ant = 0xffffffff; |
| } else if (argc == 1) { |
| tx_ant = rx_ant = strtoul(argv[0], &end, 0); |
| if (*end) |
| return 1; |
| } |
| else if (argc == 2) { |
| tx_ant = strtoul(argv[0], &end, 0); |
| if (*end) |
| return 1; |
| rx_ant = strtoul(argv[1], &end, 0); |
| if (*end) |
| return 1; |
| } else |
| return 1; |
| |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); |
| NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); |
| |
| return 0; |
| |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna, |
| "Set a bitmap of allowed antennas to use for TX and RX.\n" |
| "The driver may reject antenna configurations it cannot support."); |
| |
| static int handle_set_txq(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| unsigned int argval; |
| char *end; |
| |
| if (argc != 2) |
| return 1; |
| |
| if (!*argv[0] || !*argv[1]) |
| return 1; |
| |
| argval = strtoul(argv[1], &end, 10); |
| |
| if (*end) |
| return 1; |
| |
| if (!argval) |
| return 1; |
| |
| if (strcmp("limit", argv[0]) == 0) |
| NLA_PUT_U32(msg, NL80211_ATTR_TXQ_LIMIT, argval); |
| else if (strcmp("memory_limit", argv[0]) == 0) |
| NLA_PUT_U32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT, argval); |
| else if (strcmp("quantum", argv[0]) == 0) |
| NLA_PUT_U32(msg, NL80211_ATTR_TXQ_QUANTUM, argval); |
| else |
| return -1; |
| |
| return 0; |
| nla_put_failure: |
| return -ENOBUFS; |
| } |
| COMMAND(set, txq, "limit <packets> | memory_limit <bytes> | quantum <bytes>", |
| NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_set_txq, |
| "Set TXQ parameters. The limit and memory_limit are global queue limits\n" |
| "for the whole phy. The quantum is the DRR scheduler quantum setting.\n" |
| "Valid values: 1 - 2**32"); |
| |
| static int print_txq_handler(struct nl_msg *msg, void *arg) |
| { |
| struct nlattr *attrs[NL80211_ATTR_MAX + 1]; |
| struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| struct nlattr *txqstats_info[NL80211_TXQ_STATS_MAX + 1], *txqinfo; |
| static struct nla_policy txqstats_policy[NL80211_TXQ_STATS_MAX + 1] = { |
| [NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 }, |
| [NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 }, |
| [NL80211_TXQ_STATS_OVERLIMIT] = { .type = NLA_U32 }, |
| [NL80211_TXQ_STATS_OVERMEMORY] = { .type = NLA_U32 }, |
| [NL80211_TXQ_STATS_COLLISIONS] = { .type = NLA_U32 }, |
| [NL80211_TXQ_STATS_MAX_FLOWS] = { .type = NLA_U32 }, |
| }; |
| |
| nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| genlmsg_attrlen(gnlh, 0), NULL); |
| |
| |
| if (attrs[NL80211_ATTR_TXQ_LIMIT]) |
| printf("Packet limit:\t\t%u pkts\n", |
| nla_get_u32(attrs[NL80211_ATTR_TXQ_LIMIT])); |
| if (attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) |
| printf("Memory limit:\t\t%u bytes\n", |
| nla_get_u32(attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT])); |
| if (attrs[NL80211_ATTR_TXQ_QUANTUM]) |
| printf("Quantum:\t\t%u bytes\n", |
| nla_get_u32(attrs[NL80211_ATTR_TXQ_QUANTUM])); |
| |
| if (attrs[NL80211_ATTR_TXQ_STATS]) { |
| if (nla_parse_nested(txqstats_info, NL80211_TXQ_STATS_MAX, |
| attrs[NL80211_ATTR_TXQ_STATS], |
| txqstats_policy)) { |
| printf("failed to parse nested TXQ stats attributes!"); |
| return 0; |
| } |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_MAX_FLOWS]; |
| if (txqinfo) |
| printf("Number of queues:\t%u\n", nla_get_u32(txqinfo)); |
| |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_BACKLOG_PACKETS]; |
| if (txqinfo) |
| printf("Backlog:\t\t%u pkts\n", nla_get_u32(txqinfo)); |
| |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_BACKLOG_BYTES]; |
| if (txqinfo) |
| printf("Memory usage:\t\t%u bytes\n", nla_get_u32(txqinfo)); |
| |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_OVERLIMIT]; |
| if (txqinfo) |
| printf("Packet limit overflows:\t%u\n", nla_get_u32(txqinfo)); |
| |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_OVERMEMORY]; |
| if (txqinfo) |
| printf("Memory limit overflows:\t%u\n", nla_get_u32(txqinfo)); |
| txqinfo = txqstats_info[NL80211_TXQ_STATS_COLLISIONS]; |
| if (txqinfo) |
| printf("Hash collisions:\t%u\n", nla_get_u32(txqinfo)); |
| } |
| return NL_SKIP; |
| } |
| |
| static int handle_get_txq(struct nl80211_state *state, |
| struct nl_msg *msg, |
| int argc, char **argv, |
| enum id_input id) |
| { |
| nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); |
| nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP; |
| register_handler(print_txq_handler, NULL); |
| return 0; |
| } |
| COMMAND(get, txq, "", |
| NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_get_txq, |
| "Get TXQ parameters."); |