X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fiw.git;a=blobdiff_plain;f=station.c;h=f3e3da83398cab298baa156e2ebdbab032cd107f;hp=8c507999a6bd030dfa46ecd5edc858e401b14c1d;hb=feea0ff28c8ca01d832b8dd36af2d196fbfee772;hpb=f903d0356657587ed9ba4567d87a56ac1ddbc8f4 diff --git a/station.c b/station.c index 8c50799..f3e3da8 100644 --- a/station.c +++ b/station.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,8 +8,11 @@ #include #include +#include "nl80211.h" #include "iw.h" +SECTION(station); + enum plink_state { LISTEN, OPN_SNT, @@ -21,12 +23,182 @@ enum plink_state { BLOCKED }; -enum plink_actions { - PLINK_ACTION_UNDEFINED, - PLINK_ACTION_OPEN, - PLINK_ACTION_BLOCK, -}; +static void print_power_mode(struct nlattr *a) +{ + enum nl80211_mesh_power_mode pm = nla_get_u32(a); + + switch (pm) { + case NL80211_MESH_POWER_ACTIVE: + printf("ACTIVE"); + break; + case NL80211_MESH_POWER_LIGHT_SLEEP: + printf("LIGHT SLEEP"); + break; + case NL80211_MESH_POWER_DEEP_SLEEP: + printf("DEEP SLEEP"); + break; + default: + printf("UNKNOWN"); + break; + } +} + +void parse_tid_stats(struct nlattr *tid_stats_attr) +{ + struct nlattr *stats_info[NL80211_TID_STATS_MAX + 1], *tidattr, *info; + static struct nla_policy stats_policy[NL80211_TID_STATS_MAX + 1] = { + [NL80211_TID_STATS_RX_MSDU] = { .type = NLA_U64 }, + [NL80211_TID_STATS_TX_MSDU] = { .type = NLA_U64 }, + [NL80211_TID_STATS_TX_MSDU_RETRIES] = { .type = NLA_U64 }, + [NL80211_TID_STATS_TX_MSDU_FAILED] = { .type = NLA_U64 }, + }; + int rem, i = 0; + + printf("\n\tMSDU:\n\t\tTID\trx\ttx\ttx retries\ttx failed"); + nla_for_each_nested(tidattr, tid_stats_attr, rem) { + if (nla_parse_nested(stats_info, NL80211_TID_STATS_MAX, + tidattr, stats_policy)) { + printf("failed to parse nested stats attributes!"); + return; + } + printf("\n\t\t%d", i++); + info = stats_info[NL80211_TID_STATS_RX_MSDU]; + if (info) + printf("\t%llu", (unsigned long long)nla_get_u64(info)); + info = stats_info[NL80211_TID_STATS_TX_MSDU]; + if (info) + printf("\t%llu", (unsigned long long)nla_get_u64(info)); + info = stats_info[NL80211_TID_STATS_TX_MSDU_RETRIES]; + if (info) + printf("\t%llu", (unsigned long long)nla_get_u64(info)); + info = stats_info[NL80211_TID_STATS_TX_MSDU_FAILED]; + if (info) + printf("\t\t%llu", (unsigned long long)nla_get_u64(info)); + } +} + +void parse_bss_param(struct nlattr *bss_param_attr) +{ + struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info; + static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = { + [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG }, + [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG }, + [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG }, + [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 }, + [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 }, + }; + + if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX, + bss_param_attr, bss_poilcy)) { + printf("failed to parse nested bss param attributes!"); + } + + info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD]; + if (info) + printf("\n\tDTIM period:\t%u", nla_get_u8(info)); + info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]; + if (info) + printf("\n\tbeacon interval:%u", nla_get_u16(info)); + info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT]; + if (info) { + printf("\n\tCTS protection:"); + if (nla_get_u16(info)) + printf("\tyes"); + else + printf("\tno"); + } + info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]; + if (info) { + printf("\n\tshort preamble:"); + if (nla_get_u16(info)) + printf("\tyes"); + else + printf("\tno"); + } + info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME]; + if (info) { + printf("\n\tshort slot time:"); + if (nla_get_u16(info)) + printf("yes"); + else + printf("no"); + } +} + +void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen) +{ + int rate = 0; + char *pos = buf; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + bitrate_attr, rate_policy)) { + snprintf(buf, buflen, "failed to parse nested rate attributes!"); + return; + } + + if (rinfo[NL80211_RATE_INFO_BITRATE32]) + rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]); + else if (rinfo[NL80211_RATE_INFO_BITRATE]) + rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]); + if (rate > 0) + pos += snprintf(pos, buflen - (pos - buf), + "%d.%d MBit/s", rate / 10, rate % 10); + + if (rinfo[NL80211_RATE_INFO_MCS]) + pos += snprintf(pos, buflen - (pos - buf), + " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS])); + if (rinfo[NL80211_RATE_INFO_VHT_MCS]) + pos += snprintf(pos, buflen - (pos - buf), + " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS])); + if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH]) + pos += snprintf(pos, buflen - (pos - buf), " 40MHz"); + if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH]) + pos += snprintf(pos, buflen - (pos - buf), " 80MHz"); + if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH]) + pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz"); + if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH]) + pos += snprintf(pos, buflen - (pos - buf), " 160MHz"); + if (rinfo[NL80211_RATE_INFO_SHORT_GI]) + pos += snprintf(pos, buflen - (pos - buf), " short GI"); + if (rinfo[NL80211_RATE_INFO_VHT_NSS]) + pos += snprintf(pos, buflen - (pos - buf), + " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS])); +} + +static char *get_chain_signal(struct nlattr *attr_list) +{ + struct nlattr *attr; + static char buf[64]; + char *cur = buf; + int i = 0, rem; + const char *prefix; + if (!attr_list) + return ""; + + nla_for_each_nested(attr, attr_list, rem) { + if (i++ > 0) + prefix = ", "; + else + prefix = "["; + + cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix, + (int8_t) nla_get_u8(attr)); + } + + if (i) + snprintf(cur, sizeof(buf) - (cur - buf), "] "); + + return buf; +} static int print_sta_handler(struct nl_msg *msg, void *arg) { @@ -34,14 +206,39 @@ static int print_sta_handler(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; char mac_addr[20], state_name[10], dev[20]; + struct nl80211_sta_flag_update *sta_flags; static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64}, + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 }, + [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED }, [NL80211_STA_INFO_LLID] = { .type = NLA_U16 }, [NL80211_STA_INFO_PLID] = { .type = NLA_U16 }, [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, + [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, + [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32}, + [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64}, + [NL80211_STA_INFO_STA_FLAGS] = + { .minlen = sizeof(struct nl80211_sta_flag_update) }, + [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32}, + [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32}, + [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32}, + [NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_TID_STATS] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_BSS_PARAM] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 }, }; + char *chain; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -53,13 +250,13 @@ static int print_sta_handler(struct nl_msg *msg, void *arg) */ if (!tb[NL80211_ATTR_STA_INFO]) { - fprintf(stderr, "sta stats missing!"); + fprintf(stderr, "sta stats missing!\n"); return NL_SKIP; } if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO], stats_policy)) { - fprintf(stderr, "failed to parse nested attributes!"); + fprintf(stderr, "failed to parse nested attributes!\n"); return NL_SKIP; } @@ -68,14 +265,90 @@ static int print_sta_handler(struct nl_msg *msg, void *arg) printf("Station %s (on %s)", mac_addr, dev); if (sinfo[NL80211_STA_INFO_INACTIVE_TIME]) - printf("\n\tinactive time:\t%d ms", + printf("\n\tinactive time:\t%u ms", nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME])); - if (sinfo[NL80211_STA_INFO_RX_BYTES]) - printf("\n\trx bytes:\t%d", - nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES])); - if (sinfo[NL80211_STA_INFO_TX_BYTES]) - printf("\n\ttx bytes:\t%d", - nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES])); + if (sinfo[NL80211_STA_INFO_RX_BYTES64]) + printf("\n\trx bytes:\t%llu", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64])); + else if (sinfo[NL80211_STA_INFO_RX_BYTES]) + printf("\n\trx bytes:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES])); + if (sinfo[NL80211_STA_INFO_RX_PACKETS]) + printf("\n\trx packets:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS])); + if (sinfo[NL80211_STA_INFO_TX_BYTES64]) + printf("\n\ttx bytes:\t%llu", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64])); + else if (sinfo[NL80211_STA_INFO_TX_BYTES]) + printf("\n\ttx bytes:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES])); + if (sinfo[NL80211_STA_INFO_TX_PACKETS]) + printf("\n\ttx packets:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS])); + if (sinfo[NL80211_STA_INFO_TX_RETRIES]) + printf("\n\ttx retries:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES])); + if (sinfo[NL80211_STA_INFO_TX_FAILED]) + printf("\n\ttx failed:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED])); + if (sinfo[NL80211_STA_INFO_BEACON_LOSS]) + printf("\n\tbeacon loss:\t%u", + nla_get_u32(sinfo[NL80211_STA_INFO_BEACON_LOSS])); + if (sinfo[NL80211_STA_INFO_BEACON_RX]) + printf("\n\tbeacon rx:\t%llu", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_BEACON_RX])); + if (sinfo[NL80211_STA_INFO_RX_DROP_MISC]) + printf("\n\trx drop misc:\t%llu", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC])); + + chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]); + if (sinfo[NL80211_STA_INFO_SIGNAL]) + printf("\n\tsignal: \t%d %sdBm", + (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), + chain); + + chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]); + if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) + printf("\n\tsignal avg:\t%d %sdBm", + (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]), + chain); + + if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) + printf("\n\tbeacon signal avg:\t%d dBm", + nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])); + if (sinfo[NL80211_STA_INFO_T_OFFSET]) + printf("\n\tToffset:\t%llu us", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET])); + + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + char buf[100]; + + parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf)); + printf("\n\ttx bitrate:\t%s", buf); + } + + if (sinfo[NL80211_STA_INFO_RX_BITRATE]) { + char buf[100]; + + parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf)); + printf("\n\trx bitrate:\t%s", buf); + } + + if (sinfo[NL80211_STA_INFO_RX_DURATION]) + printf("\n\trx duration:\t%lld us", + (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION])); + + if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) { + uint32_t thr; + + thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]); + /* convert in Mbps but scale by 1000 to save kbps units */ + thr = thr * 1000 / 1024; + + printf("\n\texpected throughput:\t%u.%uMbps", + thr / 1000, thr % 1000); + } + if (sinfo[NL80211_STA_INFO_LLID]) printf("\n\tmesh llid:\t%d", nla_get_u16(sinfo[NL80211_STA_INFO_LLID])); @@ -83,7 +356,7 @@ static int print_sta_handler(struct nl_msg *msg, void *arg) printf("\n\tmesh plid:\t%d", nla_get_u16(sinfo[NL80211_STA_INFO_PLID])); if (sinfo[NL80211_STA_INFO_PLINK_STATE]) { - switch (nla_get_u16(sinfo[NL80211_STA_INFO_PLINK_STATE])) { + switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) { case LISTEN: strcpy(state_name, "LISTEN"); break; @@ -111,14 +384,97 @@ static int print_sta_handler(struct nl_msg *msg, void *arg) } printf("\n\tmesh plink:\t%s", state_name); } + if (sinfo[NL80211_STA_INFO_LOCAL_PM]) { + printf("\n\tmesh local PS mode:\t"); + print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]); + } + if (sinfo[NL80211_STA_INFO_PEER_PM]) { + printf("\n\tmesh peer PS mode:\t"); + print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]); + } + if (sinfo[NL80211_STA_INFO_NONPEER_PM]) { + printf("\n\tmesh non-peer PS mode:\t"); + print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]); + } + + if (sinfo[NL80211_STA_INFO_STA_FLAGS]) { + sta_flags = (struct nl80211_sta_flag_update *) + nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]); + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + printf("\n\tauthorized:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + printf("yes"); + else + printf("no"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + printf("\n\tauthenticated:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + printf("yes"); + else + printf("no"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) { + printf("\n\tassociated:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_ASSOCIATED)) + printf("yes"); + else + printf("no"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { + printf("\n\tpreamble:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) + printf("short"); + else + printf("long"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) { + printf("\n\tWMM/WME:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_WME)) + printf("yes"); + else + printf("no"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) { + printf("\n\tMFP:\t\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP)) + printf("yes"); + else + printf("no"); + } + + if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + printf("\n\tTDLS peer:\t"); + if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + printf("yes"); + else + printf("no"); + } + } + + if (sinfo[NL80211_STA_INFO_TID_STATS] && arg != NULL && + !strcmp((char *)arg, "-v")) + parse_tid_stats(sinfo[NL80211_STA_INFO_TID_STATS]); + if (sinfo[NL80211_STA_INFO_BSS_PARAM]) + parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]); + if (sinfo[NL80211_STA_INFO_CONNECTED_TIME]) + printf("\n\tconnected time:\t%u seconds", + nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME])); printf("\n"); return NL_SKIP; } -static int handle_station_get(struct nl_cb *cb, +static int handle_station_get(struct nl80211_state *state, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { unsigned char mac_addr[ETH_ALEN]; @@ -138,20 +494,99 @@ static int handle_station_get(struct nl_cb *cb, NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL); + register_handler(print_sta_handler, NULL); return 0; nla_put_failure: return -ENOBUFS; } COMMAND(station, get, "", - NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get); -COMMAND(station, del, "", - NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_get); + NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get, + "Get information for a specific station."); -static int handle_station_set(struct nl_cb *cb, +static int handle_station_del(struct nl80211_state *state, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) +{ + char *end; + unsigned char mac_addr[ETH_ALEN]; + int subtype; + int reason_code; + + if (argc < 1) + return 1; + + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "invalid mac address\n"); + return 2; + } + + argc--; + argv++; + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + if (argc > 1 && strcmp(argv[0], "subtype") == 0) { + argv++; + argc--; + + subtype = strtod(argv[0], &end); + if (*end != '\0') + return 1; + + NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, subtype); + argv++; + argc--; + } + + if (argc > 1 && strcmp(argv[0], "reason-code") == 0) { + argv++; + argc--; + + reason_code = strtod(argv[0], &end); + if (*end != '\0') + return 1; + + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); + argv++; + argc--; + } + + if (argc) + return 1; + + register_handler(print_sta_handler, NULL); + + return 0; + nla_put_failure: + return -ENOBUFS; +} +COMMAND(station, del, " [subtype ] [reason-code ]", + NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_del, + "Remove the given station entry (use with caution!)\n" + "Example subtype values: 0xA (disassociation), 0xC (deauthentication)"); + +static const struct cmd *station_set_plink; +static const struct cmd *station_set_vlan; +static const struct cmd *station_set_mesh_power_mode; + +static const struct cmd *select_station_cmd(int argc, char **argv) +{ + if (argc < 2) + return NULL; + if (strcmp(argv[1], "plink_action") == 0) + return station_set_plink; + if (strcmp(argv[1], "vlan") == 0) + return station_set_vlan; + if (strcmp(argv[1], "mesh_power_mode") == 0) + return station_set_mesh_power_mode; + return NULL; +} + +static int handle_station_set_plink(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) { unsigned char plink_action; unsigned char mac_addr[ETH_ALEN]; @@ -172,9 +607,9 @@ static int handle_station_set(struct nl_cb *cb, argv++; if (strcmp("open", argv[0]) == 0) - plink_action = PLINK_ACTION_OPEN; + plink_action = NL80211_PLINK_ACTION_OPEN; else if (strcmp("block", argv[0]) == 0) - plink_action = PLINK_ACTION_BLOCK; + plink_action = NL80211_PLINK_ACTION_BLOCK; else { fprintf(stderr, "plink action not supported\n"); return 2; @@ -192,15 +627,118 @@ static int handle_station_set(struct nl_cb *cb, nla_put_failure: return -ENOBUFS; } -COMMAND(station, set, " plink_action ", - NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set); +COMMAND_ALIAS(station, set, " plink_action ", + NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink, + "Set mesh peer link action for this station (peer).", + select_station_cmd, station_set_plink); + +static int handle_station_set_vlan(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + unsigned char mac_addr[ETH_ALEN]; + unsigned long sta_vlan = 0; + char *err = NULL; + + if (argc < 3) + return 1; + + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "invalid mac address\n"); + return 2; + } + argc--; + argv++; + + if (strcmp("vlan", argv[0]) != 0) + return 1; + argc--; + argv++; + + sta_vlan = strtoul(argv[0], &err, 0); + if (err && *err) { + fprintf(stderr, "invalid vlan id\n"); + return 2; + } + argc--; + argv++; + + if (argc) + return 1; + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan); + + return 0; + nla_put_failure: + return -ENOBUFS; +} +COMMAND_ALIAS(station, set, " vlan ", + NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan, + "Set an AP VLAN for this station.", + select_station_cmd, station_set_vlan); + +static int handle_station_set_mesh_power_mode(struct nl80211_state *state, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + unsigned char mesh_power_mode; + unsigned char mac_addr[ETH_ALEN]; + + if (argc < 3) + return 1; + + if (mac_addr_a2n(mac_addr, argv[0])) { + fprintf(stderr, "invalid mac address\n"); + return 2; + } + argc--; + argv++; + + if (strcmp("mesh_power_mode", argv[0]) != 0) + return 1; + argc--; + argv++; + + if (strcmp("active", argv[0]) == 0) + mesh_power_mode = NL80211_MESH_POWER_ACTIVE; + else if (strcmp("light", argv[0]) == 0) + mesh_power_mode = NL80211_MESH_POWER_LIGHT_SLEEP; + else if (strcmp("deep", argv[0]) == 0) + mesh_power_mode = NL80211_MESH_POWER_DEEP_SLEEP; + else { + fprintf(stderr, "unknown mesh power mode\n"); + return 2; + } + argc--; + argv++; + + if (argc) + return 1; + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + NLA_PUT_U32(msg, NL80211_ATTR_LOCAL_MESH_POWER_MODE, mesh_power_mode); + + return 0; +nla_put_failure: + return -ENOBUFS; +} +COMMAND_ALIAS(station, set, " mesh_power_mode " + "", NL80211_CMD_SET_STATION, 0, CIB_NETDEV, + handle_station_set_mesh_power_mode, + "Set link-specific mesh power mode for this station", + select_station_cmd, station_set_mesh_power_mode); -static int handle_station_dump(struct nl_cb *cb, +static int handle_station_dump(struct nl80211_state *state, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_sta_handler, NULL); + register_handler(print_sta_handler, *argv); return 0; } -COMMAND(station, dump, NULL, - NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump); +COMMAND(station, dump, "[-v]", + NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump, + "List all stations known, e.g. the AP on managed interfaces");