xref: /aosp_15_r20/external/iw/station.c (revision 92022041c981f431db0b590d0c3272306d0ea2a2)
1 #include <net/if.h>
2 #include <errno.h>
3 #include <string.h>
4 
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>
10 #include <time.h>
11 
12 #include "nl80211.h"
13 #include "iw.h"
14 
15 SECTION(station);
16 
17 enum plink_state {
18 	LISTEN,
19 	OPN_SNT,
20 	OPN_RCVD,
21 	CNF_RCVD,
22 	ESTAB,
23 	HOLDING,
24 	BLOCKED
25 };
26 
print_power_mode(struct nlattr * a)27 static void print_power_mode(struct nlattr *a)
28 {
29 	enum nl80211_mesh_power_mode pm = nla_get_u32(a);
30 
31 	switch (pm) {
32 	case NL80211_MESH_POWER_ACTIVE:
33 		printf("ACTIVE");
34 		break;
35 	case NL80211_MESH_POWER_LIGHT_SLEEP:
36 		printf("LIGHT SLEEP");
37 		break;
38 	case NL80211_MESH_POWER_DEEP_SLEEP:
39 		printf("DEEP SLEEP");
40 		break;
41 	default:
42 		printf("UNKNOWN");
43 		break;
44 	}
45 }
46 
parse_txq_stats(char * buf,int buflen,struct nlattr * tid_stats_attr,int header,int tid,const char * indent)47 int parse_txq_stats(char *buf, int buflen, struct nlattr *tid_stats_attr, int header,
48 		    int tid, const char *indent)
49 {
50 	struct nlattr *txqstats_info[NL80211_TXQ_STATS_MAX + 1], *txqinfo;
51 	static struct nla_policy txqstats_policy[NL80211_TXQ_STATS_MAX + 1] = {
52 		[NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
53 		[NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
54 		[NL80211_TXQ_STATS_FLOWS] = { .type = NLA_U32 },
55 		[NL80211_TXQ_STATS_DROPS] = { .type = NLA_U32 },
56 		[NL80211_TXQ_STATS_ECN_MARKS] = { .type = NLA_U32 },
57 		[NL80211_TXQ_STATS_OVERLIMIT] = { .type = NLA_U32 },
58 		[NL80211_TXQ_STATS_COLLISIONS] = { .type = NLA_U32 },
59 		[NL80211_TXQ_STATS_TX_BYTES] = { .type = NLA_U32 },
60 		[NL80211_TXQ_STATS_TX_PACKETS] = { .type = NLA_U32 },
61 	};
62 	char *pos = buf;
63 	if (nla_parse_nested(txqstats_info, NL80211_TXQ_STATS_MAX, tid_stats_attr,
64 			     txqstats_policy)) {
65 		printf("failed to parse nested TXQ stats attributes!");
66 		return 0;
67 	}
68 
69 	if (header)
70 		pos += snprintf(buf, buflen, "\n%s\t%s\tqsz-byt\t"
71 				"qsz-pkt\tflows\tdrops\tmarks\toverlmt\t"
72 				"hashcol\ttx-bytes\ttx-packets", indent,
73 				tid >= 0 ? "TID" : "");
74 
75 	pos += snprintf(pos, buflen - (pos - buf), "\n%s\t", indent);
76 	if (tid >= 0)
77 		pos += snprintf(pos, buflen - (pos - buf), "%d", tid);
78 
79 #define PRINT_STAT(key, spacer) do {					 \
80 		txqinfo = txqstats_info[NL80211_TXQ_STATS_ ## key];	 \
81 		pos += snprintf(pos, buflen - (pos - buf), spacer);	 \
82 		if (txqinfo)						 \
83 			pos += snprintf(pos, buflen - (pos - buf), "%u", \
84 					nla_get_u32(txqinfo));		 \
85 	} while (0)
86 
87 
88 	PRINT_STAT(BACKLOG_BYTES, "\t");
89 	PRINT_STAT(BACKLOG_PACKETS, "\t");
90 	PRINT_STAT(FLOWS, "\t");
91 	PRINT_STAT(DROPS, "\t");
92 	PRINT_STAT(ECN_MARKS, "\t");
93 	PRINT_STAT(OVERLIMIT, "\t");
94 	PRINT_STAT(COLLISIONS, "\t");
95 	PRINT_STAT(TX_BYTES, "\t");
96 	PRINT_STAT(TX_PACKETS, "\t\t");
97 
98 #undef PRINT_STAT
99 
100 	return pos - buf;
101 
102 }
103 
parse_tid_stats(struct nlattr * tid_stats_attr)104 static void parse_tid_stats(struct nlattr *tid_stats_attr)
105 {
106 	struct nlattr *stats_info[NL80211_TID_STATS_MAX + 1], *tidattr, *info;
107 	static struct nla_policy stats_policy[NL80211_TID_STATS_MAX + 1] = {
108 		[NL80211_TID_STATS_RX_MSDU] = { .type = NLA_U64 },
109 		[NL80211_TID_STATS_TX_MSDU] = { .type = NLA_U64 },
110 		[NL80211_TID_STATS_TX_MSDU_RETRIES] = { .type = NLA_U64 },
111 		[NL80211_TID_STATS_TX_MSDU_FAILED] = { .type = NLA_U64 },
112 		[NL80211_TID_STATS_TXQ_STATS] = { .type = NLA_NESTED },
113 	};
114 	int rem, i = 0;
115 	char txqbuf[2000] = {}, *pos = txqbuf;
116 	int buflen = sizeof(txqbuf), foundtxq = 0;
117 
118 	printf("\n\tMSDU:\n\t\tTID\trx\ttx\ttx retries\ttx failed");
119 	nla_for_each_nested(tidattr, tid_stats_attr, rem) {
120 		if (nla_parse_nested(stats_info, NL80211_TID_STATS_MAX,
121 				     tidattr, stats_policy)) {
122 			printf("failed to parse nested stats attributes!");
123 			return;
124 		}
125 		printf("\n\t\t%d", i);
126 		info = stats_info[NL80211_TID_STATS_RX_MSDU];
127 		if (info)
128 			printf("\t%llu", (unsigned long long)nla_get_u64(info));
129 		info = stats_info[NL80211_TID_STATS_TX_MSDU];
130 		if (info)
131 			printf("\t%llu", (unsigned long long)nla_get_u64(info));
132 		info = stats_info[NL80211_TID_STATS_TX_MSDU_RETRIES];
133 		if (info)
134 			printf("\t%llu", (unsigned long long)nla_get_u64(info));
135 		info = stats_info[NL80211_TID_STATS_TX_MSDU_FAILED];
136 		if (info)
137 			printf("\t\t%llu", (unsigned long long)nla_get_u64(info));
138 		info = stats_info[NL80211_TID_STATS_TXQ_STATS];
139 		if (info) {
140 			pos += parse_txq_stats(pos, buflen - (pos - txqbuf), info, !foundtxq, i, "\t");
141 			foundtxq = 1;
142 		}
143 
144 		i++;
145 	}
146 
147 	if (foundtxq)
148 		printf("\n\tTXQs:%s", txqbuf);
149 }
150 
parse_bss_param(struct nlattr * bss_param_attr)151 static void parse_bss_param(struct nlattr *bss_param_attr)
152 {
153 	struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info;
154 	static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = {
155 		[NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
156 		[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
157 		[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
158 		[NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
159 		[NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
160 	};
161 
162 	if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX,
163 			     bss_param_attr, bss_poilcy)) {
164 		printf("failed to parse nested bss param attributes!");
165 	}
166 
167 	info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD];
168 	if (info)
169 		printf("\n\tDTIM period:\t%u", nla_get_u8(info));
170 	info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL];
171 	if (info)
172 		printf("\n\tbeacon interval:%u", nla_get_u16(info));
173 	info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT];
174 	if (info) {
175 		printf("\n\tCTS protection:");
176 		if (nla_get_u16(info))
177 			printf("\tyes");
178 		else
179 			printf("\tno");
180 	}
181 	info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE];
182 	if (info) {
183 		printf("\n\tshort preamble:");
184 		if (nla_get_u16(info))
185 			printf("\tyes");
186 		else
187 			printf("\tno");
188 	}
189 	info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME];
190 	if (info) {
191 		printf("\n\tshort slot time:");
192 		if (nla_get_u16(info))
193 			printf("yes");
194 		else
195 			printf("no");
196 	}
197 }
198 
parse_bitrate(struct nlattr * bitrate_attr,char * buf,int buflen)199 void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
200 {
201 	int rate = 0;
202 	char *pos = buf;
203 	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
204 	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
205 		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
206 		[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
207 		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
208 		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
209 		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
210 	};
211 
212 	if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
213 			     bitrate_attr, rate_policy)) {
214 		snprintf(buf, buflen, "failed to parse nested rate attributes!");
215 		return;
216 	}
217 
218 	if (rinfo[NL80211_RATE_INFO_BITRATE32])
219 		rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
220 	else if (rinfo[NL80211_RATE_INFO_BITRATE])
221 		rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
222 	if (rate > 0)
223 		pos += snprintf(pos, buflen - (pos - buf),
224 				"%d.%d MBit/s", rate / 10, rate % 10);
225 	else
226 		pos += snprintf(pos, buflen - (pos - buf), "(unknown)");
227 
228 	if (rinfo[NL80211_RATE_INFO_MCS])
229 		pos += snprintf(pos, buflen - (pos - buf),
230 				" MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
231 	if (rinfo[NL80211_RATE_INFO_VHT_MCS])
232 		pos += snprintf(pos, buflen - (pos - buf),
233 				" VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
234 	if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
235 		pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
236 	if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
237 		pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
238 	if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
239 		pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
240 	if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
241 		pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
242 	if (rinfo[NL80211_RATE_INFO_320_MHZ_WIDTH])
243 		pos += snprintf(pos, buflen - (pos - buf), " 320MHz");
244 	if (rinfo[NL80211_RATE_INFO_SHORT_GI])
245 		pos += snprintf(pos, buflen - (pos - buf), " short GI");
246 	if (rinfo[NL80211_RATE_INFO_VHT_NSS])
247 		pos += snprintf(pos, buflen - (pos - buf),
248 				" VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
249 	if (rinfo[NL80211_RATE_INFO_HE_MCS])
250 		pos += snprintf(pos, buflen - (pos - buf),
251 				" HE-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_MCS]));
252 	if (rinfo[NL80211_RATE_INFO_HE_NSS])
253 		pos += snprintf(pos, buflen - (pos - buf),
254 				" HE-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_NSS]));
255 	if (rinfo[NL80211_RATE_INFO_HE_GI])
256 		pos += snprintf(pos, buflen - (pos - buf),
257 				" HE-GI %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_GI]));
258 	if (rinfo[NL80211_RATE_INFO_HE_DCM])
259 		pos += snprintf(pos, buflen - (pos - buf),
260 				" HE-DCM %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_DCM]));
261 	if (rinfo[NL80211_RATE_INFO_HE_RU_ALLOC])
262 		pos += snprintf(pos, buflen - (pos - buf),
263 				" HE-RU-ALLOC %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_RU_ALLOC]));
264 	if (rinfo[NL80211_RATE_INFO_EHT_MCS])
265 		pos += snprintf(pos, buflen - (pos - buf),
266 				" EHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_MCS]));
267 	if (rinfo[NL80211_RATE_INFO_EHT_NSS])
268 		pos += snprintf(pos, buflen - (pos - buf),
269 				" EHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_NSS]));
270 	if (rinfo[NL80211_RATE_INFO_EHT_GI])
271 		pos += snprintf(pos, buflen - (pos - buf),
272 				" EHT-GI %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_GI]));
273 	if (rinfo[NL80211_RATE_INFO_EHT_RU_ALLOC])
274 		pos += snprintf(pos, buflen - (pos - buf),
275 				" EHT-RU-ALLOC %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_RU_ALLOC]));
276 }
277 
get_chain_signal(struct nlattr * attr_list)278 static char *get_chain_signal(struct nlattr *attr_list)
279 {
280 	struct nlattr *attr;
281 	static char buf[64];
282 	char *cur = buf;
283 	int i = 0, rem;
284 	const char *prefix;
285 
286 	if (!attr_list)
287 		return "";
288 
289 	nla_for_each_nested(attr, attr_list, rem) {
290 		if (i++ > 0)
291 			prefix = ", ";
292 		else
293 			prefix = "[";
294 
295 		cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
296 				(int8_t) nla_get_u8(attr));
297 	}
298 
299 	if (i)
300 		snprintf(cur, sizeof(buf) - (cur - buf), "] ");
301 
302 	return buf;
303 }
304 
print_sta_handler(struct nl_msg * msg,void * arg)305 static int print_sta_handler(struct nl_msg *msg, void *arg)
306 {
307 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
308 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
309 	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
310 	char mac_addr[20], state_name[10], dev[20];
311 	struct nl80211_sta_flag_update *sta_flags;
312 	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
313 		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
314 		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
315 		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
316 		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
317 		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
318 		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
319 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
320 		[NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64},
321 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
322 		[NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
323 		[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
324 		[NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
325 		[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
326 		[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
327 		[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
328 		[NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
329 		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
330 		[NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32},
331 		[NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64},
332 		[NL80211_STA_INFO_STA_FLAGS] =
333 			{ .minlen = sizeof(struct nl80211_sta_flag_update) },
334 		[NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32},
335 		[NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32},
336 		[NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32},
337 		[NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED },
338 		[NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED },
339 		[NL80211_STA_INFO_TID_STATS] = { .type = NLA_NESTED },
340 		[NL80211_STA_INFO_BSS_PARAM] = { .type = NLA_NESTED },
341 		[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
342 		[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
343 		[NL80211_STA_INFO_ACK_SIGNAL] = {.type = NLA_U8 },
344 		[NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_U8 },
345 		[NL80211_STA_INFO_AIRTIME_LINK_METRIC] = { .type = NLA_U32 },
346 		[NL80211_STA_INFO_CONNECTED_TO_AS] = { .type = NLA_U8 },
347 		[NL80211_STA_INFO_CONNECTED_TO_GATE] = { .type = NLA_U8 },
348 	};
349 	char *chain;
350 	struct timeval now;
351 	unsigned long long now_ms;
352 
353 	gettimeofday(&now, NULL);
354 	now_ms = now.tv_sec * 1000ULL;
355 	now_ms += (now.tv_usec / 1000);
356 
357 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
358 		  genlmsg_attrlen(gnlh, 0), NULL);
359 
360 	/*
361 	 * TODO: validate the interface and mac address!
362 	 * Otherwise, there's a race condition as soon as
363 	 * the kernel starts sending station notifications.
364 	 */
365 
366 	if (!tb[NL80211_ATTR_STA_INFO]) {
367 		fprintf(stderr, "sta stats missing!\n");
368 		return NL_SKIP;
369 	}
370 	if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
371 			     tb[NL80211_ATTR_STA_INFO],
372 			     stats_policy)) {
373 		fprintf(stderr, "failed to parse nested attributes!\n");
374 		return NL_SKIP;
375 	}
376 
377 	mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
378 	if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
379 	printf("Station %s (on %s)", mac_addr, dev);
380 
381 	if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
382 		printf("\n\tinactive time:\t%u ms",
383 			nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
384 	if (sinfo[NL80211_STA_INFO_RX_BYTES64])
385 		printf("\n\trx bytes:\t%llu",
386 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
387 	else if (sinfo[NL80211_STA_INFO_RX_BYTES])
388 		printf("\n\trx bytes:\t%u",
389 		       nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
390 	if (sinfo[NL80211_STA_INFO_RX_PACKETS])
391 		printf("\n\trx packets:\t%u",
392 			nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
393 	if (sinfo[NL80211_STA_INFO_TX_BYTES64])
394 		printf("\n\ttx bytes:\t%llu",
395 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
396 	else if (sinfo[NL80211_STA_INFO_TX_BYTES])
397 		printf("\n\ttx bytes:\t%u",
398 		       nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
399 	if (sinfo[NL80211_STA_INFO_TX_PACKETS])
400 		printf("\n\ttx packets:\t%u",
401 			nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
402 	if (sinfo[NL80211_STA_INFO_TX_RETRIES])
403 		printf("\n\ttx retries:\t%u",
404 			nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
405 	if (sinfo[NL80211_STA_INFO_TX_FAILED])
406 		printf("\n\ttx failed:\t%u",
407 			nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
408 	if (sinfo[NL80211_STA_INFO_BEACON_LOSS])
409 		printf("\n\tbeacon loss:\t%u",
410 		       nla_get_u32(sinfo[NL80211_STA_INFO_BEACON_LOSS]));
411 	if (sinfo[NL80211_STA_INFO_BEACON_RX])
412 		printf("\n\tbeacon rx:\t%llu",
413 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_BEACON_RX]));
414 	if (sinfo[NL80211_STA_INFO_RX_DROP_MISC])
415 		printf("\n\trx drop misc:\t%llu",
416 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC]));
417 
418 	chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
419 	if (sinfo[NL80211_STA_INFO_SIGNAL])
420 		printf("\n\tsignal:  \t%d %sdBm",
421 			(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
422 			chain);
423 
424 	chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
425 	if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
426 		printf("\n\tsignal avg:\t%d %sdBm",
427 			(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
428 			chain);
429 
430 	if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
431 		printf("\n\tbeacon signal avg:\t%d dBm",
432 		       (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]));
433 	if (sinfo[NL80211_STA_INFO_T_OFFSET])
434 		printf("\n\tToffset:\t%llu us",
435 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]));
436 
437 	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
438 		char buf[100];
439 
440 		parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
441 		printf("\n\ttx bitrate:\t%s", buf);
442 	}
443 
444 	if (sinfo[NL80211_STA_INFO_TX_DURATION])
445 		printf("\n\ttx duration:\t%lld us",
446 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_DURATION]));
447 
448 	if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
449 		char buf[100];
450 
451 		parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
452 		printf("\n\trx bitrate:\t%s", buf);
453 	}
454 
455 	if (sinfo[NL80211_STA_INFO_RX_DURATION])
456 		printf("\n\trx duration:\t%lld us",
457 		       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION]));
458 
459 	if (sinfo[NL80211_STA_INFO_ACK_SIGNAL])
460 		printf("\n\tlast ack signal:%d dBm",
461 			(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL]));
462 
463 	if (sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG])
464 		printf("\n\tavg ack signal:\t%d dBm",
465 			(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_ACK_SIGNAL_AVG]));
466 
467 	if (sinfo[NL80211_STA_INFO_AIRTIME_WEIGHT]) {
468 		printf("\n\tairtime weight: %d", nla_get_u16(sinfo[NL80211_STA_INFO_AIRTIME_WEIGHT]));
469 	}
470 
471 	if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
472 		uint32_t thr;
473 
474 		thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
475 		/* convert in Mbps but scale by 1000 to save kbps units */
476 		thr = thr * 1000 / 1024;
477 
478 		printf("\n\texpected throughput:\t%u.%uMbps",
479 		       thr / 1000, thr % 1000);
480 	}
481 
482 	if (sinfo[NL80211_STA_INFO_LLID])
483 		printf("\n\tmesh llid:\t%d",
484 			nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
485 	if (sinfo[NL80211_STA_INFO_PLID])
486 		printf("\n\tmesh plid:\t%d",
487 			nla_get_u16(sinfo[NL80211_STA_INFO_PLID]));
488 	if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
489 		switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) {
490 		case LISTEN:
491 			strcpy(state_name, "LISTEN");
492 			break;
493 		case OPN_SNT:
494 			strcpy(state_name, "OPN_SNT");
495 			break;
496 		case OPN_RCVD:
497 			strcpy(state_name, "OPN_RCVD");
498 			break;
499 		case CNF_RCVD:
500 			strcpy(state_name, "CNF_RCVD");
501 			break;
502 		case ESTAB:
503 			strcpy(state_name, "ESTAB");
504 			break;
505 		case HOLDING:
506 			strcpy(state_name, "HOLDING");
507 			break;
508 		case BLOCKED:
509 			strcpy(state_name, "BLOCKED");
510 			break;
511 		default:
512 			strcpy(state_name, "UNKNOWN");
513 			break;
514 		}
515 		printf("\n\tmesh plink:\t%s", state_name);
516 	}
517 	if (sinfo[NL80211_STA_INFO_AIRTIME_LINK_METRIC])
518 		printf("\n\tmesh airtime link metric: %d",
519 			nla_get_u32(sinfo[NL80211_STA_INFO_AIRTIME_LINK_METRIC]));
520 	if (sinfo[NL80211_STA_INFO_CONNECTED_TO_GATE])
521 		printf("\n\tmesh connected to gate:\t%s",
522 			nla_get_u8(sinfo[NL80211_STA_INFO_CONNECTED_TO_GATE]) ?
523 			"yes" : "no");
524 	if (sinfo[NL80211_STA_INFO_CONNECTED_TO_AS])
525 		printf("\n\tmesh connected to auth server:\t%s",
526 			nla_get_u8(sinfo[NL80211_STA_INFO_CONNECTED_TO_AS]) ?
527 			"yes" : "no");
528 
529 	if (sinfo[NL80211_STA_INFO_LOCAL_PM]) {
530 		printf("\n\tmesh local PS mode:\t");
531 		print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]);
532 	}
533 	if (sinfo[NL80211_STA_INFO_PEER_PM]) {
534 		printf("\n\tmesh peer PS mode:\t");
535 		print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]);
536 	}
537 	if (sinfo[NL80211_STA_INFO_NONPEER_PM]) {
538 		printf("\n\tmesh non-peer PS mode:\t");
539 		print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]);
540 	}
541 
542 	if (sinfo[NL80211_STA_INFO_STA_FLAGS]) {
543 		sta_flags = (struct nl80211_sta_flag_update *)
544 			    nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
545 
546 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
547 			printf("\n\tauthorized:\t");
548 			if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED))
549 				printf("yes");
550 			else
551 				printf("no");
552 		}
553 
554 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
555 			printf("\n\tauthenticated:\t");
556 			if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
557 				printf("yes");
558 			else
559 				printf("no");
560 		}
561 
562 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
563 			printf("\n\tassociated:\t");
564 			if (sta_flags->set & BIT(NL80211_STA_FLAG_ASSOCIATED))
565 				printf("yes");
566 			else
567 				printf("no");
568 		}
569 
570 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
571 			printf("\n\tpreamble:\t");
572 			if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
573 				printf("short");
574 			else
575 				printf("long");
576 		}
577 
578 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) {
579 			printf("\n\tWMM/WME:\t");
580 			if (sta_flags->set & BIT(NL80211_STA_FLAG_WME))
581 				printf("yes");
582 			else
583 				printf("no");
584 		}
585 
586 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) {
587 			printf("\n\tMFP:\t\t");
588 			if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP))
589 				printf("yes");
590 			else
591 				printf("no");
592 		}
593 
594 		if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
595 			printf("\n\tTDLS peer:\t");
596 			if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER))
597 				printf("yes");
598 			else
599 				printf("no");
600 		}
601 	}
602 
603 	if (sinfo[NL80211_STA_INFO_TID_STATS] && arg != NULL &&
604 	    !strcmp((char *)arg, "-v"))
605 		parse_tid_stats(sinfo[NL80211_STA_INFO_TID_STATS]);
606 	if (sinfo[NL80211_STA_INFO_BSS_PARAM])
607 		parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]);
608 	if (sinfo[NL80211_STA_INFO_CONNECTED_TIME])
609 		printf("\n\tconnected time:\t%u seconds",
610 			nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]));
611 	if (sinfo[NL80211_STA_INFO_ASSOC_AT_BOOTTIME]) {
612 		unsigned long long bt;
613 		struct timespec now_ts;
614 		unsigned long long boot_ns;
615 		unsigned long long assoc_at_ms;
616 
617 		clock_gettime(CLOCK_BOOTTIME, &now_ts);
618 		boot_ns = now_ts.tv_sec * 1000000000ULL;
619 		boot_ns += now_ts.tv_nsec;
620 
621 		bt = (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_ASSOC_AT_BOOTTIME]);
622 		printf("\n\tassociated at [boottime]:\t%llu.%.3llus",
623 		       bt/1000000000, (bt%1000000000)/1000000);
624 		assoc_at_ms = now_ms - ((boot_ns - bt) / 1000000);
625 		printf("\n\tassociated at:\t%llu ms", assoc_at_ms);
626 	}
627 
628 	printf("\n\tcurrent time:\t%llu ms\n", now_ms);
629 	return NL_SKIP;
630 }
631 
handle_station_get(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)632 static int handle_station_get(struct nl80211_state *state,
633 			      struct nl_msg *msg,
634 			      int argc, char **argv,
635 			      enum id_input id)
636 {
637 	unsigned char mac_addr[ETH_ALEN];
638 
639 	if (argc < 1)
640 		return 1;
641 
642 	if (mac_addr_a2n(mac_addr, argv[0])) {
643 		fprintf(stderr, "invalid mac address\n");
644 		return 2;
645 	}
646 
647 	argc--;
648 	argv++;
649 
650 	if (argc)
651 		return 1;
652 
653 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
654 
655 	register_handler(print_sta_handler, NULL);
656 
657 	return 0;
658  nla_put_failure:
659 	return -ENOBUFS;
660 }
661 COMMAND(station, get, "<MAC address>",
662 	NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get,
663 	"Get information for a specific station.");
664 
handle_station_del(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)665 static int handle_station_del(struct nl80211_state *state,
666 			      struct nl_msg *msg,
667 			      int argc, char **argv,
668 			      enum id_input id)
669 {
670 	char *end;
671 	unsigned char mac_addr[ETH_ALEN];
672 	int subtype;
673 	int reason_code;
674 
675 	if (argc < 1)
676 		return 1;
677 
678 	if (mac_addr_a2n(mac_addr, argv[0])) {
679 		fprintf(stderr, "invalid mac address\n");
680 		return 2;
681 	}
682 
683 	argc--;
684 	argv++;
685 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
686 
687 	if (argc > 1 && strcmp(argv[0], "subtype") == 0) {
688 		argv++;
689 		argc--;
690 
691 		subtype = strtod(argv[0], &end);
692 		if (*end != '\0')
693 			return 1;
694 
695 		NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, subtype);
696 		argv++;
697 		argc--;
698 	}
699 
700 	if (argc > 1 && strcmp(argv[0], "reason-code") == 0) {
701 		argv++;
702 		argc--;
703 
704 		reason_code = strtod(argv[0], &end);
705 		if (*end != '\0')
706 			return 1;
707 
708 		NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
709 		argv++;
710 		argc--;
711 	}
712 
713 	if (argc)
714 		return 1;
715 
716 	register_handler(print_sta_handler, NULL);
717 
718 	return 0;
719  nla_put_failure:
720 	return -ENOBUFS;
721 }
722 COMMAND(station, del, "<MAC address> [subtype <subtype>] [reason-code <code>]",
723 	NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_del,
724 	"Remove the given station entry (use with caution!)\n"
725 	"Example subtype values: 0xA (disassociation), 0xC (deauthentication)");
726 
727 static const struct cmd *station_set_plink;
728 static const struct cmd *station_set_vlan;
729 static const struct cmd *station_set_mesh_power_mode;
730 static const struct cmd *station_set_airtime_weight;
731 static const struct cmd *station_set_txpwr;
732 
select_station_cmd(int argc,char ** argv)733 static const struct cmd *select_station_cmd(int argc, char **argv)
734 {
735 	if (argc < 2)
736 		return NULL;
737 	if (strcmp(argv[1], "plink_action") == 0)
738 		return station_set_plink;
739 	if (strcmp(argv[1], "vlan") == 0)
740 		return station_set_vlan;
741 	if (strcmp(argv[1], "mesh_power_mode") == 0)
742 		return station_set_mesh_power_mode;
743 	if (strcmp(argv[1], "airtime_weight") == 0)
744 		return station_set_airtime_weight;
745 	if (strcmp(argv[1], "txpwr") == 0)
746 		return station_set_txpwr;
747 	return NULL;
748 }
749 
handle_station_set_plink(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)750 static int handle_station_set_plink(struct nl80211_state *state,
751 			      struct nl_msg *msg,
752 			      int argc, char **argv,
753 			      enum id_input id)
754 {
755 	unsigned char plink_action;
756 	unsigned char mac_addr[ETH_ALEN];
757 
758 	if (argc < 3)
759 		return 1;
760 
761 	if (mac_addr_a2n(mac_addr, argv[0])) {
762 		fprintf(stderr, "invalid mac address\n");
763 		return 2;
764 	}
765 	argc--;
766 	argv++;
767 
768 	if (strcmp("plink_action", argv[0]) != 0)
769 		return 1;
770 	argc--;
771 	argv++;
772 
773 	if (strcmp("open", argv[0]) == 0)
774 		plink_action = NL80211_PLINK_ACTION_OPEN;
775 	else if (strcmp("block", argv[0]) == 0)
776 		plink_action = NL80211_PLINK_ACTION_BLOCK;
777 	else {
778 		fprintf(stderr, "plink action not supported\n");
779 		return 2;
780 	}
781 	argc--;
782 	argv++;
783 
784 	if (argc)
785 		return 1;
786 
787 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
788 	NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
789 
790 	return 0;
791  nla_put_failure:
792 	return -ENOBUFS;
793 }
794 COMMAND_ALIAS(station, set, "<MAC address> plink_action <open|block>",
795 	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink,
796 	"Set mesh peer link action for this station (peer).",
797 	select_station_cmd, station_set_plink);
798 
handle_station_set_vlan(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)799 static int handle_station_set_vlan(struct nl80211_state *state,
800 				   struct nl_msg *msg,
801 				   int argc, char **argv,
802 				   enum id_input id)
803 {
804 	unsigned char mac_addr[ETH_ALEN];
805 	unsigned long sta_vlan = 0;
806 	char *err = NULL;
807 
808 	if (argc < 3)
809 		return 1;
810 
811 	if (mac_addr_a2n(mac_addr, argv[0])) {
812 		fprintf(stderr, "invalid mac address\n");
813 		return 2;
814 	}
815 	argc--;
816 	argv++;
817 
818 	if (strcmp("vlan", argv[0]) != 0)
819 		return 1;
820 	argc--;
821 	argv++;
822 
823 	sta_vlan = strtoul(argv[0], &err, 0);
824 	if (err && *err) {
825 		fprintf(stderr, "invalid vlan id\n");
826 		return 2;
827 	}
828 	argc--;
829 	argv++;
830 
831 	if (argc)
832 		return 1;
833 
834 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
835 	NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan);
836 
837 	return 0;
838  nla_put_failure:
839 	return -ENOBUFS;
840 }
841 COMMAND_ALIAS(station, set, "<MAC address> vlan <ifindex>",
842 	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan,
843 	"Set an AP VLAN for this station.",
844 	select_station_cmd, station_set_vlan);
845 
handle_station_set_mesh_power_mode(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)846 static int handle_station_set_mesh_power_mode(struct nl80211_state *state,
847 					      struct nl_msg *msg,
848 					      int argc, char **argv,
849 					      enum id_input id)
850 {
851 	unsigned char mesh_power_mode;
852 	unsigned char mac_addr[ETH_ALEN];
853 
854 	if (argc < 3)
855 		return 1;
856 
857 	if (mac_addr_a2n(mac_addr, argv[0])) {
858 		fprintf(stderr, "invalid mac address\n");
859 		return 2;
860 	}
861 	argc--;
862 	argv++;
863 
864 	if (strcmp("mesh_power_mode", argv[0]) != 0)
865 		return 1;
866 	argc--;
867 	argv++;
868 
869 	if (strcmp("active", argv[0]) == 0)
870 		mesh_power_mode = NL80211_MESH_POWER_ACTIVE;
871 	else if (strcmp("light", argv[0]) == 0)
872 		mesh_power_mode = NL80211_MESH_POWER_LIGHT_SLEEP;
873 	else if (strcmp("deep", argv[0]) == 0)
874 		mesh_power_mode = NL80211_MESH_POWER_DEEP_SLEEP;
875 	else {
876 		fprintf(stderr, "unknown mesh power mode\n");
877 		return 2;
878 	}
879 	argc--;
880 	argv++;
881 
882 	if (argc)
883 		return 1;
884 
885 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
886 	NLA_PUT_U32(msg, NL80211_ATTR_LOCAL_MESH_POWER_MODE, mesh_power_mode);
887 
888 	return 0;
889 nla_put_failure:
890 	return -ENOBUFS;
891 }
892 COMMAND_ALIAS(station, set, "<MAC address> mesh_power_mode "
893 	"<active|light|deep>", NL80211_CMD_SET_STATION, 0, CIB_NETDEV,
894 	handle_station_set_mesh_power_mode,
895 	"Set link-specific mesh power mode for this station",
896 	select_station_cmd, station_set_mesh_power_mode);
897 
handle_station_set_airtime_weight(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)898 static int handle_station_set_airtime_weight(struct nl80211_state *state,
899 					     struct nl_msg *msg,
900 					     int argc, char **argv,
901 					     enum id_input id)
902 {
903 	unsigned char mac_addr[ETH_ALEN];
904 	unsigned long airtime_weight = 0;
905 	char *err = NULL;
906 
907 	if (argc < 3)
908 		return 1;
909 
910 	if (mac_addr_a2n(mac_addr, argv[0])) {
911 		fprintf(stderr, "invalid mac address\n");
912 		return 2;
913 	}
914 	argc--;
915 	argv++;
916 
917 	if (strcmp("airtime_weight", argv[0]) != 0)
918 		return 1;
919 	argc--;
920 	argv++;
921 
922 	airtime_weight = strtoul(argv[0], &err, 0);
923 	if (err && *err) {
924 		fprintf(stderr, "invalid airtime weight\n");
925 		return 2;
926 	}
927 	argc--;
928 	argv++;
929 
930 	if (argc)
931 		return 1;
932 
933 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
934 	NLA_PUT_U16(msg, NL80211_ATTR_AIRTIME_WEIGHT, airtime_weight);
935 
936 	return 0;
937  nla_put_failure:
938 	return -ENOBUFS;
939 
940 }
941 COMMAND_ALIAS(station, set, "<MAC address> airtime_weight <weight>",
942 	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_airtime_weight,
943 	"Set airtime weight for this station.",
944 	select_station_cmd, station_set_airtime_weight);
945 
handle_station_set_txpwr(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)946 static int handle_station_set_txpwr(struct nl80211_state *state,
947 				    struct nl_msg *msg,
948 				    int argc, char **argv,
949 				    enum id_input id)
950 {
951 	enum nl80211_tx_power_setting type;
952 	unsigned char mac_addr[ETH_ALEN];
953 	int sta_txpwr = 0;
954 	char *err = NULL;
955 
956 	if (argc != 3 && argc != 4)
957 		return 1;
958 
959 	if (mac_addr_a2n(mac_addr, argv[0])) {
960 		fprintf(stderr, "invalid mac address\n");
961 		return 2;
962 	}
963 
964 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
965 	argc--;
966 	argv++;
967 
968 	if (strcmp("txpwr", argv[0]) != 0)
969 		return 1;
970 	argc--;
971 	argv++;
972 
973 	if (!strcmp(argv[0], "auto"))
974 		type = NL80211_TX_POWER_AUTOMATIC;
975 	else if (!strcmp(argv[0], "limit"))
976 		type = NL80211_TX_POWER_LIMITED;
977 	else {
978 		printf("Invalid parameter: %s\n", argv[0]);
979 		return 2;
980 	}
981 
982 	NLA_PUT_U8(msg, NL80211_ATTR_STA_TX_POWER_SETTING, type);
983 
984 	if (type != NL80211_TX_POWER_AUTOMATIC) {
985 		if (argc != 2) {
986 			printf("Missing TX power level argument.\n");
987 			return 2;
988 		}
989 
990 		argc--;
991 		argv++;
992 
993 		sta_txpwr = strtoul(argv[0], &err, 0);
994 		NLA_PUT_U16(msg, NL80211_ATTR_STA_TX_POWER, sta_txpwr);
995 	}
996 
997 	argc--;
998 	argv++;
999 
1000 	if (argc)
1001 		return 1;
1002 
1003 	return 0;
1004  nla_put_failure:
1005 	return -ENOBUFS;
1006 }
1007 COMMAND_ALIAS(station, set, "<MAC address> txpwr <auto|limit> [<tx power dBm>]",
1008 	NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_txpwr,
1009 	"Set Tx power for this station.",
1010 	select_station_cmd, station_set_txpwr);
1011 
handle_station_dump(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)1012 static int handle_station_dump(struct nl80211_state *state,
1013 			       struct nl_msg *msg,
1014 			       int argc, char **argv,
1015 			       enum id_input id)
1016 {
1017 	register_handler(print_sta_handler, *argv);
1018 	return 0;
1019 }
1020 COMMAND(station, dump, "[-v]",
1021 	NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump,
1022 	"List all stations known, e.g. the AP on managed interfaces");
1023