]> git.ipfire.org Git - thirdparty/iw.git/blame - station.c
iw: separate wait/print when waiting for an event
[thirdparty/iw.git] / station.c
CommitLineData
2ef1be68 1#include <net/if.h>
3d1e8704 2#include <errno.h>
d5ac8ad3 3#include <string.h>
2ef1be68 4
3d1e8704
LCC
5#include <netlink/genl/genl.h>
6#include <netlink/genl/family.h>
7#include <netlink/genl/ctrl.h>
8#include <netlink/msg.h>
9#include <netlink/attr.h>
3d1e8704 10
f408e01b 11#include "nl80211.h"
3d1e8704
LCC
12#include "iw.h"
13
4698bfc2
JB
14SECTION(station);
15
3d1e8704
LCC
16enum plink_state {
17 LISTEN,
18 OPN_SNT,
19 OPN_RCVD,
20 CNF_RCVD,
21 ESTAB,
22 HOLDING,
23 BLOCKED
24};
25
8012ad28
MP
26static void print_power_mode(struct nlattr *a)
27{
28 enum nl80211_mesh_power_mode pm = nla_get_u32(a);
29
30 switch (pm) {
31 case NL80211_MESH_POWER_ACTIVE:
32 printf("ACTIVE");
33 break;
34 case NL80211_MESH_POWER_LIGHT_SLEEP:
35 printf("LIGHT SLEEP");
36 break;
37 case NL80211_MESH_POWER_DEEP_SLEEP:
38 printf("DEEP SLEEP");
39 break;
40 default:
41 printf("UNKNOWN");
42 break;
43 }
44}
3d1e8704 45
910792c0
THJ
46int parse_txq_stats(char *buf, int buflen, struct nlattr *tid_stats_attr, int header,
47 int tid, const char *indent)
48{
49 struct nlattr *txqstats_info[NL80211_TXQ_STATS_MAX + 1], *txqinfo;
50 static struct nla_policy txqstats_policy[NL80211_TXQ_STATS_MAX + 1] = {
51 [NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
52 [NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
53 [NL80211_TXQ_STATS_FLOWS] = { .type = NLA_U32 },
54 [NL80211_TXQ_STATS_DROPS] = { .type = NLA_U32 },
55 [NL80211_TXQ_STATS_ECN_MARKS] = { .type = NLA_U32 },
56 [NL80211_TXQ_STATS_OVERLIMIT] = { .type = NLA_U32 },
57 [NL80211_TXQ_STATS_COLLISIONS] = { .type = NLA_U32 },
58 [NL80211_TXQ_STATS_TX_BYTES] = { .type = NLA_U32 },
59 [NL80211_TXQ_STATS_TX_PACKETS] = { .type = NLA_U32 },
60 };
61 char *pos = buf;
62 if (nla_parse_nested(txqstats_info, NL80211_TXQ_STATS_MAX, tid_stats_attr,
63 txqstats_policy)) {
64 printf("failed to parse nested TXQ stats attributes!");
65 return 0;
66 }
67
68 if (header)
69 pos += snprintf(buf, buflen, "\n%s\t%s\tqsz-byt\t"
70 "qsz-pkt\tflows\tdrops\tmarks\toverlmt\t"
71 "hashcol\ttx-bytes\ttx-packets", indent,
72 tid >= 0 ? "TID" : "");
73
74 pos += snprintf(pos, buflen - (pos - buf), "\n%s\t", indent);
75 if (tid >= 0)
76 pos += snprintf(pos, buflen - (pos - buf), "%d", tid);
77
78#define PRINT_STAT(key, spacer) do { \
79 txqinfo = txqstats_info[NL80211_TXQ_STATS_ ## key]; \
80 pos += snprintf(pos, buflen - (pos - buf), spacer); \
81 if (txqinfo) \
82 pos += snprintf(pos, buflen - (pos - buf), "%u", \
83 nla_get_u32(txqinfo)); \
84 } while (0)
85
86
87 PRINT_STAT(BACKLOG_BYTES, "\t");
88 PRINT_STAT(BACKLOG_PACKETS, "\t");
89 PRINT_STAT(FLOWS, "\t");
90 PRINT_STAT(DROPS, "\t");
91 PRINT_STAT(ECN_MARKS, "\t");
92 PRINT_STAT(OVERLIMIT, "\t");
93 PRINT_STAT(COLLISIONS, "\t");
94 PRINT_STAT(TX_BYTES, "\t");
95 PRINT_STAT(TX_PACKETS, "\t\t");
96
97#undef PRINT_STAT
98
99 return pos - buf;
100
101}
bcdceae1
JB
102
103static void parse_tid_stats(struct nlattr *tid_stats_attr)
568c7057
AE
104{
105 struct nlattr *stats_info[NL80211_TID_STATS_MAX + 1], *tidattr, *info;
106 static struct nla_policy stats_policy[NL80211_TID_STATS_MAX + 1] = {
107 [NL80211_TID_STATS_RX_MSDU] = { .type = NLA_U64 },
108 [NL80211_TID_STATS_TX_MSDU] = { .type = NLA_U64 },
109 [NL80211_TID_STATS_TX_MSDU_RETRIES] = { .type = NLA_U64 },
110 [NL80211_TID_STATS_TX_MSDU_FAILED] = { .type = NLA_U64 },
910792c0 111 [NL80211_TID_STATS_TXQ_STATS] = { .type = NLA_NESTED },
568c7057
AE
112 };
113 int rem, i = 0;
910792c0
THJ
114 char txqbuf[2000] = {}, *pos = txqbuf;
115 int buflen = sizeof(txqbuf), foundtxq = 0;
568c7057
AE
116
117 printf("\n\tMSDU:\n\t\tTID\trx\ttx\ttx retries\ttx failed");
118 nla_for_each_nested(tidattr, tid_stats_attr, rem) {
119 if (nla_parse_nested(stats_info, NL80211_TID_STATS_MAX,
120 tidattr, stats_policy)) {
121 printf("failed to parse nested stats attributes!");
122 return;
123 }
910792c0 124 printf("\n\t\t%d", i);
568c7057
AE
125 info = stats_info[NL80211_TID_STATS_RX_MSDU];
126 if (info)
127 printf("\t%llu", (unsigned long long)nla_get_u64(info));
128 info = stats_info[NL80211_TID_STATS_TX_MSDU];
129 if (info)
130 printf("\t%llu", (unsigned long long)nla_get_u64(info));
131 info = stats_info[NL80211_TID_STATS_TX_MSDU_RETRIES];
132 if (info)
133 printf("\t%llu", (unsigned long long)nla_get_u64(info));
134 info = stats_info[NL80211_TID_STATS_TX_MSDU_FAILED];
135 if (info)
136 printf("\t\t%llu", (unsigned long long)nla_get_u64(info));
910792c0
THJ
137 info = stats_info[NL80211_TID_STATS_TXQ_STATS];
138 if (info) {
139 pos += parse_txq_stats(pos, buflen - (pos - txqbuf), info, !foundtxq, i, "\t");
140 foundtxq = 1;
141 }
142
143 i++;
568c7057 144 }
910792c0
THJ
145
146 if (foundtxq)
147 printf("\n\tTXQs:%s", txqbuf);
568c7057
AE
148}
149
bcdceae1 150static void parse_bss_param(struct nlattr *bss_param_attr)
568c7057
AE
151{
152 struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info;
153 static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = {
154 [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
155 [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
156 [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
157 [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
158 [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
159 };
160
161 if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX,
162 bss_param_attr, bss_poilcy)) {
163 printf("failed to parse nested bss param attributes!");
164 }
165
166 info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD];
167 if (info)
168 printf("\n\tDTIM period:\t%u", nla_get_u8(info));
169 info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL];
170 if (info)
171 printf("\n\tbeacon interval:%u", nla_get_u16(info));
172 info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT];
173 if (info) {
174 printf("\n\tCTS protection:");
175 if (nla_get_u16(info))
176 printf("\tyes");
177 else
178 printf("\tno");
179 }
180 info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE];
181 if (info) {
182 printf("\n\tshort preamble:");
183 if (nla_get_u16(info))
184 printf("\tyes");
185 else
186 printf("\tno");
187 }
188 info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME];
189 if (info) {
190 printf("\n\tshort slot time:");
191 if (nla_get_u16(info))
192 printf("yes");
193 else
194 printf("no");
195 }
196}
197
e94f0201 198void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
64179590
JB
199{
200 int rate = 0;
201 char *pos = buf;
202 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
203 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
204 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
205 [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
206 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
207 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
208 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
209 };
210
211 if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
212 bitrate_attr, rate_policy)) {
213 snprintf(buf, buflen, "failed to parse nested rate attributes!");
214 return;
215 }
216
217 if (rinfo[NL80211_RATE_INFO_BITRATE32])
218 rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
219 else if (rinfo[NL80211_RATE_INFO_BITRATE])
220 rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
221 if (rate > 0)
222 pos += snprintf(pos, buflen - (pos - buf),
223 "%d.%d MBit/s", rate / 10, rate % 10);
5ce1f6c7
MH
224 else
225 pos += snprintf(pos, buflen - (pos - buf), "(unknown)");
64179590
JB
226
227 if (rinfo[NL80211_RATE_INFO_MCS])
228 pos += snprintf(pos, buflen - (pos - buf),
229 " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
230 if (rinfo[NL80211_RATE_INFO_VHT_MCS])
231 pos += snprintf(pos, buflen - (pos - buf),
232 " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
233 if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
234 pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
235 if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
236 pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
237 if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
238 pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
239 if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
240 pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
241 if (rinfo[NL80211_RATE_INFO_SHORT_GI])
242 pos += snprintf(pos, buflen - (pos - buf), " short GI");
243 if (rinfo[NL80211_RATE_INFO_VHT_NSS])
244 pos += snprintf(pos, buflen - (pos - buf),
245 " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
246}
247
7d23bc2d
FF
248static char *get_chain_signal(struct nlattr *attr_list)
249{
250 struct nlattr *attr;
251 static char buf[64];
252 char *cur = buf;
253 int i = 0, rem;
254 const char *prefix;
255
256 if (!attr_list)
257 return "";
258
259 nla_for_each_nested(attr, attr_list, rem) {
260 if (i++ > 0)
261 prefix = ", ";
262 else
263 prefix = "[";
264
265 cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
266 (int8_t) nla_get_u8(attr));
267 }
268
269 if (i)
270 snprintf(cur, sizeof(buf) - (cur - buf), "] ");
271
272 return buf;
273}
274
3d1e8704
LCC
275static int print_sta_handler(struct nl_msg *msg, void *arg)
276{
277 struct nlattr *tb[NL80211_ATTR_MAX + 1];
278 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
279 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
280 char mac_addr[20], state_name[10], dev[20];
b638215d 281 struct nl80211_sta_flag_update *sta_flags;
3d1e8704
LCC
282 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
283 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
284 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
285 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
568c7057
AE
286 [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
287 [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
859677cb
JB
288 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
289 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
568c7057 290 [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64},
9cd3f1c1 291 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
ea3ade85 292 [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
9cd3f1c1 293 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
e94f0201 294 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
3d1e8704
LCC
295 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
296 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
297 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
0f5868ee
BR
298 [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
299 [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
568c7057
AE
300 [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32},
301 [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64},
b638215d
HS
302 [NL80211_STA_INFO_STA_FLAGS] =
303 { .minlen = sizeof(struct nl80211_sta_flag_update) },
8012ad28
MP
304 [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32},
305 [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32},
306 [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32},
7d23bc2d
FF
307 [NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED },
308 [NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED },
568c7057
AE
309 [NL80211_STA_INFO_TID_STATS] = { .type = NLA_NESTED },
310 [NL80211_STA_INFO_BSS_PARAM] = { .type = NLA_NESTED },
045c1c6f 311 [NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
d2272671
BP
312 [NL80211_STA_INFO_ACK_SIGNAL] = {.type = NLA_U8 },
313 [NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_U8 },
3d1e8704 314 };
7d23bc2d 315 char *chain;
3d1e8704
LCC
316
317 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
318 genlmsg_attrlen(gnlh, 0), NULL);
319
320 /*
321 * TODO: validate the interface and mac address!
322 * Otherwise, there's a race condition as soon as
323 * the kernel starts sending station notifications.
324 */
325
326 if (!tb[NL80211_ATTR_STA_INFO]) {
5fe70c0e 327 fprintf(stderr, "sta stats missing!\n");
3d1e8704
LCC
328 return NL_SKIP;
329 }
330 if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
331 tb[NL80211_ATTR_STA_INFO],
332 stats_policy)) {
5fe70c0e 333 fprintf(stderr, "failed to parse nested attributes!\n");
3d1e8704
LCC
334 return NL_SKIP;
335 }
336
337 mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
338 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
fbb181fa 339 printf("Station %s (on %s)", mac_addr, dev);
3d1e8704
LCC
340
341 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
f5c9799c 342 printf("\n\tinactive time:\t%u ms",
3d1e8704 343 nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
568c7057
AE
344 if (sinfo[NL80211_STA_INFO_RX_BYTES64])
345 printf("\n\trx bytes:\t%llu",
346 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
347 else if (sinfo[NL80211_STA_INFO_RX_BYTES])
f5c9799c 348 printf("\n\trx bytes:\t%u",
568c7057 349 nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
859677cb 350 if (sinfo[NL80211_STA_INFO_RX_PACKETS])
f5c9799c 351 printf("\n\trx packets:\t%u",
859677cb 352 nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
568c7057
AE
353 if (sinfo[NL80211_STA_INFO_TX_BYTES64])
354 printf("\n\ttx bytes:\t%llu",
355 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
356 else if (sinfo[NL80211_STA_INFO_TX_BYTES])
f5c9799c 357 printf("\n\ttx bytes:\t%u",
568c7057 358 nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
859677cb 359 if (sinfo[NL80211_STA_INFO_TX_PACKETS])
f5c9799c 360 printf("\n\ttx packets:\t%u",
859677cb 361 nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
0f5868ee
BR
362 if (sinfo[NL80211_STA_INFO_TX_RETRIES])
363 printf("\n\ttx retries:\t%u",
364 nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
365 if (sinfo[NL80211_STA_INFO_TX_FAILED])
366 printf("\n\ttx failed:\t%u",
367 nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
568c7057
AE
368 if (sinfo[NL80211_STA_INFO_BEACON_LOSS])
369 printf("\n\tbeacon loss:\t%u",
370 nla_get_u32(sinfo[NL80211_STA_INFO_BEACON_LOSS]));
371 if (sinfo[NL80211_STA_INFO_BEACON_RX])
372 printf("\n\tbeacon rx:\t%llu",
373 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_BEACON_RX]));
374 if (sinfo[NL80211_STA_INFO_RX_DROP_MISC])
375 printf("\n\trx drop misc:\t%llu",
376 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC]));
7d23bc2d
FF
377
378 chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
9cd3f1c1 379 if (sinfo[NL80211_STA_INFO_SIGNAL])
7d23bc2d
FF
380 printf("\n\tsignal: \t%d %sdBm",
381 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
382 chain);
383
384 chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
ba292ae9 385 if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
7d23bc2d
FF
386 printf("\n\tsignal avg:\t%d %sdBm",
387 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
388 chain);
389
568c7057
AE
390 if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
391 printf("\n\tbeacon signal avg:\t%d dBm",
0fc92835 392 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]));
ea3ade85 393 if (sinfo[NL80211_STA_INFO_T_OFFSET])
568c7057
AE
394 printf("\n\tToffset:\t%llu us",
395 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]));
9cd3f1c1
HR
396
397 if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
64179590
JB
398 char buf[100];
399
e94f0201 400 parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
64179590 401 printf("\n\ttx bitrate:\t%s", buf);
9cd3f1c1
HR
402 }
403
e94f0201
FF
404 if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
405 char buf[100];
406
407 parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
408 printf("\n\trx bitrate:\t%s", buf);
409 }
410
045c1c6f
MSS
411 if (sinfo[NL80211_STA_INFO_RX_DURATION])
412 printf("\n\trx duration:\t%lld us",
413 (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION]));
414
d2272671
BP
415 if (sinfo[NL80211_STA_INFO_ACK_SIGNAL])
416 printf("\n\tlast ack signal:%d dBm",
417 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL]));
418
419 if (sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG])
420 printf("\n\tavg ack signal:\t%d dBm",
421 (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG]));
422
cf8a2aab
AQ
423 if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
424 uint32_t thr;
425
426 thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
427 /* convert in Mbps but scale by 1000 to save kbps units */
428 thr = thr * 1000 / 1024;
429
430 printf("\n\texpected throughput:\t%u.%uMbps",
431 thr / 1000, thr % 1000);
432 }
433
3d1e8704 434 if (sinfo[NL80211_STA_INFO_LLID])
fbb181fa 435 printf("\n\tmesh llid:\t%d",
3d1e8704
LCC
436 nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
437 if (sinfo[NL80211_STA_INFO_PLID])
fbb181fa 438 printf("\n\tmesh plid:\t%d",
3d1e8704
LCC
439 nla_get_u16(sinfo[NL80211_STA_INFO_PLID]));
440 if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
dbaabba1 441 switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) {
3d1e8704
LCC
442 case LISTEN:
443 strcpy(state_name, "LISTEN");
444 break;
445 case OPN_SNT:
446 strcpy(state_name, "OPN_SNT");
447 break;
448 case OPN_RCVD:
449 strcpy(state_name, "OPN_RCVD");
450 break;
451 case CNF_RCVD:
452 strcpy(state_name, "CNF_RCVD");
453 break;
454 case ESTAB:
455 strcpy(state_name, "ESTAB");
456 break;
457 case HOLDING:
458 strcpy(state_name, "HOLDING");
459 break;
460 case BLOCKED:
461 strcpy(state_name, "BLOCKED");
462 break;
463 default:
464 strcpy(state_name, "UNKNOWN");
465 break;
466 }
fbb181fa 467 printf("\n\tmesh plink:\t%s", state_name);
3d1e8704 468 }
8012ad28
MP
469 if (sinfo[NL80211_STA_INFO_LOCAL_PM]) {
470 printf("\n\tmesh local PS mode:\t");
471 print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]);
472 }
473 if (sinfo[NL80211_STA_INFO_PEER_PM]) {
474 printf("\n\tmesh peer PS mode:\t");
475 print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]);
476 }
477 if (sinfo[NL80211_STA_INFO_NONPEER_PM]) {
478 printf("\n\tmesh non-peer PS mode:\t");
479 print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]);
480 }
3d1e8704 481
b638215d
HS
482 if (sinfo[NL80211_STA_INFO_STA_FLAGS]) {
483 sta_flags = (struct nl80211_sta_flag_update *)
484 nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
485
486 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
487 printf("\n\tauthorized:\t");
488 if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED))
489 printf("yes");
490 else
491 printf("no");
492 }
493
494 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
495 printf("\n\tauthenticated:\t");
496 if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
497 printf("yes");
498 else
499 printf("no");
500 }
501
568c7057
AE
502 if (sta_flags->mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
503 printf("\n\tassociated:\t");
504 if (sta_flags->set & BIT(NL80211_STA_FLAG_ASSOCIATED))
505 printf("yes");
506 else
507 printf("no");
508 }
509
b638215d
HS
510 if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
511 printf("\n\tpreamble:\t");
512 if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
513 printf("short");
514 else
515 printf("long");
516 }
517
518 if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) {
519 printf("\n\tWMM/WME:\t");
520 if (sta_flags->set & BIT(NL80211_STA_FLAG_WME))
521 printf("yes");
522 else
523 printf("no");
524 }
525
526 if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) {
527 printf("\n\tMFP:\t\t");
528 if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP))
529 printf("yes");
530 else
531 printf("no");
532 }
533
534 if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
1a463ae3 535 printf("\n\tTDLS peer:\t");
b638215d
HS
536 if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER))
537 printf("yes");
538 else
539 printf("no");
540 }
541 }
542
568c7057
AE
543 if (sinfo[NL80211_STA_INFO_TID_STATS] && arg != NULL &&
544 !strcmp((char *)arg, "-v"))
545 parse_tid_stats(sinfo[NL80211_STA_INFO_TID_STATS]);
546 if (sinfo[NL80211_STA_INFO_BSS_PARAM])
547 parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]);
087d778f
AN
548 if (sinfo[NL80211_STA_INFO_CONNECTED_TIME])
549 printf("\n\tconnected time:\t%u seconds",
550 nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]));
551
3d1e8704
LCC
552 printf("\n");
553 return NL_SKIP;
554}
555
7c37a24d 556static int handle_station_get(struct nl80211_state *state,
b1ca19a8 557 struct nl_msg *msg,
05514f95
JB
558 int argc, char **argv,
559 enum id_input id)
3d1e8704 560{
3d1e8704
LCC
561 unsigned char mac_addr[ETH_ALEN];
562
b1ca19a8 563 if (argc < 1)
5e75fd04 564 return 1;
3d1e8704
LCC
565
566 if (mac_addr_a2n(mac_addr, argv[0])) {
567 fprintf(stderr, "invalid mac address\n");
5e75fd04 568 return 2;
3d1e8704
LCC
569 }
570
571 argc--;
572 argv++;
573
b1ca19a8 574 if (argc)
5e75fd04 575 return 1;
3d1e8704
LCC
576
577 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704 578
34b23014 579 register_handler(print_sta_handler, NULL);
3d1e8704 580
70391ccf 581 return 0;
3d1e8704 582 nla_put_failure:
70391ccf 583 return -ENOBUFS;
3d1e8704 584}
b1ca19a8 585COMMAND(station, get, "<MAC address>",
70cf4544
JB
586 NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get,
587 "Get information for a specific station.");
d738686c
RM
588
589static int handle_station_del(struct nl80211_state *state,
590 struct nl_msg *msg,
591 int argc, char **argv,
592 enum id_input id)
593{
594 char *end;
595 unsigned char mac_addr[ETH_ALEN];
596 int subtype;
597 int reason_code;
598
599 if (argc < 1)
600 return 1;
601
602 if (mac_addr_a2n(mac_addr, argv[0])) {
603 fprintf(stderr, "invalid mac address\n");
604 return 2;
605 }
606
607 argc--;
608 argv++;
609 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
610
611 if (argc > 1 && strcmp(argv[0], "subtype") == 0) {
612 argv++;
613 argc--;
614
615 subtype = strtod(argv[0], &end);
616 if (*end != '\0')
617 return 1;
618
619 NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, subtype);
620 argv++;
621 argc--;
622 }
623
624 if (argc > 1 && strcmp(argv[0], "reason-code") == 0) {
625 argv++;
626 argc--;
627
628 reason_code = strtod(argv[0], &end);
629 if (*end != '\0')
630 return 1;
631
632 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
633 argv++;
634 argc--;
635 }
636
637 if (argc)
638 return 1;
639
640 register_handler(print_sta_handler, NULL);
641
642 return 0;
643 nla_put_failure:
644 return -ENOBUFS;
645}
646COMMAND(station, del, "<MAC address> [subtype <subtype>] [reason-code <code>]",
647 NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_del,
648 "Remove the given station entry (use with caution!)\n"
649 "Example subtype values: 0xA (disassociation), 0xC (deauthentication)");
3d1e8704 650
1633ddf7
JB
651static const struct cmd *station_set_plink;
652static const struct cmd *station_set_vlan;
8012ad28 653static const struct cmd *station_set_mesh_power_mode;
1633ddf7
JB
654
655static const struct cmd *select_station_cmd(int argc, char **argv)
656{
657 if (argc < 2)
658 return NULL;
659 if (strcmp(argv[1], "plink_action") == 0)
660 return station_set_plink;
661 if (strcmp(argv[1], "vlan") == 0)
662 return station_set_vlan;
8012ad28
MP
663 if (strcmp(argv[1], "mesh_power_mode") == 0)
664 return station_set_mesh_power_mode;
1633ddf7
JB
665 return NULL;
666}
667
ce0fc33a 668static int handle_station_set_plink(struct nl80211_state *state,
b1ca19a8 669 struct nl_msg *msg,
05514f95
JB
670 int argc, char **argv,
671 enum id_input id)
3d1e8704 672{
3d1e8704
LCC
673 unsigned char plink_action;
674 unsigned char mac_addr[ETH_ALEN];
675
b1ca19a8 676 if (argc < 3)
5e75fd04 677 return 1;
3d1e8704
LCC
678
679 if (mac_addr_a2n(mac_addr, argv[0])) {
680 fprintf(stderr, "invalid mac address\n");
5e75fd04 681 return 2;
3d1e8704
LCC
682 }
683 argc--;
684 argv++;
685
b1ca19a8
JB
686 if (strcmp("plink_action", argv[0]) != 0)
687 return 1;
3d1e8704
LCC
688 argc--;
689 argv++;
690
691 if (strcmp("open", argv[0]) == 0)
ac38f8ad 692 plink_action = NL80211_PLINK_ACTION_OPEN;
3d1e8704 693 else if (strcmp("block", argv[0]) == 0)
ac38f8ad 694 plink_action = NL80211_PLINK_ACTION_BLOCK;
3d1e8704
LCC
695 else {
696 fprintf(stderr, "plink action not supported\n");
5e75fd04 697 return 2;
3d1e8704
LCC
698 }
699 argc--;
700 argv++;
701
b1ca19a8 702 if (argc)
5e75fd04 703 return 1;
3d1e8704
LCC
704
705 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
3d1e8704
LCC
706 NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
707
70391ccf 708 return 0;
3d1e8704 709 nla_put_failure:
70391ccf 710 return -ENOBUFS;
3d1e8704 711}
1633ddf7 712COMMAND_ALIAS(station, set, "<MAC address> plink_action <open|block>",
ce0fc33a 713 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink,
1633ddf7
JB
714 "Set mesh peer link action for this station (peer).",
715 select_station_cmd, station_set_plink);
b1ca19a8 716
ce0fc33a 717static int handle_station_set_vlan(struct nl80211_state *state,
05514f95
JB
718 struct nl_msg *msg,
719 int argc, char **argv,
720 enum id_input id)
ce0fc33a
FF
721{
722 unsigned char mac_addr[ETH_ALEN];
723 unsigned long sta_vlan = 0;
724 char *err = NULL;
725
726 if (argc < 3)
727 return 1;
728
729 if (mac_addr_a2n(mac_addr, argv[0])) {
730 fprintf(stderr, "invalid mac address\n");
731 return 2;
732 }
733 argc--;
734 argv++;
735
736 if (strcmp("vlan", argv[0]) != 0)
737 return 1;
738 argc--;
739 argv++;
740
741 sta_vlan = strtoul(argv[0], &err, 0);
742 if (err && *err) {
743 fprintf(stderr, "invalid vlan id\n");
744 return 2;
745 }
746 argc--;
747 argv++;
748
749 if (argc)
750 return 1;
751
752 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
753 NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan);
754
755 return 0;
756 nla_put_failure:
757 return -ENOBUFS;
758}
1633ddf7 759COMMAND_ALIAS(station, set, "<MAC address> vlan <ifindex>",
ce0fc33a 760 NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan,
1633ddf7
JB
761 "Set an AP VLAN for this station.",
762 select_station_cmd, station_set_vlan);
ce0fc33a 763
8012ad28 764static int handle_station_set_mesh_power_mode(struct nl80211_state *state,
8012ad28
MP
765 struct nl_msg *msg,
766 int argc, char **argv,
767 enum id_input id)
768{
769 unsigned char mesh_power_mode;
770 unsigned char mac_addr[ETH_ALEN];
771
772 if (argc < 3)
773 return 1;
774
775 if (mac_addr_a2n(mac_addr, argv[0])) {
776 fprintf(stderr, "invalid mac address\n");
777 return 2;
778 }
779 argc--;
780 argv++;
781
782 if (strcmp("mesh_power_mode", argv[0]) != 0)
783 return 1;
784 argc--;
785 argv++;
786
787 if (strcmp("active", argv[0]) == 0)
788 mesh_power_mode = NL80211_MESH_POWER_ACTIVE;
789 else if (strcmp("light", argv[0]) == 0)
790 mesh_power_mode = NL80211_MESH_POWER_LIGHT_SLEEP;
791 else if (strcmp("deep", argv[0]) == 0)
792 mesh_power_mode = NL80211_MESH_POWER_DEEP_SLEEP;
793 else {
794 fprintf(stderr, "unknown mesh power mode\n");
795 return 2;
796 }
797 argc--;
798 argv++;
799
800 if (argc)
801 return 1;
802
803 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
804 NLA_PUT_U32(msg, NL80211_ATTR_LOCAL_MESH_POWER_MODE, mesh_power_mode);
805
806 return 0;
807nla_put_failure:
808 return -ENOBUFS;
809}
810COMMAND_ALIAS(station, set, "<MAC address> mesh_power_mode "
811 "<active|light|deep>", NL80211_CMD_SET_STATION, 0, CIB_NETDEV,
812 handle_station_set_mesh_power_mode,
813 "Set link-specific mesh power mode for this station",
814 select_station_cmd, station_set_mesh_power_mode);
ce0fc33a 815
7c37a24d 816static int handle_station_dump(struct nl80211_state *state,
b1ca19a8 817 struct nl_msg *msg,
05514f95
JB
818 int argc, char **argv,
819 enum id_input id)
3d1e8704 820{
568c7057 821 register_handler(print_sta_handler, *argv);
70391ccf 822 return 0;
3d1e8704 823}
568c7057 824COMMAND(station, dump, "[-v]",
70cf4544
JB
825 NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump,
826 "List all stations known, e.g. the AP on managed interfaces");