]> git.ipfire.org Git - thirdparty/iw.git/blame - scan.c
iw: scan: parse AP Channel Report
[thirdparty/iw.git] / scan.c
CommitLineData
3563f4c5
JB
1#include <net/if.h>
2#include <errno.h>
3#include <string.h>
764fe753 4#include <stdbool.h>
3563f4c5
JB
5
6#include <netlink/genl/genl.h>
7#include <netlink/genl/family.h>
8#include <netlink/genl/ctrl.h>
9#include <netlink/msg.h>
10#include <netlink/attr.h>
11
12#include "nl80211.h"
13#include "iw.h"
14
92a04ecd
MH
15#define WLAN_CAPABILITY_ESS (1<<0)
16#define WLAN_CAPABILITY_IBSS (1<<1)
17#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
18#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
19#define WLAN_CAPABILITY_PRIVACY (1<<4)
20#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
21#define WLAN_CAPABILITY_PBCC (1<<6)
22#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
23#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
24#define WLAN_CAPABILITY_QOS (1<<9)
25#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
26#define WLAN_CAPABILITY_APSD (1<<11)
2e8b82c1 27#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12)
92a04ecd 28#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
2e8b82c1
VK
29#define WLAN_CAPABILITY_DEL_BACK (1<<14)
30#define WLAN_CAPABILITY_IMM_BACK (1<<15)
31/* DMG (60gHz) 802.11ad */
32/* type - bits 0..1 */
33#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0)
34
35#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */
36#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */
37#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */
38
39#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2)
40#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
41#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4)
42#define WLAN_CAPABILITY_DMG_ECPAC (1<<5)
43
44#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8)
45#define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12)
92a04ecd 46
3bd60ef1
JB
47static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
48static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
9a22374a 49static unsigned char wfa_oui[3] = { 0x50, 0x6f, 0x9a };
857d966e 50
764fe753
JB
51struct scan_params {
52 bool unknown;
febeb0c0 53 enum print_ie_type type;
575280cc 54 bool show_both_ie_sets;
764fe753
JB
55};
56
2b690f0a
LR
57#define IEEE80211_COUNTRY_EXTENSION_ID 201
58
72725719
JB
59union ieee80211_country_ie_triplet {
60 struct {
61 __u8 first_channel;
62 __u8 num_channels;
63 __s8 max_power;
64 } __attribute__ ((packed)) chans;
65 struct {
66 __u8 reg_extension_id;
67 __u8 reg_class;
68 __u8 coverage_class;
69 } __attribute__ ((packed)) ext;
2b690f0a
LR
70} __attribute__ ((packed));
71
b1622287
LC
72int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv)
73{
302a378a 74 struct nl_msg *matchset = NULL, *freqs = NULL, *ssids = NULL;
9ae0d103
AS
75 struct nl_msg *scan_plans = NULL;
76 struct nlattr *match = NULL, *plan = NULL;
b1622287
LC
77 enum {
78 ND_TOPLEVEL,
79 ND_MATCH,
80 ND_FREQS,
302a378a 81 ND_ACTIVE,
9ae0d103 82 ND_PLANS,
b1622287
LC
83 } parse_state = ND_TOPLEVEL;
84 int c = *argc;
85 char *end, **v = *argv;
86 int err = 0, i = 0;
9ae0d103 87 unsigned int freq, interval = 0, delay = 0, iterations = 0;
302a378a 88 bool have_matchset = false, have_freqs = false, have_ssids = false;
9ae0d103 89 bool have_active = false, have_passive = false, have_plans = false;
5130bba0 90 uint32_t flags = 0;
b1622287
LC
91
92 matchset = nlmsg_alloc();
93 if (!matchset) {
94 err = -ENOBUFS;
95 goto out;
96 }
97
98 freqs = nlmsg_alloc();
99 if (!freqs) {
100 err = -ENOBUFS;
101 goto out;
102 }
103
302a378a
LC
104 ssids = nlmsg_alloc();
105 if (!ssids) {
106 err = -ENOMEM;
107 goto out;
108 }
109
9ae0d103
AS
110 scan_plans = nlmsg_alloc();
111 if (!scan_plans) {
112 err = -ENOBUFS;
113 goto out;
114 }
115
b1622287
LC
116 while (c) {
117 switch (parse_state) {
118 case ND_TOPLEVEL:
119 if (!strcmp(v[0], "interval")) {
120 c--; v++;
121 if (c == 0) {
122 err = -EINVAL;
123 goto nla_put_failure;
124 }
125
9ae0d103 126 if (interval || have_plans) {
b1622287
LC
127 err = -EINVAL;
128 goto nla_put_failure;
129 }
130 interval = strtoul(v[0], &end, 10);
131 if (*end || !interval) {
132 err = -EINVAL;
133 goto nla_put_failure;
134 }
135 NLA_PUT_U32(msg,
136 NL80211_ATTR_SCHED_SCAN_INTERVAL,
137 interval);
9ae0d103
AS
138 } else if (!strcmp(v[0], "scan_plans")) {
139 parse_state = ND_PLANS;
140 if (have_plans || interval) {
141 err = -EINVAL;
142 goto nla_put_failure;
143 }
144
145 have_plans = true;
146 i = 0;
b1622287
LC
147 } else if (!strcmp(v[0], "delay")) {
148 c--; v++;
149 if (c == 0) {
150 err = -EINVAL;
151 goto nla_put_failure;
152 }
153
154 if (delay) {
155 err = -EINVAL;
156 goto nla_put_failure;
157 }
158 delay = strtoul(v[0], &end, 10);
159 if (*end) {
160 err = -EINVAL;
161 goto nla_put_failure;
162 }
163 NLA_PUT_U32(msg,
164 NL80211_ATTR_SCHED_SCAN_DELAY,
165 delay);
166 } else if (!strcmp(v[0], "matches")) {
167 parse_state = ND_MATCH;
168 if (have_matchset) {
169 err = -EINVAL;
170 goto nla_put_failure;
171 }
172
173 i = 0;
174 } else if (!strcmp(v[0], "freqs")) {
175 parse_state = ND_FREQS;
176 if (have_freqs) {
177 err = -EINVAL;
178 goto nla_put_failure;
179 }
180
181 have_freqs = true;
182 i = 0;
302a378a
LC
183 } else if (!strcmp(v[0], "active")) {
184 parse_state = ND_ACTIVE;
185 if (have_active || have_passive) {
186 err = -EINVAL;
187 goto nla_put_failure;
188 }
189
190 have_active = true;
191 i = 0;
192 } else if (!strcmp(v[0], "passive")) {
193 if (have_active || have_passive) {
194 err = -EINVAL;
195 goto nla_put_failure;
196 }
197
198 have_passive = true;
5130bba0
LC
199 } else if (!strncmp(v[0], "randomise", 9) ||
200 !strncmp(v[0], "randomize", 9)) {
201 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
3c0117c1
JB
202 err = parse_random_mac_addr(msg, v[0] + 9);
203 if (err)
204 goto nla_put_failure;
b1622287
LC
205 } else {
206 /* this element is not for us, so
207 * return to continue parsing.
208 */
209 goto nla_put_failure;
210 }
211 c--; v++;
212
213 break;
214 case ND_MATCH:
215 if (!strcmp(v[0], "ssid")) {
216 c--; v++;
217 if (c == 0) {
218 err = -EINVAL;
219 goto nla_put_failure;
220 }
221
222 /* TODO: for now we can only have an
223 * SSID in the match, so we can start
224 * the match nest here.
225 */
226 match = nla_nest_start(matchset, i);
227 if (!match) {
228 err = -ENOBUFS;
229 goto nla_put_failure;
230 }
231
232 NLA_PUT(matchset,
233 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
234 strlen(v[0]), v[0]);
235 nla_nest_end(matchset, match);
236 match = NULL;
237
238 have_matchset = true;
239 i++;
240 c--; v++;
241 } else {
242 /* other element that cannot be part
243 * of a match indicates the end of the
244 * match. */
245 /* need at least one match in the matchset */
246 if (i == 0) {
247 err = -EINVAL;
248 goto nla_put_failure;
249 }
250
251 parse_state = ND_TOPLEVEL;
252 }
253
254 break;
255 case ND_FREQS:
256 freq = strtoul(v[0], &end, 10);
257 if (*end) {
258 if (i == 0) {
259 err = -EINVAL;
260 goto nla_put_failure;
261 }
262
263 parse_state = ND_TOPLEVEL;
264 } else {
265 NLA_PUT_U32(freqs, i, freq);
266 i++;
267 c--; v++;
268 }
269 break;
302a378a
LC
270 case ND_ACTIVE:
271 if (!strcmp(v[0], "ssid")) {
272 c--; v++;
273 if (c == 0) {
274 err = -EINVAL;
275 goto nla_put_failure;
276 }
277
278 NLA_PUT(ssids,
279 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
280 strlen(v[0]), v[0]);
281
282 have_ssids = true;
283 i++;
284 c--; v++;
285 } else {
286 /* other element that cannot be part
287 * of a match indicates the end of the
288 * active set. */
289 /* need at least one item in the set */
290 if (i == 0) {
291 err = -EINVAL;
292 goto nla_put_failure;
293 }
294
295 parse_state = ND_TOPLEVEL;
296 }
297 break;
9ae0d103
AS
298 case ND_PLANS:
299 iterations = 0;
300 interval = strtoul(v[0], &end, 10);
301 if (*end) {
302 char *iter;
303
304 if (*end != ':') {
305 err = -EINVAL;
306 goto nla_put_failure;
307 }
308
309 iter = ++end;
310 iterations = strtoul(iter, &end, 10);
311 if (*end || !iterations) {
312 err = -EINVAL;
313 goto nla_put_failure;
314 }
315 }
316
317 plan = nla_nest_start(scan_plans, i + 1);
318 if (!plan) {
319 err = -ENOBUFS;
320 goto nla_put_failure;
321 }
322
323 NLA_PUT_U32(scan_plans,
324 NL80211_SCHED_SCAN_PLAN_INTERVAL,
325 interval);
326
327 if (iterations)
328 NLA_PUT_U32(scan_plans,
329 NL80211_SCHED_SCAN_PLAN_ITERATIONS,
330 iterations);
331 else
332 parse_state = ND_TOPLEVEL;
333
334 nla_nest_end(scan_plans, plan);
335 plan = NULL;
336 i++;
337 c--; v++;
338 break;
b1622287
LC
339 }
340 }
341
302a378a
LC
342 if (!have_ssids)
343 NLA_PUT(ssids, 1, 0, "");
344 if (!have_passive)
345 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
b1622287
LC
346 if (have_freqs)
347 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
348 if (have_matchset)
349 nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
9ae0d103
AS
350 if (have_plans)
351 nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_PLANS, scan_plans);
5130bba0
LC
352 if (flags)
353 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
b1622287
LC
354
355nla_put_failure:
356 if (match)
357 nla_nest_end(msg, match);
edb5f4f0 358out:
b1622287
LC
359 nlmsg_free(freqs);
360 nlmsg_free(matchset);
9ae0d103 361 nlmsg_free(scan_plans);
edb5f4f0 362 nlmsg_free(ssids);
b1622287 363
b1622287
LC
364 *argc = c;
365 *argv = v;
366 return err;
367}
368
7c37a24d 369static int handle_scan(struct nl80211_state *state,
3563f4c5 370 struct nl_msg *msg,
05514f95
JB
371 int argc, char **argv,
372 enum id_input id)
3563f4c5 373{
559a1713
JB
374 struct nl_msg *ssids = NULL, *freqs = NULL;
375 char *eptr;
3563f4c5 376 int err = -ENOBUFS;
559a1713
JB
377 int i;
378 enum {
379 NONE,
380 FREQ,
64797a7f 381 IES,
559a1713 382 SSID,
851d35c2 383 MESHID,
3a99ff6d 384 DURATION,
559a1713
JB
385 DONE,
386 } parse = NONE;
387 int freq;
3a99ff6d 388 unsigned int duration = 0;
559a1713 389 bool passive = false, have_ssids = false, have_freqs = false;
3a99ff6d 390 bool duration_mandatory = false;
851d35c2 391 size_t ies_len = 0, meshid_len = 0;
2f74c59c 392 unsigned char *ies = NULL, *meshid = NULL, *tmpies = NULL;
5085aa2b 393 unsigned int flags = 0;
3563f4c5
JB
394
395 ssids = nlmsg_alloc();
396 if (!ssids)
397 return -ENOMEM;
559a1713
JB
398
399 freqs = nlmsg_alloc();
400 if (!freqs) {
401 nlmsg_free(ssids);
402 return -ENOMEM;
403 }
404
405 for (i = 0; i < argc; i++) {
559a1713
JB
406 switch (parse) {
407 case NONE:
64797a7f
JB
408 if (strcmp(argv[i], "freq") == 0) {
409 parse = FREQ;
410 have_freqs = true;
411 break;
412 } else if (strcmp(argv[i], "ies") == 0) {
413 parse = IES;
414 break;
fe862239 415 } else if (strcmp(argv[i], "lowpri") == 0) {
fe862239
SL
416 flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
417 break;
418 } else if (strcmp(argv[i], "flush") == 0) {
fe862239
SL
419 flags |= NL80211_SCAN_FLAG_FLUSH;
420 break;
ced94d5f 421 } else if (strcmp(argv[i], "ap-force") == 0) {
ced94d5f
AQ
422 flags |= NL80211_SCAN_FLAG_AP;
423 break;
3a99ff6d
PKC
424 } else if (strcmp(argv[i], "duration-mandatory") == 0) {
425 duration_mandatory = true;
426 break;
5085aa2b
JB
427 } else if (strncmp(argv[i], "randomise", 9) == 0 ||
428 strncmp(argv[i], "randomize", 9) == 0) {
429 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
3c0117c1 430 err = parse_random_mac_addr(msg, argv[i] + 9);
5085aa2b
JB
431 if (err)
432 goto nla_put_failure;
433 break;
64797a7f
JB
434 } else if (strcmp(argv[i], "ssid") == 0) {
435 parse = SSID;
436 have_ssids = true;
437 break;
438 } else if (strcmp(argv[i], "passive") == 0) {
439 parse = DONE;
440 passive = true;
441 break;
851d35c2 442 } else if (strcmp(argv[i], "meshid") == 0) {
443 parse = MESHID;
444 break;
3a99ff6d
PKC
445 } else if (strcmp(argv[i], "duration") == 0) {
446 parse = DURATION;
447 break;
64797a7f 448 }
6ab936f0 449 /* fall through - this is an error */
559a1713 450 case DONE:
2f74c59c
JC
451 err = 1;
452 goto nla_put_failure;
559a1713
JB
453 case FREQ:
454 freq = strtoul(argv[i], &eptr, 10);
cc12e895
JB
455 if (eptr != argv[i] + strlen(argv[i])) {
456 /* failed to parse as number -- maybe a tag? */
457 i--;
458 parse = NONE;
459 continue;
460 }
559a1713 461 NLA_PUT_U32(freqs, i, freq);
64797a7f
JB
462 break;
463 case IES:
2f74c59c
JC
464 if (ies)
465 free(ies);
851d35c2 466 ies = parse_hex(argv[i], &ies_len);
64797a7f
JB
467 if (!ies)
468 goto nla_put_failure;
64797a7f 469 parse = NONE;
559a1713
JB
470 break;
471 case SSID:
472 NLA_PUT(ssids, i, strlen(argv[i]), argv[i]);
473 break;
851d35c2 474 case MESHID:
475 meshid_len = strlen(argv[i]);
476 meshid = (unsigned char *) malloc(meshid_len + 2);
477 if (!meshid)
478 goto nla_put_failure;
479 meshid[0] = 114; /* mesh element id */
480 meshid[1] = meshid_len;
481 memcpy(&meshid[2], argv[i], meshid_len);
482 meshid_len += 2;
483 parse = NONE;
484 break;
3a99ff6d
PKC
485 case DURATION:
486 duration = strtoul(argv[i], &eptr, 10);
487 parse = NONE;
488 break;
851d35c2 489 }
490 }
491
492 if (ies || meshid) {
493 tmpies = (unsigned char *) malloc(ies_len + meshid_len);
2f74c59c 494 if (!tmpies)
851d35c2 495 goto nla_put_failure;
2f74c59c 496 if (ies)
851d35c2 497 memcpy(tmpies, ies, ies_len);
2f74c59c 498 if (meshid)
851d35c2 499 memcpy(&tmpies[ies_len], meshid, meshid_len);
2f74c59c 500 if (nla_put(msg, NL80211_ATTR_IE, ies_len + meshid_len, tmpies) < 0)
ab65a092 501 goto nla_put_failure;
559a1713
JB
502 }
503
504 if (!have_ssids)
505 NLA_PUT(ssids, 1, 0, "");
506 if (!passive)
507 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
508
509 if (have_freqs)
510 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
fe862239
SL
511 if (flags)
512 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
3a99ff6d
PKC
513 if (duration)
514 NLA_PUT_U16(msg, NL80211_ATTR_MEASUREMENT_DURATION, duration);
515 if (duration_mandatory) {
516 if (duration) {
517 NLA_PUT_FLAG(msg,
518 NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY);
519 } else {
520 err = -EINVAL;
521 goto nla_put_failure;
522 }
523 }
3563f4c5
JB
524
525 err = 0;
526 nla_put_failure:
527 nlmsg_free(ssids);
559a1713 528 nlmsg_free(freqs);
2f74c59c
JC
529 if (meshid)
530 free(meshid);
531 if (ies)
532 free(ies);
533 if (tmpies)
534 free(tmpies);
3563f4c5
JB
535 return err;
536}
3563f4c5 537
857d966e
MH
538static void tab_on_first(bool *first)
539{
540 if (!*first)
541 printf("\t");
542 else
543 *first = false;
544}
545
5f310d83
SM
546struct print_ies_data {
547 unsigned char *ie;
548 int ielen;
549};
550
551static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data,
552 const struct print_ies_data *ie_buffer)
3563f4c5 553{
83b4934c 554 printf(" ");
748f8489 555 print_ssid_escaped(len, data);
3563f4c5
JB
556 printf("\n");
557}
558
ca159934 559#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
1fd19c39
CL
560#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
561
5f310d83
SM
562static void print_supprates(const uint8_t type, uint8_t len,
563 const uint8_t *data,
564 const struct print_ies_data *ie_buffer)
3563f4c5
JB
565{
566 int i;
567
83b4934c 568 printf(" ");
3563f4c5 569
83b4934c 570 for (i = 0; i < len; i++) {
3563f4c5 571 int r = data[i] & 0x7f;
1fd19c39 572
ca159934
JB
573 if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
574 printf("VHT");
575 else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
1fd19c39
CL
576 printf("HT");
577 else
578 printf("%d.%d", r/2, 5*(r&1));
579
580 printf("%s ", data[i] & 0x80 ? "*" : "");
3563f4c5
JB
581 }
582 printf("\n");
583}
584
4d72d1d2
MT
585static void print_rm_enabled_capabilities(const uint8_t type, uint8_t len,
586 const uint8_t *data,
587 const struct print_ies_data *ie_buffer)
588{
22145cf3
MT
589 __u64 capa = ((__u64) data[0]) |
590 ((__u64) data[1]) << 8 |
591 ((__u64) data[2]) << 16 |
592 ((__u64) data[3]) << 24 |
4d72d1d2
MT
593 ((__u64) data[4]) << 32;
594
595 printf("\n");
596 printf("\t\tCapabilities: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
597 data[0], data[1],
598 data[2], data[3],
599 data[4]);
600
601#define PRINT_RM_CAPA(_bit, _str) \
602 do { \
603 if (capa & BIT(_bit)) \
604 printf("\t\t\t" _str "\n"); \
605 } while (0)
606
607 PRINT_RM_CAPA(0, "Link Measurement");
608 PRINT_RM_CAPA(1, "Neighbor Report");
609 PRINT_RM_CAPA(2, "Parallel Measurements");
610 PRINT_RM_CAPA(3, "Repeated Measurements");
611 PRINT_RM_CAPA(4, "Beacon Passive Measurement");
a532a81b 612 PRINT_RM_CAPA(5, "Beacon Active Measurement");
4d72d1d2
MT
613 PRINT_RM_CAPA(6, "Beacon Table Measurement");
614 PRINT_RM_CAPA(7, "Beacon Measurement Reporting Conditions");
615 PRINT_RM_CAPA(8, "Frame Measurement");
616 PRINT_RM_CAPA(9, "Channel Load");
617 PRINT_RM_CAPA(10, "Noise Histogram Measurement");
618 PRINT_RM_CAPA(11, "Statistics Measurement");
619 PRINT_RM_CAPA(12, "LCI Measurement");
620 PRINT_RM_CAPA(13, "LCI Azimuth");
621 PRINT_RM_CAPA(14, "Transmit Stream/Category Measurement");
622 PRINT_RM_CAPA(15, "Triggered Transmit Stream/Category");
623 PRINT_RM_CAPA(16, "AP Channel Report");
624 PRINT_RM_CAPA(17, "RM MIB Capability");
625
626 PRINT_RM_CAPA(27, "Measurement Pilot Transmission Information");
627 PRINT_RM_CAPA(28, "Neighbor Report TSF Offset");
628 PRINT_RM_CAPA(29, "RCPI Measurement");
629 PRINT_RM_CAPA(30, "RSNI Measurement");
630 PRINT_RM_CAPA(31, "BSS Average Access Delay");
631 PRINT_RM_CAPA(32, "BSS Available Admission");
632 PRINT_RM_CAPA(33, "Antenna");
633 PRINT_RM_CAPA(34, "FTM Range Report");
634 PRINT_RM_CAPA(35, "Civic Location Measurement");
635
636 printf("\t\tNonoperating Channel Max Measurement Duration: %i\n", data[3] >> 5);
637 printf("\t\tMeasurement Pilot Capability: %i\n", data[4] & 7);
638}
639
5f310d83
SM
640static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data,
641 const struct print_ies_data *ie_buffer)
3563f4c5 642{
83b4934c 643 printf(" channel %d\n", data[0]);
3563f4c5
JB
644}
645
2b690f0a 646static const char *country_env_str(char environment)
b7e8fa37 647{
2b690f0a 648 switch (environment) {
b7e8fa37 649 case 'I':
2b690f0a 650 return "Indoor only";
b7e8fa37 651 case 'O':
2b690f0a 652 return "Outdoor only";
b6c0d634 653 case ' ':
2b690f0a 654 return "Indoor/Outdoor";
b6c0d634 655 default:
2b690f0a 656 return "bogus";
b7e8fa37 657 }
2b690f0a
LR
658}
659
5f310d83
SM
660static void print_country(const uint8_t type, uint8_t len, const uint8_t *data,
661 const struct print_ies_data *ie_buffer)
2b690f0a
LR
662{
663 printf(" %.*s", 2, data);
664
665 printf("\tEnvironment: %s\n", country_env_str(data[2]));
666
667 data += 3;
668 len -= 3;
669
670 if (len < 3) {
671 printf("\t\tNo country IE triplets present\n");
672 return;
673 }
674
675 while (len >= 3) {
676 int end_channel;
72725719 677 union ieee80211_country_ie_triplet *triplet = (void *) data;
2b690f0a
LR
678
679 if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
680 printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
681 triplet->ext.reg_extension_id,
682 triplet->ext.reg_class,
683 triplet->ext.coverage_class,
684 triplet->ext.coverage_class * 450);
685
686 data += 3;
687 len -= 3;
688 continue;
689 }
690
691 /* 2 GHz */
692 if (triplet->chans.first_channel <= 14)
693 end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
694 else
695 end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
696
a9859d30 697 printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
2b690f0a
LR
698
699 data += 3;
700 len -= 3;
701 }
702
703 return;
b7e8fa37
MH
704}
705
5f310d83
SM
706static void print_powerconstraint(const uint8_t type, uint8_t len,
707 const uint8_t *data,
708 const struct print_ies_data *ie_buffer)
d1563a1b
MH
709{
710 printf(" %d dB\n", data[0]);
711}
712
5f310d83
SM
713static void print_tpcreport(const uint8_t type, uint8_t len,
714 const uint8_t *data,
715 const struct print_ies_data *ie_buffer)
792172b0
JM
716{
717 printf(" TX power: %d dBm\n", data[0]);
718 /* printf(" Link Margin (%d dB) is reserved in Beacons\n", data[1]); */
719}
720
5f310d83
SM
721static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data,
722 const struct print_ies_data *ie_buffer)
fc4d1484
MH
723{
724 if (data[0] == 0x00)
83b4934c 725 printf(" <no flags>");
fc4d1484
MH
726 if (data[0] & 0x01)
727 printf(" NonERP_Present");
728 if (data[0] & 0x02)
729 printf(" Use_Protection");
730 if (data[0] & 0x04)
731 printf(" Barker_Preamble_Mode");
732 printf("\n");
733}
734
fe969ade
MT
735static void print_ap_channel_report(const uint8_t type, uint8_t len, const uint8_t *data,
736 const struct print_ies_data *ie_buffer)
737{
738 uint8_t oper_class = data[0];
739 int i;
740
741 printf("\n");
742 printf("\t\t * operating class: %d\n", oper_class);
743 printf("\t\t * channel(s):");
744 for (i = 1; i < len; ++i) {
745 printf(" %d", data[i]);
746 }
747 printf("\n");
748}
749
83b4934c 750static void print_cipher(const uint8_t *data)
857d966e 751{
3bd60ef1 752 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 753 switch (data[3]) {
510e0e2f 754 case 0:
857d966e
MH
755 printf("Use group cipher suite");
756 break;
510e0e2f 757 case 1:
857d966e
MH
758 printf("WEP-40");
759 break;
510e0e2f 760 case 2:
857d966e
MH
761 printf("TKIP");
762 break;
510e0e2f 763 case 4:
857d966e
MH
764 printf("CCMP");
765 break;
510e0e2f 766 case 5:
857d966e
MH
767 printf("WEP-104");
768 break;
769 default:
332769c6 770 printf("%.02x-%.02x-%.02x:%d",
5594fd23 771 data[0], data[1] ,data[2], data[3]);
857d966e
MH
772 break;
773 }
774 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
775 switch (data[3]) {
510e0e2f 776 case 0:
857d966e
MH
777 printf("Use group cipher suite");
778 break;
510e0e2f 779 case 1:
857d966e
MH
780 printf("WEP-40");
781 break;
510e0e2f 782 case 2:
857d966e
MH
783 printf("TKIP");
784 break;
510e0e2f 785 case 4:
857d966e
MH
786 printf("CCMP");
787 break;
510e0e2f 788 case 5:
857d966e
MH
789 printf("WEP-104");
790 break;
510e0e2f 791 case 6:
857d966e
MH
792 printf("AES-128-CMAC");
793 break;
f188ab67
BG
794 case 7:
795 printf("NO-GROUP");
796 break;
a8b3da9d
VK
797 case 8:
798 printf("GCMP");
799 break;
857d966e 800 default:
332769c6 801 printf("%.02x-%.02x-%.02x:%d",
5594fd23 802 data[0], data[1] ,data[2], data[3]);
857d966e
MH
803 break;
804 }
805 } else
332769c6 806 printf("%.02x-%.02x-%.02x:%d",
5594fd23 807 data[0], data[1] ,data[2], data[3]);
857d966e
MH
808}
809
83b4934c 810static void print_auth(const uint8_t *data)
857d966e 811{
3bd60ef1 812 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 813 switch (data[3]) {
510e0e2f 814 case 1:
857d966e
MH
815 printf("IEEE 802.1X");
816 break;
510e0e2f 817 case 2:
857d966e
MH
818 printf("PSK");
819 break;
820 default:
332769c6 821 printf("%.02x-%.02x-%.02x:%d",
5594fd23 822 data[0], data[1] ,data[2], data[3]);
857d966e
MH
823 break;
824 }
825 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
826 switch (data[3]) {
510e0e2f 827 case 1:
857d966e
MH
828 printf("IEEE 802.1X");
829 break;
510e0e2f 830 case 2:
857d966e
MH
831 printf("PSK");
832 break;
0fe1c415
JB
833 case 3:
834 printf("FT/IEEE 802.1X");
835 break;
836 case 4:
837 printf("FT/PSK");
838 break;
839 case 5:
840 printf("IEEE 802.1X/SHA-256");
841 break;
842 case 6:
843 printf("PSK/SHA-256");
844 break;
4361eef3
JM
845 case 7:
846 printf("TDLS/TPK");
847 break;
a58f492d
HM
848 case 8:
849 printf("SAE");
850 break;
851 case 9:
852 printf("FT/SAE");
853 break;
854 case 11:
855 printf("IEEE 802.1X/SUITE-B");
856 break;
857 case 12:
858 printf("IEEE 802.1X/SUITE-B-192");
859 break;
860 case 13:
861 printf("FT/IEEE 802.1X/SHA-384");
862 break;
863 case 14:
864 printf("FILS/SHA-256");
865 break;
866 case 15:
867 printf("FILS/SHA-384");
868 break;
869 case 16:
870 printf("FT/FILS/SHA-256");
871 break;
872 case 17:
873 printf("FT/FILS/SHA-384");
874 break;
875 case 18:
876 printf("OWE");
877 break;
857d966e 878 default:
332769c6 879 printf("%.02x-%.02x-%.02x:%d",
5594fd23 880 data[0], data[1] ,data[2], data[3]);
857d966e
MH
881 break;
882 }
f188ab67
BG
883 } else if (memcmp(data, wfa_oui, 3) == 0) {
884 switch (data[3]) {
885 case 1:
886 printf("OSEN");
887 break;
a58f492d
HM
888 case 2:
889 printf("DPP");
890 break;
f188ab67
BG
891 default:
892 printf("%.02x-%.02x-%.02x:%d",
893 data[0], data[1] ,data[2], data[3]);
894 break;
895 }
857d966e 896 } else
332769c6 897 printf("%.02x-%.02x-%.02x:%d",
5594fd23 898 data[0], data[1] ,data[2], data[3]);
857d966e
MH
899}
900
f188ab67
BG
901static void _print_rsn_ie(const char *defcipher, const char *defauth,
902 uint8_t len, const uint8_t *data, int is_osen)
857d966e
MH
903{
904 bool first = true;
f188ab67 905 __u16 count, capa;
857d966e
MH
906 int i;
907
f188ab67
BG
908 if (!is_osen) {
909 __u16 version;
910 version = data[0] + (data[1] << 8);
911 tab_on_first(&first);
912 printf("\t * Version: %d\n", version);
857d966e 913
f188ab67
BG
914 data += 2;
915 len -= 2;
916 }
857d966e
MH
917
918 if (len < 4) {
919 tab_on_first(&first);
920 printf("\t * Group cipher: %s\n", defcipher);
921 printf("\t * Pairwise ciphers: %s\n", defcipher);
922 return;
923 }
924
925 tab_on_first(&first);
926 printf("\t * Group cipher: ");
927 print_cipher(data);
928 printf("\n");
929
930 data += 4;
931 len -= 4;
932
933 if (len < 2) {
934 tab_on_first(&first);
935 printf("\t * Pairwise ciphers: %s\n", defcipher);
936 return;
937 }
938
939 count = data[0] | (data[1] << 8);
31d477fb
MH
940 if (2 + (count * 4) > len)
941 goto invalid;
942
857d966e
MH
943 tab_on_first(&first);
944 printf("\t * Pairwise ciphers:");
31d477fb 945 for (i = 0; i < count; i++) {
857d966e
MH
946 printf(" ");
947 print_cipher(data + 2 + (i * 4));
948 }
949 printf("\n");
950
951 data += 2 + (count * 4);
952 len -= 2 + (count * 4);
953
954 if (len < 2) {
955 tab_on_first(&first);
956 printf("\t * Authentication suites: %s\n", defauth);
957 return;
958 }
959
960 count = data[0] | (data[1] << 8);
31d477fb
MH
961 if (2 + (count * 4) > len)
962 goto invalid;
963
857d966e
MH
964 tab_on_first(&first);
965 printf("\t * Authentication suites:");
83b4934c 966 for (i = 0; i < count; i++) {
857d966e
MH
967 printf(" ");
968 print_auth(data + 2 + (i * 4));
969 }
970 printf("\n");
971
972 data += 2 + (count * 4);
973 len -= 2 + (count * 4);
974
6a4f24e8
JB
975 if (len >= 2) {
976 capa = data[0] | (data[1] << 8);
977 tab_on_first(&first);
cadbe89a
JB
978 printf("\t * Capabilities:");
979 if (capa & 0x0001)
980 printf(" PreAuth");
981 if (capa & 0x0002)
982 printf(" NoPairwise");
983 switch ((capa & 0x000c) >> 2) {
984 case 0:
4361eef3 985 printf(" 1-PTKSA-RC");
cadbe89a
JB
986 break;
987 case 1:
988 printf(" 2-PTKSA-RC");
989 break;
990 case 2:
991 printf(" 4-PTKSA-RC");
992 break;
993 case 3:
994 printf(" 16-PTKSA-RC");
995 break;
996 }
997 switch ((capa & 0x0030) >> 4) {
998 case 0:
4361eef3 999 printf(" 1-GTKSA-RC");
cadbe89a
JB
1000 break;
1001 case 1:
1002 printf(" 2-GTKSA-RC");
1003 break;
1004 case 2:
1005 printf(" 4-GTKSA-RC");
1006 break;
1007 case 3:
1008 printf(" 16-GTKSA-RC");
1009 break;
1010 }
1011 if (capa & 0x0040)
1012 printf(" MFP-required");
1013 if (capa & 0x0080)
1014 printf(" MFP-capable");
1015 if (capa & 0x0200)
1016 printf(" Peerkey-enabled");
1017 if (capa & 0x0400)
1018 printf(" SPP-AMSDU-capable");
1019 if (capa & 0x0800)
1020 printf(" SPP-AMSDU-required");
1021 printf(" (0x%.4x)\n", capa);
6a4f24e8
JB
1022 data += 2;
1023 len -= 2;
1024 }
31d477fb 1025
5ba3f523
JB
1026 if (len >= 2) {
1027 int pmkid_count = data[0] | (data[1] << 8);
1028
1029 if (len >= 2 + 16 * pmkid_count) {
1030 tab_on_first(&first);
1031 printf("\t * %d PMKIDs\n", pmkid_count);
1032 /* not printing PMKID values */
1033 data += 2 + 16 * pmkid_count;
1034 len -= 2 + 16 * pmkid_count;
1035 } else
1036 goto invalid;
1037 }
1038
1039 if (len >= 4) {
1040 tab_on_first(&first);
1041 printf("\t * Group mgmt cipher suite: ");
1042 print_cipher(data);
1043 printf("\n");
1044 data += 4;
1045 len -= 4;
1046 }
1047
cadbe89a 1048 invalid:
31d477fb
MH
1049 if (len != 0) {
1050 printf("\t\t * bogus tail data (%d):", len);
1051 while (len) {
1052 printf(" %.2x", *data);
1053 data++;
1054 len--;
1055 }
1056 printf("\n");
1057 }
857d966e
MH
1058}
1059
f188ab67
BG
1060static void print_rsn_ie(const char *defcipher, const char *defauth,
1061 uint8_t len, const uint8_t *data)
1062{
1063 _print_rsn_ie(defcipher, defauth, len, data, 0);
1064}
1065
1066static void print_osen_ie(const char *defcipher, const char *defauth,
1067 uint8_t len, const uint8_t *data)
1068{
1069 printf("\n\t");
1070 _print_rsn_ie(defcipher, defauth, len, data, 1);
1071}
1072
5f310d83
SM
1073static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data,
1074 const struct print_ies_data *ie_buffer)
857d966e 1075{
83b4934c 1076 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
857d966e
MH
1077}
1078
5f310d83
SM
1079static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
1080 const struct print_ies_data *ie_buffer)
0c445c24 1081{
357c1a5d 1082 printf("\n");
7ddfb679
JB
1083 print_ht_capability(data[0] | (data[1] << 8));
1084 print_ampdu_length(data[2] & 3);
c79c7464 1085 print_ampdu_spacing((data[2] >> 2) & 7);
7ddfb679 1086 print_ht_mcs(data + 3);
0c445c24
LR
1087}
1088
489f0ea9
BG
1089static const char* ntype_11u(uint8_t t)
1090{
1091 switch (t) {
1092 case 0: return "Private";
1093 case 1: return "Private with Guest";
1094 case 2: return "Chargeable Public";
1095 case 3: return "Free Public";
1096 case 4: return "Personal Device";
1097 case 5: return "Emergency Services Only";
1098 case 14: return "Test or Experimental";
1099 case 15: return "Wildcard";
1100 default: return "Reserved";
1101 }
1102}
1103
1104static const char* vgroup_11u(uint8_t t)
1105{
1106 switch (t) {
1107 case 0: return "Unspecified";
1108 case 1: return "Assembly";
1109 case 2: return "Business";
1110 case 3: return "Educational";
1111 case 4: return "Factory and Industrial";
1112 case 5: return "Institutional";
1113 case 6: return "Mercantile";
1114 case 7: return "Residential";
1115 case 8: return "Storage";
1116 case 9: return "Utility and Miscellaneous";
1117 case 10: return "Vehicular";
1118 case 11: return "Outdoor";
1119 default: return "Reserved";
1120 }
1121}
1122
5f310d83
SM
1123static void print_interworking(const uint8_t type, uint8_t len,
1124 const uint8_t *data,
1125 const struct print_ies_data *ie_buffer)
489f0ea9
BG
1126{
1127 /* See Section 7.3.2.92 in the 802.11u spec. */
1128 printf("\n");
1129 if (len >= 1) {
1130 uint8_t ano = data[0];
1131 printf("\t\tNetwork Options: 0x%hx\n", (unsigned short)(ano));
1132 printf("\t\t\tNetwork Type: %i (%s)\n",
1133 (int)(ano & 0xf), ntype_11u(ano & 0xf));
1134 if (ano & (1<<4))
1135 printf("\t\t\tInternet\n");
1136 if (ano & (1<<5))
1137 printf("\t\t\tASRA\n");
1138 if (ano & (1<<6))
1139 printf("\t\t\tESR\n");
1140 if (ano & (1<<7))
1141 printf("\t\t\tUESA\n");
1142 }
1143 if ((len == 3) || (len == 9)) {
1144 printf("\t\tVenue Group: %i (%s)\n",
1145 (int)(data[1]), vgroup_11u(data[1]));
1146 printf("\t\tVenue Type: %i\n", (int)(data[2]));
1147 }
1148 if (len == 9)
6e0ce463 1149 printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
489f0ea9
BG
1150 data[3], data[4], data[5], data[6], data[7], data[8]);
1151 else if (len == 7)
6e0ce463 1152 printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
489f0ea9
BG
1153 data[1], data[2], data[3], data[4], data[5], data[6]);
1154}
1155
5f310d83
SM
1156static void print_11u_advert(const uint8_t type, uint8_t len,
1157 const uint8_t *data,
1158 const struct print_ies_data *ie_buffer)
3f818f6d
BG
1159{
1160 /* See Section 7.3.2.93 in the 802.11u spec. */
1161 /* TODO: This code below does not decode private protocol IDs */
1162 int idx = 0;
1163 printf("\n");
1164 while (idx < (len - 1)) {
1165 uint8_t qri = data[idx];
1166 uint8_t proto_id = data[idx + 1];
1167 printf("\t\tQuery Response Info: 0x%hx\n", (unsigned short)(qri));
1168 printf("\t\t\tQuery Response Length Limit: %i\n",
1169 (qri & 0x7f));
1170 if (qri & (1<<7))
1171 printf("\t\t\tPAME-BI\n");
1172 switch(proto_id) {
1173 case 0:
1174 printf("\t\t\tANQP\n"); break;
1175 case 1:
1176 printf("\t\t\tMIH Information Service\n"); break;
1177 case 2:
1178 printf("\t\t\tMIH Command and Event Services Capability Discovery\n"); break;
1179 case 3:
1180 printf("\t\t\tEmergency Alert System (EAS)\n"); break;
1181 case 221:
1182 printf("\t\t\tVendor Specific\n"); break;
1183 default:
1184 printf("\t\t\tReserved: %i\n", proto_id); break;
1185 }
1186 idx += 2;
1187 }
1188}
1189
5f310d83
SM
1190static void print_11u_rcon(const uint8_t type, uint8_t len, const uint8_t *data,
1191 const struct print_ies_data *ie_buffer)
bc9e14f1
BG
1192{
1193 /* See Section 7.3.2.96 in the 802.11u spec. */
1194 int idx = 0;
1195 int ln0 = data[1] & 0xf;
1196 int ln1 = ((data[1] & 0xf0) >> 4);
1197 int ln2 = 0;
1198 printf("\n");
1199
1200 if (ln1)
1201 ln2 = len - 2 - ln0 - ln1;
1202
1203 printf("\t\tANQP OIs: %i\n", data[0]);
1204
1205 if (ln0 > 0) {
1206 printf("\t\tOI 1: ");
1207 if (2 + ln0 > len) {
1208 printf("Invalid IE length.\n");
1209 } else {
1210 for (idx = 0; idx < ln0; idx++) {
6e0ce463 1211 printf("%02hhx", data[2 + idx]);
bc9e14f1
BG
1212 }
1213 printf("\n");
1214 }
1215 }
1216
1217 if (ln1 > 0) {
1218 printf("\t\tOI 2: ");
1219 if (2 + ln0 + ln1 > len) {
1220 printf("Invalid IE length.\n");
1221 } else {
1222 for (idx = 0; idx < ln1; idx++) {
6e0ce463 1223 printf("%02hhx", data[2 + ln0 + idx]);
bc9e14f1
BG
1224 }
1225 printf("\n");
1226 }
1227 }
1228
1229 if (ln2 > 0) {
1230 printf("\t\tOI 3: ");
1231 if (2 + ln0 + ln1 + ln2 > len) {
1232 printf("Invalid IE length.\n");
1233 } else {
1234 for (idx = 0; idx < ln2; idx++) {
6e0ce463 1235 printf("%02hhx", data[2 + ln0 + ln1 + idx]);
bc9e14f1
BG
1236 }
1237 printf("\n");
1238 }
1239 }
1240}
1241
2dfd1f0a
MT
1242static void print_tx_power_envelope(const uint8_t type, uint8_t len,
1243 const uint8_t *data,
1244 const struct print_ies_data *ie_buffer)
1245{
1246 const uint8_t local_max_tx_power_count = data[0] & 7;
1247 const uint8_t local_max_tx_power_unit_interp = (data[0] >> 3) & 7;
1248 int i;
1249 static const char *power_names[] = {
1250 "Local Maximum Transmit Power For 20 MHz",
1251 "Local Maximum Transmit Power For 40 MHz",
1252 "Local Maximum Transmit Power For 80 MHz",
1253 "Local Maximum Transmit Power For 160/80+80 MHz",
1254 };
1255
1256 printf("\n");
1257
1258 if (local_max_tx_power_count + 2 != len)
1259 return;
1260 if (local_max_tx_power_unit_interp != 0)
1261 return;
1262 for (i = 0; i < local_max_tx_power_count + 1; ++i) {
1263 int8_t power_val = ((int8_t)data[1 + i]) >> 1;
1264 int8_t point5 = data[1 + i] & 1;
1265 if (point5)
1266 printf("\t\t * %s: %i.5 dBm\n", power_names[i], power_val);
1267 else
1268 printf("\t\t * %s: %i dBm\n", power_names[i], power_val);
1269 }
1270}
1271
29f7579e
JB
1272static const char *ht_secondary_offset[4] = {
1273 "no secondary",
1274 "above",
1275 "[reserved!]",
1276 "below",
1277};
1278
5f310d83
SM
1279static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data,
1280 const struct print_ies_data *ie_buffer)
be7602fb 1281{
be7602fb
JB
1282 static const char *protection[4] = {
1283 "no",
1284 "nonmember",
1285 "20 MHz",
1286 "non-HT mixed",
1287 };
1288 static const char *sta_chan_width[2] = {
1289 "20 MHz",
1290 "any",
1291 };
1292
1293 printf("\n");
1294 printf("\t\t * primary channel: %d\n", data[0]);
1295 printf("\t\t * secondary channel offset: %s\n",
29f7579e 1296 ht_secondary_offset[data[1] & 0x3]);
be7602fb
JB
1297 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
1298 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
1299 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
1300 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
1301 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
1302 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
1303 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
1304 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
1305 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
1306 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
1307 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
1308}
1309
5f310d83
SM
1310static void print_capabilities(const uint8_t type, uint8_t len,
1311 const uint8_t *data,
1312 const struct print_ies_data *ie_buffer)
9b880b00 1313{
5f310d83
SM
1314 int i, base, bit, si_duration = 0, max_amsdu = 0;
1315 bool s_psmp_support = false, is_vht_cap = false;
1316 unsigned char *ie = ie_buffer->ie;
1317 int ielen = ie_buffer->ielen;
31d2d259 1318
5f310d83
SM
1319 while (ielen >= 2 && ielen >= ie[1]) {
1320 if (ie[0] == 191) {
1321 is_vht_cap = true;
1322 break;
1323 }
1324 ielen -= ie[1] + 2;
1325 ie += ie[1] + 2;
1326 }
31d2d259
JB
1327
1328 for (i = 0; i < len; i++) {
1329 base = i * 8;
1330
1331 for (bit = 0; bit < 8; bit++) {
1332 if (!(data[i] & (1 << bit)))
1333 continue;
1334
5f310d83 1335 printf("\n\t\t *");
31d2d259 1336
70c649d2
JB
1337#define CAPA(bit, name) case bit: printf(" " name); break
1338
5f310d83
SM
1339/* if the capability 'cap' exists add 'val' to 'sum'
1340 * otherwise print 'Reserved' */
1341#define ADD_BIT_VAL(bit, cap, sum, val) case (bit): do { \
1342 if (!(cap)) { \
1343 printf(" Reserved"); \
1344 break; \
1345 } \
1346 sum += val; \
1347 break; \
1348} while (0)
1349
31d2d259 1350 switch (bit + base) {
70c649d2
JB
1351 CAPA(0, "HT Information Exchange Supported");
1352 CAPA(1, "reserved (On-demand Beacon)");
1353 CAPA(2, "Extended Channel Switching");
1354 CAPA(3, "reserved (Wave Indication)");
1355 CAPA(4, "PSMP Capability");
1356 CAPA(5, "reserved (Service Interval Granularity)");
5f310d83
SM
1357
1358 case 6:
1359 s_psmp_support = true;
1360 printf(" S-PSMP Capability");
1361 break;
1362
70c649d2
JB
1363 CAPA(7, "Event");
1364 CAPA(8, "Diagnostics");
1365 CAPA(9, "Multicast Diagnostics");
1366 CAPA(10, "Location Tracking");
1367 CAPA(11, "FMS");
1368 CAPA(12, "Proxy ARP Service");
1369 CAPA(13, "Collocated Interference Reporting");
1370 CAPA(14, "Civic Location");
1371 CAPA(15, "Geospatial Location");
1372 CAPA(16, "TFS");
1373 CAPA(17, "WNM-Sleep Mode");
1374 CAPA(18, "TIM Broadcast");
1375 CAPA(19, "BSS Transition");
1376 CAPA(20, "QoS Traffic Capability");
1377 CAPA(21, "AC Station Count");
1378 CAPA(22, "Multiple BSSID");
1379 CAPA(23, "Timing Measurement");
1380 CAPA(24, "Channel Usage");
1381 CAPA(25, "SSID List");
1382 CAPA(26, "DMS");
1383 CAPA(27, "UTC TSF Offset");
1384 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
1385 CAPA(29, "TDLS Peer PSM Support");
1386 CAPA(30, "TDLS channel switching");
1387 CAPA(31, "Interworking");
1388 CAPA(32, "QoS Map");
1389 CAPA(33, "EBR");
1390 CAPA(34, "SSPN Interface");
1391 CAPA(35, "Reserved");
1392 CAPA(36, "MSGCF Capability");
1393 CAPA(37, "TDLS Support");
1394 CAPA(38, "TDLS Prohibited");
1395 CAPA(39, "TDLS Channel Switching Prohibited");
1396 CAPA(40, "Reject Unadmitted Frame");
5f310d83
SM
1397
1398 ADD_BIT_VAL(41, s_psmp_support, si_duration, 1);
1399 ADD_BIT_VAL(42, s_psmp_support, si_duration, 2);
1400 ADD_BIT_VAL(43, s_psmp_support, si_duration, 4);
1401
70c649d2
JB
1402 CAPA(44, "Identifier Location");
1403 CAPA(45, "U-APSD Coexistence");
1404 CAPA(46, "WNM-Notification");
1405 CAPA(47, "Reserved");
1406 CAPA(48, "UTF-8 SSID");
5f310d83
SM
1407 CAPA(49, "QMFActivated");
1408 CAPA(50, "QMFReconfigurationActivated");
1409 CAPA(51, "Robust AV Streaming");
1410 CAPA(52, "Advanced GCR");
1411 CAPA(53, "Mesh GCR");
1412 CAPA(54, "SCS");
1413 CAPA(55, "QLoad Report");
1414 CAPA(56, "Alternate EDCA");
1415 CAPA(57, "Unprotected TXOP Negotiation");
1416 CAPA(58, "Protected TXOP egotiation");
1417 CAPA(59, "Reserved");
1418 CAPA(60, "Protected QLoad Report");
1419 CAPA(61, "TDLS Wider Bandwidth");
1420 CAPA(62, "Operating Mode Notification");
1421
1422 ADD_BIT_VAL(63, is_vht_cap, max_amsdu, 1);
1423 ADD_BIT_VAL(64, is_vht_cap, max_amsdu, 2);
1424
1425 CAPA(65, "Channel Schedule Management");
1426 CAPA(66, "Geodatabase Inband Enabling Signal");
1427 CAPA(67, "Network Channel Control");
1428 CAPA(68, "White Space Map");
1429 CAPA(69, "Channel Availability Query");
d06cda9e
AK
1430 CAPA(70, "FTM Responder");
1431 CAPA(71, "FTM Initiator");
5f310d83
SM
1432 CAPA(72, "Reserved");
1433 CAPA(73, "Extended Spectrum Management Capable");
1434 CAPA(74, "Reserved");
31d2d259
JB
1435 default:
1436 printf(" %d", bit);
1437 break;
1438 }
5f310d83 1439#undef ADD_BIT_VAL
70c649d2 1440#undef CAPA
31d2d259
JB
1441 }
1442 }
9b880b00 1443
5f310d83
SM
1444 if (s_psmp_support)
1445 printf("\n\t\t * Service Interval Granularity is %d ms",
1446 (si_duration + 1) * 5);
1447
1448 if (is_vht_cap) {
1449 printf("\n\t\t * Max Number Of MSDUs In A-MSDU is ");
1450 switch (max_amsdu) {
1451 case 0:
1452 printf("unlimited");
1453 break;
1454 case 1:
1455 printf("32");
1456 break;
1457 case 2:
1458 printf("16");
1459 break;
1460 case 3:
1461 printf("8");
1462 break;
1463 default:
1464 break;
1465 }
1466 }
1467
9b880b00
MH
1468 printf("\n");
1469}
1470
5f310d83
SM
1471static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data,
1472 const struct print_ies_data *ie_buffer)
575280cc
JM
1473{
1474 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
1475 "Bitmap[0] 0x%x",
1476 data[0], data[1], data[2], data[3]);
1477 if (len - 4)
1478 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
1479 printf("\n");
1480}
1481
5f310d83
SM
1482static void print_ibssatim(const uint8_t type, uint8_t len, const uint8_t *data,
1483 const struct print_ies_data *ie_buffer)
792172b0 1484{
08c3f7d3 1485 printf(" %d TUs\n", (data[1] << 8) + data[0]);
792172b0
JM
1486}
1487
5f310d83
SM
1488static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
1489 const struct print_ies_data *ie_buffer)
54eb1613
JB
1490{
1491 printf("\n");
a7375d5e
MT
1492 print_vht_info((__u32) data[0] | ((__u32)data[1] << 8) |
1493 ((__u32)data[2] << 16) | ((__u32)data[3] << 24),
54eb1613
JB
1494 data + 4);
1495}
1496
5f310d83
SM
1497static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data,
1498 const struct print_ies_data *ie_buffer)
ca159934
JB
1499{
1500 const char *chandwidths[] = {
1501 [0] = "20 or 40 MHz",
1502 [1] = "80 MHz",
1503 [3] = "80+80 MHz",
1504 [2] = "160 MHz",
1505 };
1506
1507 printf("\n");
1508 printf("\t\t * channel width: %d (%s)\n", data[0],
1509 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
1510 printf("\t\t * center freq segment 1: %d\n", data[1]);
1511 printf("\t\t * center freq segment 2: %d\n", data[2]);
1512 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
1513}
1514
3d343608
MT
1515static void print_supp_op_classes(const uint8_t type, uint8_t len,
1516 const uint8_t *data,
1517 const struct print_ies_data *ie_buffer)
1518{
1519 uint8_t *p = (uint8_t*) data;
1520 const uint8_t *next_data = p + len;
1521 int zero_delimiter = 0;
1522 int one_hundred_thirty_delimiter = 0;
1523
1524 printf("\n");
1525 printf("\t\t * current operating class: %d\n", *p);
1526 while (++p < next_data) {
1527 if (*p == 130) {
1528 one_hundred_thirty_delimiter = 1;
1529 break;
1530 }
1531 if (*p == 0) {
1532 zero_delimiter = 0;
1533 break;
1534 }
1535 printf("\t\t * operating class: %d\n", *p);
1536 }
1537 if (one_hundred_thirty_delimiter)
1538 while (++p < next_data) {
1539 printf("\t\t * current operating class extension: %d\n", *p);
1540 }
1541 if (zero_delimiter)
1542 while (++p < next_data - 1) {
1543 printf("\t\t * operating class tuple: %d %d\n", p[0], p[1]);
1544 if (*p == 0)
1545 break;
1546 }
1547}
1548
0e6d5446
MT
1549static void print_measurement_pilot_tx(const uint8_t type, uint8_t len,
1550 const uint8_t *data,
1551 const struct print_ies_data *ie_buffer)
1552{
1553 uint8_t *p, len_remaining;
1554
1555 printf("\n");
1556 printf("\t\t * interval: %d TUs\n", data[0]);
1557
1558 if(len <= 1)
1559 return;
1560
1561 p = (uint8_t *) data + 1;
1562 len_remaining = len - 1;
1563
1564 while (len_remaining >=5) {
1565 uint8_t subelement_id = *p, len, *end;
1566
1567 p++;
1568 len = *p;
1569 p++;
1570 end = p + len;
1571
1572 len_remaining -= 2;
1573
1574 /* 802.11-2016 only allows vendor specific elements */
1575 if (subelement_id != 221) {
1576 printf("\t\t * <Invalid subelement ID %d>\n", subelement_id);
1577 return;
1578 }
1579
1580 if (len < 3 || len > len_remaining) {
1581 printf(" <Parse error, element too short>\n");
1582 return;
1583 }
1584
1585 printf("\t\t * vendor specific: OUI %.2x:%.2x:%.2x, data:",
1586 p[0], p[1], p[2]);
1587 /* add only two here and use ++p in while loop */
1588 p += 2;
1589
1590 while (++p < end)
1591 printf(" %.2x", *p);
1592 printf("\n");
1593
1594 len_remaining -= len;
1595 }
1596}
1597
5f310d83
SM
1598static void print_obss_scan_params(const uint8_t type, uint8_t len,
1599 const uint8_t *data,
1600 const struct print_ies_data *ie_buffer)
29f7579e
JB
1601{
1602 printf("\n");
1603 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
1604 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
1605 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
1606 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
1607 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
1608 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
1609 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
1610 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
1611}
1612
5f310d83
SM
1613static void print_secchan_offs(const uint8_t type, uint8_t len,
1614 const uint8_t *data,
1615 const struct print_ies_data *ie_buffer)
29f7579e
JB
1616{
1617 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
1618 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
1619 else
1620 printf(" %d\n", data[0]);
1621}
1622
5f310d83
SM
1623static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data,
1624 const struct print_ies_data *ie_buffer)
29f7579e
JB
1625{
1626 printf("\n");
1627 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
1628 printf("\t\t * channel utilisation: %d/255\n", data[2]);
1629 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
1630}
1631
5f310d83
SM
1632static void print_mesh_conf(const uint8_t type, uint8_t len,
1633 const uint8_t *data,
1634 const struct print_ies_data *ie_buffer)
2d86b0f3
CYY
1635{
1636 printf("\n");
1637 printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
1638 printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
1639 printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
1640 printf("\t\t * Synchronization Method ID: %d\n", data[3]);
1641 printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
1642 printf("\t\t * Mesh Formation Info:\n");
1643 printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
1644 if (data[5] & 0x01)
1645 printf("\t\t\t Connected to Mesh Gate\n");
1646 if (data[5] & 0x80)
1647 printf("\t\t\t Connected to AS\n");
1648 printf("\t\t * Mesh Capability\n");
1649 if (data[6] & 0x01)
1650 printf("\t\t\t Accepting Additional Mesh Peerings\n");
1651 if (data[6] & 0x02)
1652 printf("\t\t\t MCCA Supported\n");
1653 if (data[6] & 0x04)
1654 printf("\t\t\t MCCA Enabled\n");
1655 if (data[6] & 0x08)
1656 printf("\t\t\t Forwarding\n");
1657 if (data[6] & 0x10)
1658 printf("\t\t\t MBCA Supported\n");
1659 if (data[6] & 0x20)
1660 printf("\t\t\t TBTT Adjusting\n");
1661 if (data[6] & 0x40)
1662 printf("\t\t\t Mesh Power Save Level\n");
1663}
1664
83b4934c
JB
1665struct ie_print {
1666 const char *name;
5f310d83
SM
1667 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data,
1668 const struct print_ies_data *ie_buffer);
83b4934c 1669 uint8_t minlen, maxlen;
febeb0c0 1670 uint8_t flags;
764fe753
JB
1671};
1672
5f310d83
SM
1673static void print_ie(const struct ie_print *p, const uint8_t type, uint8_t len,
1674 const uint8_t *data,
1675 const struct print_ies_data *ie_buffer)
4673a894 1676{
83b4934c
JB
1677 int i;
1678
1679 if (!p->print)
1680 return;
1681
1682 printf("\t%s:", p->name);
1683 if (len < p->minlen || len > p->maxlen) {
1684 if (len > 1) {
1685 printf(" <invalid: %d bytes:", len);
1686 for (i = 0; i < len; i++)
1687 printf(" %.02x", data[i]);
1688 printf(">\n");
1689 } else if (len)
1690 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
1691 else
1692 printf(" <invalid: no data>\n");
1693 return;
1694 }
1695
5f310d83 1696 p->print(type, len, data, ie_buffer);
83b4934c
JB
1697}
1698
1699#define PRINT_IGN { \
1700 .name = "IGNORE", \
1701 .print = NULL, \
1702 .minlen = 0, \
1703 .maxlen = 255, \
4673a894
JB
1704}
1705
83b4934c 1706static const struct ie_print ieprinters[] = {
febeb0c0
JB
1707 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
1708 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
014d581a 1709 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
575280cc 1710 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
792172b0 1711 [6] = { "IBSS ATIM window", print_ibssatim, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1712 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
29f7579e 1713 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
febeb0c0 1714 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
792172b0 1715 [35] = { "TPC report", print_tpcreport, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1716 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
a2e61861 1717 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
792172b0 1718 [47] = { "ERP D4.0", print_erp, 1, 255, BIT(PRINT_SCAN), },
fe969ade 1719 [51] = { "AP Channel Report", print_ap_channel_report, 1, 255, BIT(PRINT_SCAN), },
3d343608 1720 [59] = { "Supported operating classes", print_supp_op_classes, 1, 255, BIT(PRINT_SCAN), },
0e6d5446 1721 [66] = { "Measurement Pilot Transmission", print_measurement_pilot_tx, 1, 255, BIT(PRINT_SCAN), },
29f7579e 1722 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
be7602fb 1723 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
29f7579e 1724 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
54eb1613 1725 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
ca159934 1726 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
febeb0c0
JB
1727 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
1728 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
4d72d1d2 1729 [70] = { "RM enabled capabilities", print_rm_enabled_capabilities, 5, 5, BIT(PRINT_SCAN), },
2d86b0f3 1730 [113] = { "MESH Configuration", print_mesh_conf, 7, 7, BIT(PRINT_SCAN), },
720583e9 1731 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
febeb0c0 1732 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
489f0ea9 1733 [107] = { "802.11u Interworking", print_interworking, 0, 255, BIT(PRINT_SCAN), },
3f818f6d 1734 [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
baea1a0b 1735 [111] = { "802.11u Roaming Consortium", print_11u_rcon, 2, 255, BIT(PRINT_SCAN), },
2dfd1f0a 1736 [195] = { "Transmit Power Envelope", print_tx_power_envelope, 2, 5, BIT(PRINT_SCAN), },
83b4934c
JB
1737};
1738
5f310d83
SM
1739static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data,
1740 const struct print_ies_data *ie_buffer)
83b4934c
JB
1741{
1742 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
1743}
1744
5f310d83
SM
1745static void print_wifi_osen(const uint8_t type, uint8_t len,
1746 const uint8_t *data,
1747 const struct print_ies_data *ie_buffer)
f188ab67
BG
1748{
1749 print_osen_ie("OSEN", "OSEN", len, data);
1750}
1751
1cab57eb
JB
1752static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
1753{
1754 int i;
1755 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
1756
1757 if (len < 19)
1758 goto invalid;
1759
1760 if (data[0] != 1) {
cee4fe20 1761 printf("Parameter: not version 1: ");
1cab57eb
JB
1762 return false;
1763 }
1764
89ea706f 1765 printf("\t * Parameter version 1");
1cab57eb
JB
1766
1767 data++;
1768
1769 if (data[0] & 0x80)
89ea706f 1770 printf("\n\t\t * u-APSD");
1cab57eb
JB
1771
1772 data += 2;
1773
1774 for (i = 0; i < 4; i++) {
89ea706f 1775 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
87181516 1776 if (data[0] & 0x10)
1cab57eb
JB
1777 printf(" acm");
1778 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
1779 (1 << (data[1] >> 4)) - 1);
a2a4c265 1780 printf(", AIFSN %d", data[0] & 0xf);
1cab57eb 1781 if (data[2] | data[3])
cee4fe20 1782 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
1cab57eb
JB
1783 data += 4;
1784 }
1785
1786 printf("\n");
1787 return true;
1788
1789 invalid:
1790 printf("invalid: ");
1791 return false;
1792}
1793
5f310d83
SM
1794static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data,
1795 const struct print_ies_data *ie_buffer)
6ff0c93a
MH
1796{
1797 int i;
1798
6ff0c93a
MH
1799 switch (data[0]) {
1800 case 0x00:
83b4934c 1801 printf(" information:");
6ff0c93a
MH
1802 break;
1803 case 0x01:
1cab57eb
JB
1804 if (print_wifi_wmm_param(data + 1, len - 1))
1805 return;
6ff0c93a
MH
1806 break;
1807 default:
83b4934c 1808 printf(" type %d:", data[0]);
6ff0c93a
MH
1809 break;
1810 }
1811
1cab57eb
JB
1812 for(i = 1; i < len; i++)
1813 printf(" %.02x", data[i]);
6ff0c93a
MH
1814 printf("\n");
1815}
1816
a6816965
JM
1817static const char * wifi_wps_dev_passwd_id(uint16_t id)
1818{
1819 switch (id) {
1820 case 0:
1821 return "Default (PIN)";
1822 case 1:
1823 return "User-specified";
1824 case 2:
1825 return "Machine-specified";
1826 case 3:
1827 return "Rekey";
1828 case 4:
1829 return "PushButton";
1830 case 5:
1831 return "Registrar-specified";
1832 default:
1833 return "??";
1834 }
1835}
1836
5f310d83
SM
1837static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data,
1838 const struct print_ies_data *ie_buffer)
4673a894
JB
1839{
1840 bool first = true;
1841 __u16 subtype, sublen;
1842
4673a894
JB
1843 while (len >= 4) {
1844 subtype = (data[0] << 8) + data[1];
1845 sublen = (data[2] << 8) + data[3];
e4ebc462 1846 if (sublen > len - 4)
4673a894
JB
1847 break;
1848
1849 switch (subtype) {
1850 case 0x104a:
1851 tab_on_first(&first);
7be767cb
MT
1852 if (sublen < 1) {
1853 printf("\t * Version: (invalid "
1854 "length %d)\n", sublen);
1855 break;
1856 }
dffc6750 1857 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
4673a894
JB
1858 break;
1859 case 0x1011:
1860 tab_on_first(&first);
84289c05
JB
1861 if (sublen < 1) {
1862 printf("\t * Device Name: (invalid length %d)\n",
1863 sublen);
1864 break;
1865 }
4673a894
JB
1866 printf("\t * Device name: %.*s\n", sublen, data + 4);
1867 break;
a6816965
JM
1868 case 0x1012: {
1869 uint16_t id;
1870 tab_on_first(&first);
1871 if (sublen != 2) {
7be767cb
MT
1872 printf("\t * Device Password ID: (invalid length %d)\n",
1873 sublen);
a6816965
JM
1874 break;
1875 }
1876 id = data[4] << 8 | data[5];
1877 printf("\t * Device Password ID: %u (%s)\n",
1878 id, wifi_wps_dev_passwd_id(id));
1879 break;
1880 }
4673a894
JB
1881 case 0x1021:
1882 tab_on_first(&first);
84289c05
JB
1883 if (sublen < 1) {
1884 printf("\t * Manufacturer: (invalid length %d)\n",
1885 sublen);
1886 break;
1887 }
4673a894
JB
1888 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
1889 break;
1890 case 0x1023:
1891 tab_on_first(&first);
84289c05
JB
1892 if (sublen < 1) {
1893 printf("\t * Model: (invalid length %d)\n",
1894 sublen);
1895 break;
1896 }
4673a894
JB
1897 printf("\t * Model: %.*s\n", sublen, data + 4);
1898 break;
a6816965
JM
1899 case 0x1024:
1900 tab_on_first(&first);
84289c05
JB
1901 if (sublen < 1) {
1902 printf("\t * Model Number: (invalid length %d)\n",
1903 sublen);
1904 break;
1905 }
a6816965
JM
1906 printf("\t * Model Number: %.*s\n", sublen, data + 4);
1907 break;
1908 case 0x103b: {
7be767cb
MT
1909 __u8 val;
1910
1911 if (sublen < 1) {
1912 printf("\t * Response Type: (invalid length %d)\n",
1913 sublen);
1914 break;
1915 }
1916 val = data[4];
a6816965
JM
1917 tab_on_first(&first);
1918 printf("\t * Response Type: %d%s\n",
1919 val, val == 3 ? " (AP)" : "");
1920 break;
1921 }
1922 case 0x103c: {
84289c05
JB
1923 __u8 val;
1924
1925 if (sublen < 1) {
1926 printf("\t * RF Bands: (invalid length %d)\n",
1927 sublen);
1928 break;
1929 }
1930 val = data[4];
a6816965
JM
1931 tab_on_first(&first);
1932 printf("\t * RF Bands: 0x%x\n", val);
1933 break;
1934 }
1935 case 0x1041: {
7be767cb
MT
1936 __u8 val;
1937
1938 if (sublen < 1) {
1939 printf("\t * Selected Registrar: (invalid length %d)\n",
1940 sublen);
1941 break;
1942 }
1943 val = data[4];
a6816965
JM
1944 tab_on_first(&first);
1945 printf("\t * Selected Registrar: 0x%x\n", val);
1946 break;
1947 }
1948 case 0x1042:
1949 tab_on_first(&first);
84289c05
JB
1950 if (sublen < 1) {
1951 printf("\t * Serial Number: (invalid length %d)\n",
1952 sublen);
1953 break;
1954 }
a6816965
JM
1955 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
1956 break;
1957 case 0x1044: {
7be767cb
MT
1958 __u8 val;
1959
1960 if (sublen < 1) {
1961 printf("\t * Wi-Fi Protected Setup State: (invalid length %d)\n",
1962 sublen);
1963 break;
1964 }
1965 val = data[4];
a6816965
JM
1966 tab_on_first(&first);
1967 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
1968 val,
1969 val == 1 ? " (Unconfigured)" : "",
1970 val == 2 ? " (Configured)" : "");
1971 break;
1972 }
09fe09dc
JB
1973 case 0x1047:
1974 tab_on_first(&first);
1975 printf("\t * UUID: ");
1976 if (sublen != 16) {
1977 printf("(invalid, length=%d)\n", sublen);
1978 break;
1979 }
1980 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
1981 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
1982 data[4], data[5], data[6], data[7],
1983 data[8], data[9], data[10], data[11],
1984 data[12], data[13], data[14], data[15],
1985 data[16], data[17], data[18], data[19]);
1986 break;
f395f751
MT
1987 case 0x1049:
1988 tab_on_first(&first);
1989 if (sublen == 6 &&
1990 data[4] == 0x00 &&
1991 data[5] == 0x37 &&
1992 data[6] == 0x2a &&
1993 data[7] == 0x00 &&
1994 data[8] == 0x01) {
1995 uint8_t v2 = data[9];
1996 printf("\t * Version2: %d.%d\n", v2 >> 4, v2 & 0xf);
1997 }
1998 break;
a6816965
JM
1999 case 0x1054: {
2000 tab_on_first(&first);
2001 if (sublen != 8) {
7be767cb
MT
2002 printf("\t * Primary Device Type: (invalid length %d)\n",
2003 sublen);
a6816965
JM
2004 break;
2005 }
2006 printf("\t * Primary Device Type: "
2007 "%u-%02x%02x%02x%02x-%u\n",
2008 data[4] << 8 | data[5],
2009 data[6], data[7], data[8], data[9],
2010 data[10] << 8 | data[11]);
2011 break;
2012 }
7ee5a865 2013 case 0x1057: {
fe31a22e 2014 __u8 val = data[4];
7ee5a865 2015 tab_on_first(&first);
84289c05
JB
2016 if (sublen < 1) {
2017 printf("\t * AP setup locked: (invalid length %d)\n",
2018 sublen);
2019 break;
2020 }
fe31a22e 2021 printf("\t * AP setup locked: 0x%.2x\n", val);
7ee5a865
JB
2022 break;
2023 }
a6816965
JM
2024 case 0x1008:
2025 case 0x1053: {
7be767cb
MT
2026 __u16 meth;
2027 bool comma;
2028
2029 if (sublen < 2) {
2030 printf("\t * Config methods: (invalid length %d)\n",
2031 sublen);
2032 break;
2033 }
2034 meth = (data[4] << 8) + data[5];
2035 comma = false;
4673a894 2036 tab_on_first(&first);
a6816965
JM
2037 printf("\t * %sConfig methods:",
2038 subtype == 0x1053 ? "Selected Registrar ": "");
4673a894
JB
2039#define T(bit, name) do { \
2040 if (meth & (1<<bit)) { \
2041 if (comma) \
2042 printf(","); \
2043 comma = true; \
2044 printf(" " name); \
2045 } } while (0)
2046 T(0, "USB");
2047 T(1, "Ethernet");
2048 T(2, "Label");
2049 T(3, "Display");
2050 T(4, "Ext. NFC");
2051 T(5, "Int. NFC");
2052 T(6, "NFC Intf.");
2053 T(7, "PBC");
2054 T(8, "Keypad");
2055 printf("\n");
2056 break;
2057#undef T
2058 }
2650d46b
JB
2059 default: {
2060 const __u8 *subdata = data + 4;
2061 __u16 tmplen = sublen;
2062
2063 tab_on_first(&first);
2064 printf("\t * Unknown TLV (%#.4x, %d bytes):",
2065 subtype, tmplen);
2066 while (tmplen) {
2067 printf(" %.2x", *subdata);
2068 subdata++;
2069 tmplen--;
2070 }
2071 printf("\n");
4673a894
JB
2072 break;
2073 }
2650d46b 2074 }
4673a894
JB
2075
2076 data += sublen + 4;
2077 len -= sublen + 4;
2078 }
2079
2080 if (len != 0) {
2081 printf("\t\t * bogus tail data (%d):", len);
2082 while (len) {
2083 printf(" %.2x", *data);
2084 data++;
2085 len--;
2086 }
2087 printf("\n");
2088 }
2089}
2090
83b4934c 2091static const struct ie_print wifiprinters[] = {
febeb0c0
JB
2092 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
2093 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
2094 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
4673a894
JB
2095};
2096
5f310d83
SM
2097static inline void print_p2p(const uint8_t type, uint8_t len,
2098 const uint8_t *data,
2099 const struct print_ies_data *ie_buffer)
9a22374a
JB
2100{
2101 bool first = true;
2102 __u8 subtype;
2103 __u16 sublen;
2104
2105 while (len >= 3) {
2106 subtype = data[0];
2107 sublen = (data[2] << 8) + data[1];
2108
2109 if (sublen > len - 3)
2110 break;
2111
2112 switch (subtype) {
2113 case 0x02: /* capability */
2114 tab_on_first(&first);
2115 if (sublen < 2) {
2116 printf("\t * malformed capability\n");
2117 break;
2118 }
2119 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
2120 data[3], data[4]);
2121 break;
2122 case 0x0d: /* device info */
2123 if (sublen < 6 + 2 + 8 + 1) {
2124 printf("\t * malformed device info\n");
2125 break;
2126 }
c8edf8f5 2127 /* fall through */
9a22374a
JB
2128 case 0x00: /* status */
2129 case 0x01: /* minor reason */
2130 case 0x03: /* device ID */
2131 case 0x04: /* GO intent */
2132 case 0x05: /* configuration timeout */
2133 case 0x06: /* listen channel */
2134 case 0x07: /* group BSSID */
2135 case 0x08: /* ext listen timing */
2136 case 0x09: /* intended interface address */
2137 case 0x0a: /* manageability */
2138 case 0x0b: /* channel list */
2139 case 0x0c: /* NoA */
2140 case 0x0e: /* group info */
2141 case 0x0f: /* group ID */
2142 case 0x10: /* interface */
2143 case 0x11: /* operating channel */
2144 case 0x12: /* invitation flags */
2145 case 0xdd: /* vendor specific */
2146 default: {
505f9e48 2147 const __u8 *subdata = data + 3;
9a22374a
JB
2148 __u16 tmplen = sublen;
2149
2150 tab_on_first(&first);
2151 printf("\t * Unknown TLV (%#.2x, %d bytes):",
2152 subtype, tmplen);
2153 while (tmplen) {
2154 printf(" %.2x", *subdata);
2155 subdata++;
2156 tmplen--;
2157 }
2158 printf("\n");
2159 break;
2160 }
2161 }
2162
2163 data += sublen + 3;
2164 len -= sublen + 3;
2165 }
2166
2167 if (len != 0) {
2168 tab_on_first(&first);
2169 printf("\t * bogus tail data (%d):", len);
2170 while (len) {
2171 printf(" %.2x", *data);
2172 data++;
2173 len--;
2174 }
2175 printf("\n");
2176 }
2177}
2178
5f310d83
SM
2179static inline void print_hs20_ind(const uint8_t type, uint8_t len,
2180 const uint8_t *data,
2181 const struct print_ies_data *ie_buffer)
7e10c4ab
BG
2182{
2183 /* I can't find the spec for this...just going off what wireshark uses. */
2184 printf("\n");
2185 if (len > 0)
2186 printf("\t\tDGAF: %i\n", (int)(data[0] & 0x1));
2187 else
2188 printf("\t\tUnexpected length: %i\n", len);
2189}
2190
fc166079
HM
2191static void print_wifi_owe_tarns(const uint8_t type, uint8_t len,
2192 const uint8_t *data,
2193 const struct print_ies_data *ie_buffer)
2194{
2195 char mac_addr[20];
2196 int ssid_len;
2197
2198 printf("\n");
2199 if (len < 7)
2200 return;
2201
2202 mac_addr_n2a(mac_addr, data);
2203 printf("\t\tBSSID: %s\n", mac_addr);
2204
2205 ssid_len = data[6];
2206 if (ssid_len > len - 7)
2207 return;
2208 printf("\t\tSSID: ");
2209 print_ssid_escaped(ssid_len, data + 7);
2210 printf("\n");
2211
2212 /* optional elements */
2213 if (len >= ssid_len + 9) {
2214 printf("\t\tBand Info: %u\n", data[ssid_len + 7]);
2215 printf("\t\tChannel Info: %u\n", data[ssid_len + 8]);
2216 }
2217}
2218
9a22374a
JB
2219static const struct ie_print wfa_printers[] = {
2220 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
7e10c4ab 2221 [16] = { "HotSpot 2.0 Indication", print_hs20_ind, 1, 255, BIT(PRINT_SCAN), },
f188ab67 2222 [18] = { "HotSpot 2.0 OSEN", print_wifi_osen, 1, 255, BIT(PRINT_SCAN), },
fc166079 2223 [28] = { "OWE Transition Mode", print_wifi_owe_tarns, 7, 255, BIT(PRINT_SCAN), },
9a22374a
JB
2224};
2225
764fe753 2226static void print_vendor(unsigned char len, unsigned char *data,
febeb0c0 2227 bool unknown, enum print_ie_type ptype)
3563f4c5
JB
2228{
2229 int i;
2230
fbf80af5 2231 if (len < 3) {
4673a894 2232 printf("\tVendor specific: <too short> data:");
fbf80af5
JB
2233 for(i = 0; i < len; i++)
2234 printf(" %.02x", data[i]);
2235 printf("\n");
2236 return;
2237 }
2238
3bd60ef1 2239 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
febeb0c0
JB
2240 if (data[3] < ARRAY_SIZE(wifiprinters) &&
2241 wifiprinters[data[3]].name &&
2242 wifiprinters[data[3]].flags & BIT(ptype)) {
5f310d83 2243 print_ie(&wifiprinters[data[3]],
bcdceae1
JB
2244 data[3], len - 4, data + 4,
2245 NULL);
83b4934c
JB
2246 return;
2247 }
febeb0c0 2248 if (!unknown)
4673a894 2249 return;
3bd60ef1 2250 printf("\tMS/WiFi %#.2x, data:", data[3]);
4673a894
JB
2251 for(i = 0; i < len - 4; i++)
2252 printf(" %.02x", data[i + 4]);
2253 printf("\n");
2254 return;
2255 }
2256
9a22374a
JB
2257 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
2258 if (data[3] < ARRAY_SIZE(wfa_printers) &&
2259 wfa_printers[data[3]].name &&
2260 wfa_printers[data[3]].flags & BIT(ptype)) {
5f310d83 2261 print_ie(&wfa_printers[data[3]],
bcdceae1
JB
2262 data[3], len - 4, data + 4,
2263 NULL);
9a22374a
JB
2264 return;
2265 }
2266 if (!unknown)
2267 return;
2268 printf("\tWFA %#.2x, data:", data[3]);
2269 for(i = 0; i < len - 4; i++)
2270 printf(" %.02x", data[i + 4]);
2271 printf("\n");
2272 return;
2273 }
2274
febeb0c0 2275 if (!unknown)
764fe753
JB
2276 return;
2277
fbf80af5 2278 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
3563f4c5 2279 data[0], data[1], data[2]);
fbf80af5
JB
2280 for (i = 3; i < len; i++)
2281 printf(" %.2x", data[i]);
3563f4c5
JB
2282 printf("\n");
2283}
2284
febeb0c0
JB
2285void print_ies(unsigned char *ie, int ielen, bool unknown,
2286 enum print_ie_type ptype)
3563f4c5 2287{
5f310d83
SM
2288 struct print_ies_data ie_buffer = {
2289 .ie = ie,
2290 .ielen = ielen };
2291
cb13b9df
MT
2292 if (ie == NULL || ielen < 0)
2293 return;
2294
2295 while (ielen >= 2 && ielen - 2 >= ie[1]) {
febeb0c0
JB
2296 if (ie[0] < ARRAY_SIZE(ieprinters) &&
2297 ieprinters[ie[0]].name &&
2298 ieprinters[ie[0]].flags & BIT(ptype)) {
5f310d83
SM
2299 print_ie(&ieprinters[ie[0]],
2300 ie[0], ie[1], ie + 2, &ie_buffer);
764fe753 2301 } else if (ie[0] == 221 /* vendor */) {
febeb0c0
JB
2302 print_vendor(ie[1], ie + 2, unknown, ptype);
2303 } else if (unknown) {
3563f4c5
JB
2304 int i;
2305
8086b700 2306 printf("\tUnknown IE (%d):", ie[0]);
3563f4c5 2307 for (i=0; i<ie[1]; i++)
8086b700 2308 printf(" %.2x", ie[2+i]);
3563f4c5
JB
2309 printf("\n");
2310 }
2311 ielen -= ie[1] + 2;
2312 ie += ie[1] + 2;
2313 }
2314}
2315
2e8b82c1
VK
2316static void print_capa_dmg(__u16 capa)
2317{
2318 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
2319 case WLAN_CAPABILITY_DMG_TYPE_AP:
2320 printf(" DMG_ESS");
2321 break;
2322 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
2323 printf(" DMG_PCP");
2324 break;
2325 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
2326 printf(" DMG_IBSS");
2327 break;
2328 }
2329
2330 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
2331 printf(" CBAP_Only");
2332 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
2333 printf(" CBAP_Src");
2334 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
2335 printf(" Privacy");
2336 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
2337 printf(" ECPAC");
2338 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
2339 printf(" SpectrumMgmt");
2340 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
2341 printf(" RadioMeasure");
2342}
2343
2344static void print_capa_non_dmg(__u16 capa)
2345{
2346 if (capa & WLAN_CAPABILITY_ESS)
2347 printf(" ESS");
2348 if (capa & WLAN_CAPABILITY_IBSS)
2349 printf(" IBSS");
2350 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
2351 printf(" CfPollable");
2352 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
2353 printf(" CfPollReq");
2354 if (capa & WLAN_CAPABILITY_PRIVACY)
2355 printf(" Privacy");
2356 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
2357 printf(" ShortPreamble");
2358 if (capa & WLAN_CAPABILITY_PBCC)
2359 printf(" PBCC");
2360 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
2361 printf(" ChannelAgility");
2362 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
2363 printf(" SpectrumMgmt");
2364 if (capa & WLAN_CAPABILITY_QOS)
2365 printf(" QoS");
2366 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2367 printf(" ShortSlotTime");
2368 if (capa & WLAN_CAPABILITY_APSD)
2369 printf(" APSD");
2370 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
2371 printf(" RadioMeasure");
2372 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
2373 printf(" DSSS-OFDM");
2374 if (capa & WLAN_CAPABILITY_DEL_BACK)
2375 printf(" DelayedBACK");
2376 if (capa & WLAN_CAPABILITY_IMM_BACK)
2377 printf(" ImmediateBACK");
2378}
2379
3563f4c5
JB
2380static int print_bss_handler(struct nl_msg *msg, void *arg)
2381{
2382 struct nlattr *tb[NL80211_ATTR_MAX + 1];
2383 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
2384 struct nlattr *bss[NL80211_BSS_MAX + 1];
2385 char mac_addr[20], dev[20];
2386 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
2387 [NL80211_BSS_TSF] = { .type = NLA_U64 },
2388 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
2389 [NL80211_BSS_BSSID] = { },
2390 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
2391 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
2392 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
f2e17e1f
JB
2393 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
2394 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
a56117a6 2395 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
c04a78df 2396 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
575280cc 2397 [NL80211_BSS_BEACON_IES] = { },
3563f4c5 2398 };
febeb0c0 2399 struct scan_params *params = arg;
1c5bcd9c 2400 int show = params->show_both_ie_sets ? 2 : 1;
2e8b82c1 2401 bool is_dmg = false;
3563f4c5
JB
2402
2403 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
2404 genlmsg_attrlen(gnlh, 0), NULL);
2405
2406 if (!tb[NL80211_ATTR_BSS]) {
5fe70c0e 2407 fprintf(stderr, "bss info missing!\n");
3563f4c5
JB
2408 return NL_SKIP;
2409 }
2410 if (nla_parse_nested(bss, NL80211_BSS_MAX,
2411 tb[NL80211_ATTR_BSS],
2412 bss_policy)) {
5fe70c0e 2413 fprintf(stderr, "failed to parse nested attributes!\n");
3563f4c5
JB
2414 return NL_SKIP;
2415 }
2416
2417 if (!bss[NL80211_BSS_BSSID])
2418 return NL_SKIP;
2419
2420 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
00889fb2
JB
2421 printf("BSS %s", mac_addr);
2422 if (tb[NL80211_ATTR_IFINDEX]) {
2423 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
2424 printf("(on %s)", dev);
2425 }
a56117a6
JB
2426
2427 if (bss[NL80211_BSS_STATUS]) {
2428 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
2429 case NL80211_BSS_STATUS_AUTHENTICATED:
2430 printf(" -- authenticated");
2431 break;
2432 case NL80211_BSS_STATUS_ASSOCIATED:
2433 printf(" -- associated");
2434 break;
2435 case NL80211_BSS_STATUS_IBSS_JOINED:
2436 printf(" -- joined");
2437 break;
2438 default:
2439 printf(" -- unknown status: %d",
2440 nla_get_u32(bss[NL80211_BSS_STATUS]));
2441 break;
2442 }
2443 }
2444 printf("\n");
3563f4c5 2445
f6fca12c
JB
2446 if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
2447 unsigned long long bt;
2448 bt = (unsigned long long)nla_get_u64(bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
2449 printf("\tlast seen: %llu.%.3llus [boottime]\n", bt/1000000000, (bt%1000000000)/1000000);
2450 }
2451
e7109a8a
JB
2452 if (bss[NL80211_BSS_TSF]) {
2453 unsigned long long tsf;
2454 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
2455 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
2456 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
2457 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
2458 }
2e8b82c1
VK
2459 if (bss[NL80211_BSS_FREQUENCY]) {
2460 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
2461 printf("\tfreq: %d\n", freq);
2462 if (freq > 45000)
2463 is_dmg = true;
2464 }
3563f4c5 2465 if (bss[NL80211_BSS_BEACON_INTERVAL])
792172b0 2466 printf("\tbeacon interval: %d TUs\n",
3563f4c5 2467 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
92a04ecd
MH
2468 if (bss[NL80211_BSS_CAPABILITY]) {
2469 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
2470 printf("\tcapability:");
2e8b82c1
VK
2471 if (is_dmg)
2472 print_capa_dmg(capa);
2473 else
2474 print_capa_non_dmg(capa);
92a04ecd
MH
2475 printf(" (0x%.4x)\n", capa);
2476 }
f2e17e1f
JB
2477 if (bss[NL80211_BSS_SIGNAL_MBM]) {
2478 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
2479 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
2480 }
2481 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
2482 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
2483 printf("\tsignal: %d/100\n", s);
2484 }
c04a78df
HS
2485 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
2486 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
2487 printf("\tlast seen: %d ms ago\n", age);
2488 }
c551449a 2489
1c5bcd9c 2490 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
b50d1456
JB
2491 struct nlattr *ies = bss[NL80211_BSS_INFORMATION_ELEMENTS];
2492 struct nlattr *bcnies = bss[NL80211_BSS_BEACON_IES];
2493
2494 if (bss[NL80211_BSS_PRESP_DATA] ||
86d907a3 2495 (bcnies && (nla_len(ies) != nla_len(bcnies) ||
c9c3b69d
JB
2496 memcmp(nla_data(ies), nla_data(bcnies),
2497 nla_len(ies)))))
575280cc
JM
2498 printf("\tInformation elements from Probe Response "
2499 "frame:\n");
b50d1456 2500 print_ies(nla_data(ies), nla_len(ies),
febeb0c0 2501 params->unknown, params->type);
575280cc 2502 }
1c5bcd9c 2503 if (bss[NL80211_BSS_BEACON_IES] && show--) {
575280cc
JM
2504 printf("\tInformation elements from Beacon frame:\n");
2505 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
2506 nla_len(bss[NL80211_BSS_BEACON_IES]),
2507 params->unknown, params->type);
2508 }
3563f4c5
JB
2509
2510 return NL_SKIP;
2511}
2512
764fe753 2513static struct scan_params scan_params;
3563f4c5 2514
7c37a24d 2515static int handle_scan_dump(struct nl80211_state *state,
3563f4c5 2516 struct nl_msg *msg,
05514f95
JB
2517 int argc, char **argv,
2518 enum id_input id)
3563f4c5 2519{
764fe753
JB
2520 if (argc > 1)
2521 return 1;
2522
1c5bcd9c
JB
2523 memset(&scan_params, 0, sizeof(scan_params));
2524
764fe753
JB
2525 if (argc == 1 && !strcmp(argv[0], "-u"))
2526 scan_params.unknown = true;
575280cc
JM
2527 else if (argc == 1 && !strcmp(argv[0], "-b"))
2528 scan_params.show_both_ie_sets = true;
764fe753 2529
febeb0c0
JB
2530 scan_params.type = PRINT_SCAN;
2531
f3c96669 2532 register_handler(print_bss_handler, &scan_params);
3563f4c5
JB
2533 return 0;
2534}
a5fe4ef2
JB
2535
2536static int handle_scan_combined(struct nl80211_state *state,
a5fe4ef2 2537 struct nl_msg *msg,
05514f95
JB
2538 int argc, char **argv,
2539 enum id_input id)
a5fe4ef2 2540{
559a1713 2541 char **trig_argv;
a5fe4ef2
JB
2542 static char *dump_argv[] = {
2543 NULL,
2544 "scan",
2545 "dump",
92649eab 2546 NULL,
a5fe4ef2
JB
2547 };
2548 static const __u32 cmds[] = {
2549 NL80211_CMD_NEW_SCAN_RESULTS,
2550 NL80211_CMD_SCAN_ABORTED,
2551 };
559a1713 2552 int trig_argc, dump_argc, err;
7d19e35d 2553 int i;
a5fe4ef2 2554
559a1713
JB
2555 if (argc >= 3 && !strcmp(argv[2], "-u")) {
2556 dump_argc = 4;
2557 dump_argv[3] = "-u";
575280cc
JM
2558 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
2559 dump_argc = 4;
2560 dump_argv[3] = "-b";
559a1713
JB
2561 } else
2562 dump_argc = 3;
2563
2564 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
2565 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
2566 if (!trig_argv)
2567 return -ENOMEM;
a5fe4ef2 2568 trig_argv[0] = argv[0];
559a1713
JB
2569 trig_argv[1] = "scan";
2570 trig_argv[2] = "trigger";
7d19e35d 2571
559a1713
JB
2572 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
2573 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
75f4204c 2574 err = handle_cmd(state, id, trig_argc, trig_argv);
559a1713 2575 free(trig_argv);
a5fe4ef2
JB
2576 if (err)
2577 return err;
2578
61725dbe
JB
2579 /*
2580 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
2581 *
2582 * This code has a bug, which requires creating a separate
2583 * nl80211 socket to fix:
2584 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
2585 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
2586 * before (!) we listen to it, because we only start listening
2587 * after we send our scan request.
2588 *
2589 * Doing it the other way around has a race condition as well,
2590 * if you first open the events socket you may get a notification
2591 * for a previous scan.
2592 *
2593 * The only proper way to fix this would be to listen to events
2594 * before sending the command, and for the kernel to send the
2595 * scan request along with the event, so that you can match up
2596 * whether the scan you requested was finished or aborted (this
2597 * may result in processing a scan that another application
2598 * requested, but that doesn't seem to be a problem).
2599 *
2600 * Alas, the kernel doesn't do that (yet).
2601 */
2602
a5fe4ef2
JB
2603 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
2604 NL80211_CMD_SCAN_ABORTED) {
2605 printf("scan aborted!\n");
2606 return 0;
2607 }
2608
2609 dump_argv[0] = argv[0];
75f4204c 2610 return handle_cmd(state, id, dump_argc, dump_argv);
a5fe4ef2 2611}
3a99ff6d 2612TOPLEVEL(scan, "[-u] [freq <freq>*] [duration <dur>] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force,duration-mandatory] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]", 0, 0,
6ca98d28
JB
2613 CIB_NETDEV, handle_scan_combined,
2614 "Scan on the given frequencies and probe for the given SSIDs\n"
2615 "(or wildcard if not given) unless passive scanning is requested.\n"
64797a7f
JB
2616 "If -u is specified print unknown data in the scan results.\n"
2617 "Specified (vendor) IEs must be well-formed.");
4698bfc2
JB
2618COMMAND(scan, dump, "[-u]",
2619 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
2620 "Dump the current scan results. If -u is specified, print unknown\n"
2621 "data in scan results.");
3a99ff6d 2622COMMAND(scan, trigger, "[freq <freq>*] [duration <dur>] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force,duration-mandatory] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
4698bfc2
JB
2623 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
2624 "Trigger a scan on the given frequencies with probing for the given\n"
3a99ff6d
PKC
2625 "SSIDs (or wildcard if not given) unless passive scanning is requested.\n"
2626 "Duration(in TUs), if specified, will be used to set dwell times.\n");
3fce58aa
LC
2627
2628
0a12cf8d
EP
2629static int handle_scan_abort(struct nl80211_state *state,
2630 struct nl_msg *msg,
2631 int argc, char **argv,
2632 enum id_input id)
2633{
2634 return 0;
2635}
2636COMMAND(scan, abort, "",
2637 NL80211_CMD_ABORT_SCAN, 0, CIB_NETDEV, handle_scan_abort,
2638 "Abort ongoing scan");
2639
3fce58aa 2640static int handle_start_sched_scan(struct nl80211_state *state,
34b23014 2641 struct nl_msg *msg,
3fce58aa
LC
2642 int argc, char **argv, enum id_input id)
2643{
2644 return parse_sched_scan(msg, &argc, &argv);
2645}
2646
34b23014 2647static int handle_stop_sched_scan(struct nl80211_state *state,
3fce58aa
LC
2648 struct nl_msg *msg, int argc, char **argv,
2649 enum id_input id)
2650{
2651 if (argc != 0)
2652 return 1;
2653
2654 return 0;
2655}
2656
2657COMMAND(scan, sched_start,
2658 SCHED_SCAN_OPTIONS,
2659 NL80211_CMD_START_SCHED_SCAN, 0, CIB_NETDEV, handle_start_sched_scan,
2660 "Start a scheduled scan at the specified interval on the given frequencies\n"
2661 "with probing for the given SSIDs (or wildcard if not given) unless passive\n"
2662 "scanning is requested. If matches are specified, only matching results\n"
2663 "will be returned.");
2664COMMAND(scan, sched_stop, "",
2665 NL80211_CMD_STOP_SCHED_SCAN, 0, CIB_NETDEV, handle_stop_sched_scan,
2666 "Stop an ongoing scheduled scan.");