]> git.ipfire.org Git - thirdparty/iw.git/blame - scan.c
iw: scan: fix some format string warnings found by clang
[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
83b4934c 735static void print_cipher(const uint8_t *data)
857d966e 736{
3bd60ef1 737 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 738 switch (data[3]) {
510e0e2f 739 case 0:
857d966e
MH
740 printf("Use group cipher suite");
741 break;
510e0e2f 742 case 1:
857d966e
MH
743 printf("WEP-40");
744 break;
510e0e2f 745 case 2:
857d966e
MH
746 printf("TKIP");
747 break;
510e0e2f 748 case 4:
857d966e
MH
749 printf("CCMP");
750 break;
510e0e2f 751 case 5:
857d966e
MH
752 printf("WEP-104");
753 break;
754 default:
332769c6 755 printf("%.02x-%.02x-%.02x:%d",
5594fd23 756 data[0], data[1] ,data[2], data[3]);
857d966e
MH
757 break;
758 }
759 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
760 switch (data[3]) {
510e0e2f 761 case 0:
857d966e
MH
762 printf("Use group cipher suite");
763 break;
510e0e2f 764 case 1:
857d966e
MH
765 printf("WEP-40");
766 break;
510e0e2f 767 case 2:
857d966e
MH
768 printf("TKIP");
769 break;
510e0e2f 770 case 4:
857d966e
MH
771 printf("CCMP");
772 break;
510e0e2f 773 case 5:
857d966e
MH
774 printf("WEP-104");
775 break;
510e0e2f 776 case 6:
857d966e
MH
777 printf("AES-128-CMAC");
778 break;
f188ab67
BG
779 case 7:
780 printf("NO-GROUP");
781 break;
a8b3da9d
VK
782 case 8:
783 printf("GCMP");
784 break;
857d966e 785 default:
332769c6 786 printf("%.02x-%.02x-%.02x:%d",
5594fd23 787 data[0], data[1] ,data[2], data[3]);
857d966e
MH
788 break;
789 }
790 } else
332769c6 791 printf("%.02x-%.02x-%.02x:%d",
5594fd23 792 data[0], data[1] ,data[2], data[3]);
857d966e
MH
793}
794
83b4934c 795static void print_auth(const uint8_t *data)
857d966e 796{
3bd60ef1 797 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 798 switch (data[3]) {
510e0e2f 799 case 1:
857d966e
MH
800 printf("IEEE 802.1X");
801 break;
510e0e2f 802 case 2:
857d966e
MH
803 printf("PSK");
804 break;
805 default:
332769c6 806 printf("%.02x-%.02x-%.02x:%d",
5594fd23 807 data[0], data[1] ,data[2], data[3]);
857d966e
MH
808 break;
809 }
810 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
811 switch (data[3]) {
510e0e2f 812 case 1:
857d966e
MH
813 printf("IEEE 802.1X");
814 break;
510e0e2f 815 case 2:
857d966e
MH
816 printf("PSK");
817 break;
0fe1c415
JB
818 case 3:
819 printf("FT/IEEE 802.1X");
820 break;
821 case 4:
822 printf("FT/PSK");
823 break;
824 case 5:
825 printf("IEEE 802.1X/SHA-256");
826 break;
827 case 6:
828 printf("PSK/SHA-256");
829 break;
4361eef3
JM
830 case 7:
831 printf("TDLS/TPK");
832 break;
a58f492d
HM
833 case 8:
834 printf("SAE");
835 break;
836 case 9:
837 printf("FT/SAE");
838 break;
839 case 11:
840 printf("IEEE 802.1X/SUITE-B");
841 break;
842 case 12:
843 printf("IEEE 802.1X/SUITE-B-192");
844 break;
845 case 13:
846 printf("FT/IEEE 802.1X/SHA-384");
847 break;
848 case 14:
849 printf("FILS/SHA-256");
850 break;
851 case 15:
852 printf("FILS/SHA-384");
853 break;
854 case 16:
855 printf("FT/FILS/SHA-256");
856 break;
857 case 17:
858 printf("FT/FILS/SHA-384");
859 break;
860 case 18:
861 printf("OWE");
862 break;
857d966e 863 default:
332769c6 864 printf("%.02x-%.02x-%.02x:%d",
5594fd23 865 data[0], data[1] ,data[2], data[3]);
857d966e
MH
866 break;
867 }
f188ab67
BG
868 } else if (memcmp(data, wfa_oui, 3) == 0) {
869 switch (data[3]) {
870 case 1:
871 printf("OSEN");
872 break;
a58f492d
HM
873 case 2:
874 printf("DPP");
875 break;
f188ab67
BG
876 default:
877 printf("%.02x-%.02x-%.02x:%d",
878 data[0], data[1] ,data[2], data[3]);
879 break;
880 }
857d966e 881 } else
332769c6 882 printf("%.02x-%.02x-%.02x:%d",
5594fd23 883 data[0], data[1] ,data[2], data[3]);
857d966e
MH
884}
885
f188ab67
BG
886static void _print_rsn_ie(const char *defcipher, const char *defauth,
887 uint8_t len, const uint8_t *data, int is_osen)
857d966e
MH
888{
889 bool first = true;
f188ab67 890 __u16 count, capa;
857d966e
MH
891 int i;
892
f188ab67
BG
893 if (!is_osen) {
894 __u16 version;
895 version = data[0] + (data[1] << 8);
896 tab_on_first(&first);
897 printf("\t * Version: %d\n", version);
857d966e 898
f188ab67
BG
899 data += 2;
900 len -= 2;
901 }
857d966e
MH
902
903 if (len < 4) {
904 tab_on_first(&first);
905 printf("\t * Group cipher: %s\n", defcipher);
906 printf("\t * Pairwise ciphers: %s\n", defcipher);
907 return;
908 }
909
910 tab_on_first(&first);
911 printf("\t * Group cipher: ");
912 print_cipher(data);
913 printf("\n");
914
915 data += 4;
916 len -= 4;
917
918 if (len < 2) {
919 tab_on_first(&first);
920 printf("\t * Pairwise ciphers: %s\n", defcipher);
921 return;
922 }
923
924 count = data[0] | (data[1] << 8);
31d477fb
MH
925 if (2 + (count * 4) > len)
926 goto invalid;
927
857d966e
MH
928 tab_on_first(&first);
929 printf("\t * Pairwise ciphers:");
31d477fb 930 for (i = 0; i < count; i++) {
857d966e
MH
931 printf(" ");
932 print_cipher(data + 2 + (i * 4));
933 }
934 printf("\n");
935
936 data += 2 + (count * 4);
937 len -= 2 + (count * 4);
938
939 if (len < 2) {
940 tab_on_first(&first);
941 printf("\t * Authentication suites: %s\n", defauth);
942 return;
943 }
944
945 count = data[0] | (data[1] << 8);
31d477fb
MH
946 if (2 + (count * 4) > len)
947 goto invalid;
948
857d966e
MH
949 tab_on_first(&first);
950 printf("\t * Authentication suites:");
83b4934c 951 for (i = 0; i < count; i++) {
857d966e
MH
952 printf(" ");
953 print_auth(data + 2 + (i * 4));
954 }
955 printf("\n");
956
957 data += 2 + (count * 4);
958 len -= 2 + (count * 4);
959
6a4f24e8
JB
960 if (len >= 2) {
961 capa = data[0] | (data[1] << 8);
962 tab_on_first(&first);
cadbe89a
JB
963 printf("\t * Capabilities:");
964 if (capa & 0x0001)
965 printf(" PreAuth");
966 if (capa & 0x0002)
967 printf(" NoPairwise");
968 switch ((capa & 0x000c) >> 2) {
969 case 0:
4361eef3 970 printf(" 1-PTKSA-RC");
cadbe89a
JB
971 break;
972 case 1:
973 printf(" 2-PTKSA-RC");
974 break;
975 case 2:
976 printf(" 4-PTKSA-RC");
977 break;
978 case 3:
979 printf(" 16-PTKSA-RC");
980 break;
981 }
982 switch ((capa & 0x0030) >> 4) {
983 case 0:
4361eef3 984 printf(" 1-GTKSA-RC");
cadbe89a
JB
985 break;
986 case 1:
987 printf(" 2-GTKSA-RC");
988 break;
989 case 2:
990 printf(" 4-GTKSA-RC");
991 break;
992 case 3:
993 printf(" 16-GTKSA-RC");
994 break;
995 }
996 if (capa & 0x0040)
997 printf(" MFP-required");
998 if (capa & 0x0080)
999 printf(" MFP-capable");
1000 if (capa & 0x0200)
1001 printf(" Peerkey-enabled");
1002 if (capa & 0x0400)
1003 printf(" SPP-AMSDU-capable");
1004 if (capa & 0x0800)
1005 printf(" SPP-AMSDU-required");
1006 printf(" (0x%.4x)\n", capa);
6a4f24e8
JB
1007 data += 2;
1008 len -= 2;
1009 }
31d477fb 1010
5ba3f523
JB
1011 if (len >= 2) {
1012 int pmkid_count = data[0] | (data[1] << 8);
1013
1014 if (len >= 2 + 16 * pmkid_count) {
1015 tab_on_first(&first);
1016 printf("\t * %d PMKIDs\n", pmkid_count);
1017 /* not printing PMKID values */
1018 data += 2 + 16 * pmkid_count;
1019 len -= 2 + 16 * pmkid_count;
1020 } else
1021 goto invalid;
1022 }
1023
1024 if (len >= 4) {
1025 tab_on_first(&first);
1026 printf("\t * Group mgmt cipher suite: ");
1027 print_cipher(data);
1028 printf("\n");
1029 data += 4;
1030 len -= 4;
1031 }
1032
cadbe89a 1033 invalid:
31d477fb
MH
1034 if (len != 0) {
1035 printf("\t\t * bogus tail data (%d):", len);
1036 while (len) {
1037 printf(" %.2x", *data);
1038 data++;
1039 len--;
1040 }
1041 printf("\n");
1042 }
857d966e
MH
1043}
1044
f188ab67
BG
1045static void print_rsn_ie(const char *defcipher, const char *defauth,
1046 uint8_t len, const uint8_t *data)
1047{
1048 _print_rsn_ie(defcipher, defauth, len, data, 0);
1049}
1050
1051static void print_osen_ie(const char *defcipher, const char *defauth,
1052 uint8_t len, const uint8_t *data)
1053{
1054 printf("\n\t");
1055 _print_rsn_ie(defcipher, defauth, len, data, 1);
1056}
1057
5f310d83
SM
1058static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data,
1059 const struct print_ies_data *ie_buffer)
857d966e 1060{
83b4934c 1061 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
857d966e
MH
1062}
1063
5f310d83
SM
1064static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
1065 const struct print_ies_data *ie_buffer)
0c445c24 1066{
357c1a5d 1067 printf("\n");
7ddfb679
JB
1068 print_ht_capability(data[0] | (data[1] << 8));
1069 print_ampdu_length(data[2] & 3);
c79c7464 1070 print_ampdu_spacing((data[2] >> 2) & 7);
7ddfb679 1071 print_ht_mcs(data + 3);
0c445c24
LR
1072}
1073
489f0ea9
BG
1074static const char* ntype_11u(uint8_t t)
1075{
1076 switch (t) {
1077 case 0: return "Private";
1078 case 1: return "Private with Guest";
1079 case 2: return "Chargeable Public";
1080 case 3: return "Free Public";
1081 case 4: return "Personal Device";
1082 case 5: return "Emergency Services Only";
1083 case 14: return "Test or Experimental";
1084 case 15: return "Wildcard";
1085 default: return "Reserved";
1086 }
1087}
1088
1089static const char* vgroup_11u(uint8_t t)
1090{
1091 switch (t) {
1092 case 0: return "Unspecified";
1093 case 1: return "Assembly";
1094 case 2: return "Business";
1095 case 3: return "Educational";
1096 case 4: return "Factory and Industrial";
1097 case 5: return "Institutional";
1098 case 6: return "Mercantile";
1099 case 7: return "Residential";
1100 case 8: return "Storage";
1101 case 9: return "Utility and Miscellaneous";
1102 case 10: return "Vehicular";
1103 case 11: return "Outdoor";
1104 default: return "Reserved";
1105 }
1106}
1107
5f310d83
SM
1108static void print_interworking(const uint8_t type, uint8_t len,
1109 const uint8_t *data,
1110 const struct print_ies_data *ie_buffer)
489f0ea9
BG
1111{
1112 /* See Section 7.3.2.92 in the 802.11u spec. */
1113 printf("\n");
1114 if (len >= 1) {
1115 uint8_t ano = data[0];
1116 printf("\t\tNetwork Options: 0x%hx\n", (unsigned short)(ano));
1117 printf("\t\t\tNetwork Type: %i (%s)\n",
1118 (int)(ano & 0xf), ntype_11u(ano & 0xf));
1119 if (ano & (1<<4))
1120 printf("\t\t\tInternet\n");
1121 if (ano & (1<<5))
1122 printf("\t\t\tASRA\n");
1123 if (ano & (1<<6))
1124 printf("\t\t\tESR\n");
1125 if (ano & (1<<7))
1126 printf("\t\t\tUESA\n");
1127 }
1128 if ((len == 3) || (len == 9)) {
1129 printf("\t\tVenue Group: %i (%s)\n",
1130 (int)(data[1]), vgroup_11u(data[1]));
1131 printf("\t\tVenue Type: %i\n", (int)(data[2]));
1132 }
1133 if (len == 9)
6e0ce463 1134 printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
489f0ea9
BG
1135 data[3], data[4], data[5], data[6], data[7], data[8]);
1136 else if (len == 7)
6e0ce463 1137 printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
489f0ea9
BG
1138 data[1], data[2], data[3], data[4], data[5], data[6]);
1139}
1140
5f310d83
SM
1141static void print_11u_advert(const uint8_t type, uint8_t len,
1142 const uint8_t *data,
1143 const struct print_ies_data *ie_buffer)
3f818f6d
BG
1144{
1145 /* See Section 7.3.2.93 in the 802.11u spec. */
1146 /* TODO: This code below does not decode private protocol IDs */
1147 int idx = 0;
1148 printf("\n");
1149 while (idx < (len - 1)) {
1150 uint8_t qri = data[idx];
1151 uint8_t proto_id = data[idx + 1];
1152 printf("\t\tQuery Response Info: 0x%hx\n", (unsigned short)(qri));
1153 printf("\t\t\tQuery Response Length Limit: %i\n",
1154 (qri & 0x7f));
1155 if (qri & (1<<7))
1156 printf("\t\t\tPAME-BI\n");
1157 switch(proto_id) {
1158 case 0:
1159 printf("\t\t\tANQP\n"); break;
1160 case 1:
1161 printf("\t\t\tMIH Information Service\n"); break;
1162 case 2:
1163 printf("\t\t\tMIH Command and Event Services Capability Discovery\n"); break;
1164 case 3:
1165 printf("\t\t\tEmergency Alert System (EAS)\n"); break;
1166 case 221:
1167 printf("\t\t\tVendor Specific\n"); break;
1168 default:
1169 printf("\t\t\tReserved: %i\n", proto_id); break;
1170 }
1171 idx += 2;
1172 }
1173}
1174
5f310d83
SM
1175static void print_11u_rcon(const uint8_t type, uint8_t len, const uint8_t *data,
1176 const struct print_ies_data *ie_buffer)
bc9e14f1
BG
1177{
1178 /* See Section 7.3.2.96 in the 802.11u spec. */
1179 int idx = 0;
1180 int ln0 = data[1] & 0xf;
1181 int ln1 = ((data[1] & 0xf0) >> 4);
1182 int ln2 = 0;
1183 printf("\n");
1184
1185 if (ln1)
1186 ln2 = len - 2 - ln0 - ln1;
1187
1188 printf("\t\tANQP OIs: %i\n", data[0]);
1189
1190 if (ln0 > 0) {
1191 printf("\t\tOI 1: ");
1192 if (2 + ln0 > len) {
1193 printf("Invalid IE length.\n");
1194 } else {
1195 for (idx = 0; idx < ln0; idx++) {
6e0ce463 1196 printf("%02hhx", data[2 + idx]);
bc9e14f1
BG
1197 }
1198 printf("\n");
1199 }
1200 }
1201
1202 if (ln1 > 0) {
1203 printf("\t\tOI 2: ");
1204 if (2 + ln0 + ln1 > len) {
1205 printf("Invalid IE length.\n");
1206 } else {
1207 for (idx = 0; idx < ln1; idx++) {
6e0ce463 1208 printf("%02hhx", data[2 + ln0 + idx]);
bc9e14f1
BG
1209 }
1210 printf("\n");
1211 }
1212 }
1213
1214 if (ln2 > 0) {
1215 printf("\t\tOI 3: ");
1216 if (2 + ln0 + ln1 + ln2 > len) {
1217 printf("Invalid IE length.\n");
1218 } else {
1219 for (idx = 0; idx < ln2; idx++) {
6e0ce463 1220 printf("%02hhx", data[2 + ln0 + ln1 + idx]);
bc9e14f1
BG
1221 }
1222 printf("\n");
1223 }
1224 }
1225}
1226
2dfd1f0a
MT
1227static void print_tx_power_envelope(const uint8_t type, uint8_t len,
1228 const uint8_t *data,
1229 const struct print_ies_data *ie_buffer)
1230{
1231 const uint8_t local_max_tx_power_count = data[0] & 7;
1232 const uint8_t local_max_tx_power_unit_interp = (data[0] >> 3) & 7;
1233 int i;
1234 static const char *power_names[] = {
1235 "Local Maximum Transmit Power For 20 MHz",
1236 "Local Maximum Transmit Power For 40 MHz",
1237 "Local Maximum Transmit Power For 80 MHz",
1238 "Local Maximum Transmit Power For 160/80+80 MHz",
1239 };
1240
1241 printf("\n");
1242
1243 if (local_max_tx_power_count + 2 != len)
1244 return;
1245 if (local_max_tx_power_unit_interp != 0)
1246 return;
1247 for (i = 0; i < local_max_tx_power_count + 1; ++i) {
1248 int8_t power_val = ((int8_t)data[1 + i]) >> 1;
1249 int8_t point5 = data[1 + i] & 1;
1250 if (point5)
1251 printf("\t\t * %s: %i.5 dBm\n", power_names[i], power_val);
1252 else
1253 printf("\t\t * %s: %i dBm\n", power_names[i], power_val);
1254 }
1255}
1256
29f7579e
JB
1257static const char *ht_secondary_offset[4] = {
1258 "no secondary",
1259 "above",
1260 "[reserved!]",
1261 "below",
1262};
1263
5f310d83
SM
1264static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data,
1265 const struct print_ies_data *ie_buffer)
be7602fb 1266{
be7602fb
JB
1267 static const char *protection[4] = {
1268 "no",
1269 "nonmember",
1270 "20 MHz",
1271 "non-HT mixed",
1272 };
1273 static const char *sta_chan_width[2] = {
1274 "20 MHz",
1275 "any",
1276 };
1277
1278 printf("\n");
1279 printf("\t\t * primary channel: %d\n", data[0]);
1280 printf("\t\t * secondary channel offset: %s\n",
29f7579e 1281 ht_secondary_offset[data[1] & 0x3]);
be7602fb
JB
1282 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
1283 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
1284 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
1285 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
1286 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
1287 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
1288 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
1289 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
1290 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
1291 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
1292 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
1293}
1294
5f310d83
SM
1295static void print_capabilities(const uint8_t type, uint8_t len,
1296 const uint8_t *data,
1297 const struct print_ies_data *ie_buffer)
9b880b00 1298{
5f310d83
SM
1299 int i, base, bit, si_duration = 0, max_amsdu = 0;
1300 bool s_psmp_support = false, is_vht_cap = false;
1301 unsigned char *ie = ie_buffer->ie;
1302 int ielen = ie_buffer->ielen;
31d2d259 1303
5f310d83
SM
1304 while (ielen >= 2 && ielen >= ie[1]) {
1305 if (ie[0] == 191) {
1306 is_vht_cap = true;
1307 break;
1308 }
1309 ielen -= ie[1] + 2;
1310 ie += ie[1] + 2;
1311 }
31d2d259
JB
1312
1313 for (i = 0; i < len; i++) {
1314 base = i * 8;
1315
1316 for (bit = 0; bit < 8; bit++) {
1317 if (!(data[i] & (1 << bit)))
1318 continue;
1319
5f310d83 1320 printf("\n\t\t *");
31d2d259 1321
70c649d2
JB
1322#define CAPA(bit, name) case bit: printf(" " name); break
1323
5f310d83
SM
1324/* if the capability 'cap' exists add 'val' to 'sum'
1325 * otherwise print 'Reserved' */
1326#define ADD_BIT_VAL(bit, cap, sum, val) case (bit): do { \
1327 if (!(cap)) { \
1328 printf(" Reserved"); \
1329 break; \
1330 } \
1331 sum += val; \
1332 break; \
1333} while (0)
1334
31d2d259 1335 switch (bit + base) {
70c649d2
JB
1336 CAPA(0, "HT Information Exchange Supported");
1337 CAPA(1, "reserved (On-demand Beacon)");
1338 CAPA(2, "Extended Channel Switching");
1339 CAPA(3, "reserved (Wave Indication)");
1340 CAPA(4, "PSMP Capability");
1341 CAPA(5, "reserved (Service Interval Granularity)");
5f310d83
SM
1342
1343 case 6:
1344 s_psmp_support = true;
1345 printf(" S-PSMP Capability");
1346 break;
1347
70c649d2
JB
1348 CAPA(7, "Event");
1349 CAPA(8, "Diagnostics");
1350 CAPA(9, "Multicast Diagnostics");
1351 CAPA(10, "Location Tracking");
1352 CAPA(11, "FMS");
1353 CAPA(12, "Proxy ARP Service");
1354 CAPA(13, "Collocated Interference Reporting");
1355 CAPA(14, "Civic Location");
1356 CAPA(15, "Geospatial Location");
1357 CAPA(16, "TFS");
1358 CAPA(17, "WNM-Sleep Mode");
1359 CAPA(18, "TIM Broadcast");
1360 CAPA(19, "BSS Transition");
1361 CAPA(20, "QoS Traffic Capability");
1362 CAPA(21, "AC Station Count");
1363 CAPA(22, "Multiple BSSID");
1364 CAPA(23, "Timing Measurement");
1365 CAPA(24, "Channel Usage");
1366 CAPA(25, "SSID List");
1367 CAPA(26, "DMS");
1368 CAPA(27, "UTC TSF Offset");
1369 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
1370 CAPA(29, "TDLS Peer PSM Support");
1371 CAPA(30, "TDLS channel switching");
1372 CAPA(31, "Interworking");
1373 CAPA(32, "QoS Map");
1374 CAPA(33, "EBR");
1375 CAPA(34, "SSPN Interface");
1376 CAPA(35, "Reserved");
1377 CAPA(36, "MSGCF Capability");
1378 CAPA(37, "TDLS Support");
1379 CAPA(38, "TDLS Prohibited");
1380 CAPA(39, "TDLS Channel Switching Prohibited");
1381 CAPA(40, "Reject Unadmitted Frame");
5f310d83
SM
1382
1383 ADD_BIT_VAL(41, s_psmp_support, si_duration, 1);
1384 ADD_BIT_VAL(42, s_psmp_support, si_duration, 2);
1385 ADD_BIT_VAL(43, s_psmp_support, si_duration, 4);
1386
70c649d2
JB
1387 CAPA(44, "Identifier Location");
1388 CAPA(45, "U-APSD Coexistence");
1389 CAPA(46, "WNM-Notification");
1390 CAPA(47, "Reserved");
1391 CAPA(48, "UTF-8 SSID");
5f310d83
SM
1392 CAPA(49, "QMFActivated");
1393 CAPA(50, "QMFReconfigurationActivated");
1394 CAPA(51, "Robust AV Streaming");
1395 CAPA(52, "Advanced GCR");
1396 CAPA(53, "Mesh GCR");
1397 CAPA(54, "SCS");
1398 CAPA(55, "QLoad Report");
1399 CAPA(56, "Alternate EDCA");
1400 CAPA(57, "Unprotected TXOP Negotiation");
1401 CAPA(58, "Protected TXOP egotiation");
1402 CAPA(59, "Reserved");
1403 CAPA(60, "Protected QLoad Report");
1404 CAPA(61, "TDLS Wider Bandwidth");
1405 CAPA(62, "Operating Mode Notification");
1406
1407 ADD_BIT_VAL(63, is_vht_cap, max_amsdu, 1);
1408 ADD_BIT_VAL(64, is_vht_cap, max_amsdu, 2);
1409
1410 CAPA(65, "Channel Schedule Management");
1411 CAPA(66, "Geodatabase Inband Enabling Signal");
1412 CAPA(67, "Network Channel Control");
1413 CAPA(68, "White Space Map");
1414 CAPA(69, "Channel Availability Query");
d06cda9e
AK
1415 CAPA(70, "FTM Responder");
1416 CAPA(71, "FTM Initiator");
5f310d83
SM
1417 CAPA(72, "Reserved");
1418 CAPA(73, "Extended Spectrum Management Capable");
1419 CAPA(74, "Reserved");
31d2d259
JB
1420 default:
1421 printf(" %d", bit);
1422 break;
1423 }
5f310d83 1424#undef ADD_BIT_VAL
70c649d2 1425#undef CAPA
31d2d259
JB
1426 }
1427 }
9b880b00 1428
5f310d83
SM
1429 if (s_psmp_support)
1430 printf("\n\t\t * Service Interval Granularity is %d ms",
1431 (si_duration + 1) * 5);
1432
1433 if (is_vht_cap) {
1434 printf("\n\t\t * Max Number Of MSDUs In A-MSDU is ");
1435 switch (max_amsdu) {
1436 case 0:
1437 printf("unlimited");
1438 break;
1439 case 1:
1440 printf("32");
1441 break;
1442 case 2:
1443 printf("16");
1444 break;
1445 case 3:
1446 printf("8");
1447 break;
1448 default:
1449 break;
1450 }
1451 }
1452
9b880b00
MH
1453 printf("\n");
1454}
1455
5f310d83
SM
1456static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data,
1457 const struct print_ies_data *ie_buffer)
575280cc
JM
1458{
1459 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
1460 "Bitmap[0] 0x%x",
1461 data[0], data[1], data[2], data[3]);
1462 if (len - 4)
1463 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
1464 printf("\n");
1465}
1466
5f310d83
SM
1467static void print_ibssatim(const uint8_t type, uint8_t len, const uint8_t *data,
1468 const struct print_ies_data *ie_buffer)
792172b0 1469{
08c3f7d3 1470 printf(" %d TUs\n", (data[1] << 8) + data[0]);
792172b0
JM
1471}
1472
5f310d83
SM
1473static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
1474 const struct print_ies_data *ie_buffer)
54eb1613
JB
1475{
1476 printf("\n");
a7375d5e
MT
1477 print_vht_info((__u32) data[0] | ((__u32)data[1] << 8) |
1478 ((__u32)data[2] << 16) | ((__u32)data[3] << 24),
54eb1613
JB
1479 data + 4);
1480}
1481
5f310d83
SM
1482static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data,
1483 const struct print_ies_data *ie_buffer)
ca159934
JB
1484{
1485 const char *chandwidths[] = {
1486 [0] = "20 or 40 MHz",
1487 [1] = "80 MHz",
1488 [3] = "80+80 MHz",
1489 [2] = "160 MHz",
1490 };
1491
1492 printf("\n");
1493 printf("\t\t * channel width: %d (%s)\n", data[0],
1494 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
1495 printf("\t\t * center freq segment 1: %d\n", data[1]);
1496 printf("\t\t * center freq segment 2: %d\n", data[2]);
1497 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
1498}
1499
3d343608
MT
1500static void print_supp_op_classes(const uint8_t type, uint8_t len,
1501 const uint8_t *data,
1502 const struct print_ies_data *ie_buffer)
1503{
1504 uint8_t *p = (uint8_t*) data;
1505 const uint8_t *next_data = p + len;
1506 int zero_delimiter = 0;
1507 int one_hundred_thirty_delimiter = 0;
1508
1509 printf("\n");
1510 printf("\t\t * current operating class: %d\n", *p);
1511 while (++p < next_data) {
1512 if (*p == 130) {
1513 one_hundred_thirty_delimiter = 1;
1514 break;
1515 }
1516 if (*p == 0) {
1517 zero_delimiter = 0;
1518 break;
1519 }
1520 printf("\t\t * operating class: %d\n", *p);
1521 }
1522 if (one_hundred_thirty_delimiter)
1523 while (++p < next_data) {
1524 printf("\t\t * current operating class extension: %d\n", *p);
1525 }
1526 if (zero_delimiter)
1527 while (++p < next_data - 1) {
1528 printf("\t\t * operating class tuple: %d %d\n", p[0], p[1]);
1529 if (*p == 0)
1530 break;
1531 }
1532}
1533
0e6d5446
MT
1534static void print_measurement_pilot_tx(const uint8_t type, uint8_t len,
1535 const uint8_t *data,
1536 const struct print_ies_data *ie_buffer)
1537{
1538 uint8_t *p, len_remaining;
1539
1540 printf("\n");
1541 printf("\t\t * interval: %d TUs\n", data[0]);
1542
1543 if(len <= 1)
1544 return;
1545
1546 p = (uint8_t *) data + 1;
1547 len_remaining = len - 1;
1548
1549 while (len_remaining >=5) {
1550 uint8_t subelement_id = *p, len, *end;
1551
1552 p++;
1553 len = *p;
1554 p++;
1555 end = p + len;
1556
1557 len_remaining -= 2;
1558
1559 /* 802.11-2016 only allows vendor specific elements */
1560 if (subelement_id != 221) {
1561 printf("\t\t * <Invalid subelement ID %d>\n", subelement_id);
1562 return;
1563 }
1564
1565 if (len < 3 || len > len_remaining) {
1566 printf(" <Parse error, element too short>\n");
1567 return;
1568 }
1569
1570 printf("\t\t * vendor specific: OUI %.2x:%.2x:%.2x, data:",
1571 p[0], p[1], p[2]);
1572 /* add only two here and use ++p in while loop */
1573 p += 2;
1574
1575 while (++p < end)
1576 printf(" %.2x", *p);
1577 printf("\n");
1578
1579 len_remaining -= len;
1580 }
1581}
1582
5f310d83
SM
1583static void print_obss_scan_params(const uint8_t type, uint8_t len,
1584 const uint8_t *data,
1585 const struct print_ies_data *ie_buffer)
29f7579e
JB
1586{
1587 printf("\n");
1588 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
1589 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
1590 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
1591 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
1592 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
1593 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
1594 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
1595 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
1596}
1597
5f310d83
SM
1598static void print_secchan_offs(const uint8_t type, uint8_t len,
1599 const uint8_t *data,
1600 const struct print_ies_data *ie_buffer)
29f7579e
JB
1601{
1602 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
1603 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
1604 else
1605 printf(" %d\n", data[0]);
1606}
1607
5f310d83
SM
1608static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data,
1609 const struct print_ies_data *ie_buffer)
29f7579e
JB
1610{
1611 printf("\n");
1612 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
1613 printf("\t\t * channel utilisation: %d/255\n", data[2]);
1614 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
1615}
1616
5f310d83
SM
1617static void print_mesh_conf(const uint8_t type, uint8_t len,
1618 const uint8_t *data,
1619 const struct print_ies_data *ie_buffer)
2d86b0f3
CYY
1620{
1621 printf("\n");
1622 printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
1623 printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
1624 printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
1625 printf("\t\t * Synchronization Method ID: %d\n", data[3]);
1626 printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
1627 printf("\t\t * Mesh Formation Info:\n");
1628 printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
1629 if (data[5] & 0x01)
1630 printf("\t\t\t Connected to Mesh Gate\n");
1631 if (data[5] & 0x80)
1632 printf("\t\t\t Connected to AS\n");
1633 printf("\t\t * Mesh Capability\n");
1634 if (data[6] & 0x01)
1635 printf("\t\t\t Accepting Additional Mesh Peerings\n");
1636 if (data[6] & 0x02)
1637 printf("\t\t\t MCCA Supported\n");
1638 if (data[6] & 0x04)
1639 printf("\t\t\t MCCA Enabled\n");
1640 if (data[6] & 0x08)
1641 printf("\t\t\t Forwarding\n");
1642 if (data[6] & 0x10)
1643 printf("\t\t\t MBCA Supported\n");
1644 if (data[6] & 0x20)
1645 printf("\t\t\t TBTT Adjusting\n");
1646 if (data[6] & 0x40)
1647 printf("\t\t\t Mesh Power Save Level\n");
1648}
1649
83b4934c
JB
1650struct ie_print {
1651 const char *name;
5f310d83
SM
1652 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data,
1653 const struct print_ies_data *ie_buffer);
83b4934c 1654 uint8_t minlen, maxlen;
febeb0c0 1655 uint8_t flags;
764fe753
JB
1656};
1657
5f310d83
SM
1658static void print_ie(const struct ie_print *p, const uint8_t type, uint8_t len,
1659 const uint8_t *data,
1660 const struct print_ies_data *ie_buffer)
4673a894 1661{
83b4934c
JB
1662 int i;
1663
1664 if (!p->print)
1665 return;
1666
1667 printf("\t%s:", p->name);
1668 if (len < p->minlen || len > p->maxlen) {
1669 if (len > 1) {
1670 printf(" <invalid: %d bytes:", len);
1671 for (i = 0; i < len; i++)
1672 printf(" %.02x", data[i]);
1673 printf(">\n");
1674 } else if (len)
1675 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
1676 else
1677 printf(" <invalid: no data>\n");
1678 return;
1679 }
1680
5f310d83 1681 p->print(type, len, data, ie_buffer);
83b4934c
JB
1682}
1683
1684#define PRINT_IGN { \
1685 .name = "IGNORE", \
1686 .print = NULL, \
1687 .minlen = 0, \
1688 .maxlen = 255, \
4673a894
JB
1689}
1690
83b4934c 1691static const struct ie_print ieprinters[] = {
febeb0c0
JB
1692 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
1693 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
014d581a 1694 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
575280cc 1695 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
792172b0 1696 [6] = { "IBSS ATIM window", print_ibssatim, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1697 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
29f7579e 1698 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
febeb0c0 1699 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
792172b0 1700 [35] = { "TPC report", print_tpcreport, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1701 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
a2e61861 1702 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
792172b0 1703 [47] = { "ERP D4.0", print_erp, 1, 255, BIT(PRINT_SCAN), },
3d343608 1704 [59] = { "Supported operating classes", print_supp_op_classes, 1, 255, BIT(PRINT_SCAN), },
0e6d5446 1705 [66] = { "Measurement Pilot Transmission", print_measurement_pilot_tx, 1, 255, BIT(PRINT_SCAN), },
29f7579e 1706 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
be7602fb 1707 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
29f7579e 1708 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
54eb1613 1709 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
ca159934 1710 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
febeb0c0
JB
1711 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
1712 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
4d72d1d2 1713 [70] = { "RM enabled capabilities", print_rm_enabled_capabilities, 5, 5, BIT(PRINT_SCAN), },
2d86b0f3 1714 [113] = { "MESH Configuration", print_mesh_conf, 7, 7, BIT(PRINT_SCAN), },
720583e9 1715 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
febeb0c0 1716 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
489f0ea9 1717 [107] = { "802.11u Interworking", print_interworking, 0, 255, BIT(PRINT_SCAN), },
3f818f6d 1718 [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
baea1a0b 1719 [111] = { "802.11u Roaming Consortium", print_11u_rcon, 2, 255, BIT(PRINT_SCAN), },
2dfd1f0a 1720 [195] = { "Transmit Power Envelope", print_tx_power_envelope, 2, 5, BIT(PRINT_SCAN), },
83b4934c
JB
1721};
1722
5f310d83
SM
1723static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data,
1724 const struct print_ies_data *ie_buffer)
83b4934c
JB
1725{
1726 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
1727}
1728
5f310d83
SM
1729static void print_wifi_osen(const uint8_t type, uint8_t len,
1730 const uint8_t *data,
1731 const struct print_ies_data *ie_buffer)
f188ab67
BG
1732{
1733 print_osen_ie("OSEN", "OSEN", len, data);
1734}
1735
1cab57eb
JB
1736static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
1737{
1738 int i;
1739 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
1740
1741 if (len < 19)
1742 goto invalid;
1743
1744 if (data[0] != 1) {
cee4fe20 1745 printf("Parameter: not version 1: ");
1cab57eb
JB
1746 return false;
1747 }
1748
89ea706f 1749 printf("\t * Parameter version 1");
1cab57eb
JB
1750
1751 data++;
1752
1753 if (data[0] & 0x80)
89ea706f 1754 printf("\n\t\t * u-APSD");
1cab57eb
JB
1755
1756 data += 2;
1757
1758 for (i = 0; i < 4; i++) {
89ea706f 1759 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
87181516 1760 if (data[0] & 0x10)
1cab57eb
JB
1761 printf(" acm");
1762 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
1763 (1 << (data[1] >> 4)) - 1);
a2a4c265 1764 printf(", AIFSN %d", data[0] & 0xf);
1cab57eb 1765 if (data[2] | data[3])
cee4fe20 1766 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
1cab57eb
JB
1767 data += 4;
1768 }
1769
1770 printf("\n");
1771 return true;
1772
1773 invalid:
1774 printf("invalid: ");
1775 return false;
1776}
1777
5f310d83
SM
1778static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data,
1779 const struct print_ies_data *ie_buffer)
6ff0c93a
MH
1780{
1781 int i;
1782
6ff0c93a
MH
1783 switch (data[0]) {
1784 case 0x00:
83b4934c 1785 printf(" information:");
6ff0c93a
MH
1786 break;
1787 case 0x01:
1cab57eb
JB
1788 if (print_wifi_wmm_param(data + 1, len - 1))
1789 return;
6ff0c93a
MH
1790 break;
1791 default:
83b4934c 1792 printf(" type %d:", data[0]);
6ff0c93a
MH
1793 break;
1794 }
1795
1cab57eb
JB
1796 for(i = 1; i < len; i++)
1797 printf(" %.02x", data[i]);
6ff0c93a
MH
1798 printf("\n");
1799}
1800
a6816965
JM
1801static const char * wifi_wps_dev_passwd_id(uint16_t id)
1802{
1803 switch (id) {
1804 case 0:
1805 return "Default (PIN)";
1806 case 1:
1807 return "User-specified";
1808 case 2:
1809 return "Machine-specified";
1810 case 3:
1811 return "Rekey";
1812 case 4:
1813 return "PushButton";
1814 case 5:
1815 return "Registrar-specified";
1816 default:
1817 return "??";
1818 }
1819}
1820
5f310d83
SM
1821static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data,
1822 const struct print_ies_data *ie_buffer)
4673a894
JB
1823{
1824 bool first = true;
1825 __u16 subtype, sublen;
1826
4673a894
JB
1827 while (len >= 4) {
1828 subtype = (data[0] << 8) + data[1];
1829 sublen = (data[2] << 8) + data[3];
e4ebc462 1830 if (sublen > len - 4)
4673a894
JB
1831 break;
1832
1833 switch (subtype) {
1834 case 0x104a:
1835 tab_on_first(&first);
dffc6750 1836 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
4673a894
JB
1837 break;
1838 case 0x1011:
1839 tab_on_first(&first);
1840 printf("\t * Device name: %.*s\n", sublen, data + 4);
1841 break;
a6816965
JM
1842 case 0x1012: {
1843 uint16_t id;
1844 tab_on_first(&first);
1845 if (sublen != 2) {
1846 printf("\t * Device Password ID: (invalid "
1847 "length %d)\n", sublen);
1848 break;
1849 }
1850 id = data[4] << 8 | data[5];
1851 printf("\t * Device Password ID: %u (%s)\n",
1852 id, wifi_wps_dev_passwd_id(id));
1853 break;
1854 }
4673a894
JB
1855 case 0x1021:
1856 tab_on_first(&first);
1857 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
1858 break;
1859 case 0x1023:
1860 tab_on_first(&first);
1861 printf("\t * Model: %.*s\n", sublen, data + 4);
1862 break;
a6816965
JM
1863 case 0x1024:
1864 tab_on_first(&first);
1865 printf("\t * Model Number: %.*s\n", sublen, data + 4);
1866 break;
1867 case 0x103b: {
1868 __u8 val = data[4];
1869 tab_on_first(&first);
1870 printf("\t * Response Type: %d%s\n",
1871 val, val == 3 ? " (AP)" : "");
1872 break;
1873 }
1874 case 0x103c: {
1875 __u8 val = data[4];
1876 tab_on_first(&first);
1877 printf("\t * RF Bands: 0x%x\n", val);
1878 break;
1879 }
1880 case 0x1041: {
1881 __u8 val = data[4];
1882 tab_on_first(&first);
1883 printf("\t * Selected Registrar: 0x%x\n", val);
1884 break;
1885 }
1886 case 0x1042:
1887 tab_on_first(&first);
1888 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
1889 break;
1890 case 0x1044: {
1891 __u8 val = data[4];
1892 tab_on_first(&first);
1893 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
1894 val,
1895 val == 1 ? " (Unconfigured)" : "",
1896 val == 2 ? " (Configured)" : "");
1897 break;
1898 }
09fe09dc
JB
1899 case 0x1047:
1900 tab_on_first(&first);
1901 printf("\t * UUID: ");
1902 if (sublen != 16) {
1903 printf("(invalid, length=%d)\n", sublen);
1904 break;
1905 }
1906 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
1907 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
1908 data[4], data[5], data[6], data[7],
1909 data[8], data[9], data[10], data[11],
1910 data[12], data[13], data[14], data[15],
1911 data[16], data[17], data[18], data[19]);
1912 break;
a6816965
JM
1913 case 0x1054: {
1914 tab_on_first(&first);
1915 if (sublen != 8) {
1916 printf("\t * Primary Device Type: (invalid "
1917 "length %d)\n", sublen);
1918 break;
1919 }
1920 printf("\t * Primary Device Type: "
1921 "%u-%02x%02x%02x%02x-%u\n",
1922 data[4] << 8 | data[5],
1923 data[6], data[7], data[8], data[9],
1924 data[10] << 8 | data[11]);
1925 break;
1926 }
7ee5a865 1927 case 0x1057: {
fe31a22e 1928 __u8 val = data[4];
7ee5a865 1929 tab_on_first(&first);
fe31a22e 1930 printf("\t * AP setup locked: 0x%.2x\n", val);
7ee5a865
JB
1931 break;
1932 }
a6816965
JM
1933 case 0x1008:
1934 case 0x1053: {
4673a894
JB
1935 __u16 meth = (data[4] << 8) + data[5];
1936 bool comma = false;
1937 tab_on_first(&first);
a6816965
JM
1938 printf("\t * %sConfig methods:",
1939 subtype == 0x1053 ? "Selected Registrar ": "");
4673a894
JB
1940#define T(bit, name) do { \
1941 if (meth & (1<<bit)) { \
1942 if (comma) \
1943 printf(","); \
1944 comma = true; \
1945 printf(" " name); \
1946 } } while (0)
1947 T(0, "USB");
1948 T(1, "Ethernet");
1949 T(2, "Label");
1950 T(3, "Display");
1951 T(4, "Ext. NFC");
1952 T(5, "Int. NFC");
1953 T(6, "NFC Intf.");
1954 T(7, "PBC");
1955 T(8, "Keypad");
1956 printf("\n");
1957 break;
1958#undef T
1959 }
2650d46b
JB
1960 default: {
1961 const __u8 *subdata = data + 4;
1962 __u16 tmplen = sublen;
1963
1964 tab_on_first(&first);
1965 printf("\t * Unknown TLV (%#.4x, %d bytes):",
1966 subtype, tmplen);
1967 while (tmplen) {
1968 printf(" %.2x", *subdata);
1969 subdata++;
1970 tmplen--;
1971 }
1972 printf("\n");
4673a894
JB
1973 break;
1974 }
2650d46b 1975 }
4673a894
JB
1976
1977 data += sublen + 4;
1978 len -= sublen + 4;
1979 }
1980
1981 if (len != 0) {
1982 printf("\t\t * bogus tail data (%d):", len);
1983 while (len) {
1984 printf(" %.2x", *data);
1985 data++;
1986 len--;
1987 }
1988 printf("\n");
1989 }
1990}
1991
83b4934c 1992static const struct ie_print wifiprinters[] = {
febeb0c0
JB
1993 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
1994 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
1995 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
4673a894
JB
1996};
1997
5f310d83
SM
1998static inline void print_p2p(const uint8_t type, uint8_t len,
1999 const uint8_t *data,
2000 const struct print_ies_data *ie_buffer)
9a22374a
JB
2001{
2002 bool first = true;
2003 __u8 subtype;
2004 __u16 sublen;
2005
2006 while (len >= 3) {
2007 subtype = data[0];
2008 sublen = (data[2] << 8) + data[1];
2009
2010 if (sublen > len - 3)
2011 break;
2012
2013 switch (subtype) {
2014 case 0x02: /* capability */
2015 tab_on_first(&first);
2016 if (sublen < 2) {
2017 printf("\t * malformed capability\n");
2018 break;
2019 }
2020 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
2021 data[3], data[4]);
2022 break;
2023 case 0x0d: /* device info */
2024 if (sublen < 6 + 2 + 8 + 1) {
2025 printf("\t * malformed device info\n");
2026 break;
2027 }
c8edf8f5 2028 /* fall through */
9a22374a
JB
2029 case 0x00: /* status */
2030 case 0x01: /* minor reason */
2031 case 0x03: /* device ID */
2032 case 0x04: /* GO intent */
2033 case 0x05: /* configuration timeout */
2034 case 0x06: /* listen channel */
2035 case 0x07: /* group BSSID */
2036 case 0x08: /* ext listen timing */
2037 case 0x09: /* intended interface address */
2038 case 0x0a: /* manageability */
2039 case 0x0b: /* channel list */
2040 case 0x0c: /* NoA */
2041 case 0x0e: /* group info */
2042 case 0x0f: /* group ID */
2043 case 0x10: /* interface */
2044 case 0x11: /* operating channel */
2045 case 0x12: /* invitation flags */
2046 case 0xdd: /* vendor specific */
2047 default: {
505f9e48 2048 const __u8 *subdata = data + 3;
9a22374a
JB
2049 __u16 tmplen = sublen;
2050
2051 tab_on_first(&first);
2052 printf("\t * Unknown TLV (%#.2x, %d bytes):",
2053 subtype, tmplen);
2054 while (tmplen) {
2055 printf(" %.2x", *subdata);
2056 subdata++;
2057 tmplen--;
2058 }
2059 printf("\n");
2060 break;
2061 }
2062 }
2063
2064 data += sublen + 3;
2065 len -= sublen + 3;
2066 }
2067
2068 if (len != 0) {
2069 tab_on_first(&first);
2070 printf("\t * bogus tail data (%d):", len);
2071 while (len) {
2072 printf(" %.2x", *data);
2073 data++;
2074 len--;
2075 }
2076 printf("\n");
2077 }
2078}
2079
5f310d83
SM
2080static inline void print_hs20_ind(const uint8_t type, uint8_t len,
2081 const uint8_t *data,
2082 const struct print_ies_data *ie_buffer)
7e10c4ab
BG
2083{
2084 /* I can't find the spec for this...just going off what wireshark uses. */
2085 printf("\n");
2086 if (len > 0)
2087 printf("\t\tDGAF: %i\n", (int)(data[0] & 0x1));
2088 else
2089 printf("\t\tUnexpected length: %i\n", len);
2090}
2091
fc166079
HM
2092static void print_wifi_owe_tarns(const uint8_t type, uint8_t len,
2093 const uint8_t *data,
2094 const struct print_ies_data *ie_buffer)
2095{
2096 char mac_addr[20];
2097 int ssid_len;
2098
2099 printf("\n");
2100 if (len < 7)
2101 return;
2102
2103 mac_addr_n2a(mac_addr, data);
2104 printf("\t\tBSSID: %s\n", mac_addr);
2105
2106 ssid_len = data[6];
2107 if (ssid_len > len - 7)
2108 return;
2109 printf("\t\tSSID: ");
2110 print_ssid_escaped(ssid_len, data + 7);
2111 printf("\n");
2112
2113 /* optional elements */
2114 if (len >= ssid_len + 9) {
2115 printf("\t\tBand Info: %u\n", data[ssid_len + 7]);
2116 printf("\t\tChannel Info: %u\n", data[ssid_len + 8]);
2117 }
2118}
2119
9a22374a
JB
2120static const struct ie_print wfa_printers[] = {
2121 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
7e10c4ab 2122 [16] = { "HotSpot 2.0 Indication", print_hs20_ind, 1, 255, BIT(PRINT_SCAN), },
f188ab67 2123 [18] = { "HotSpot 2.0 OSEN", print_wifi_osen, 1, 255, BIT(PRINT_SCAN), },
fc166079 2124 [28] = { "OWE Transition Mode", print_wifi_owe_tarns, 7, 255, BIT(PRINT_SCAN), },
9a22374a
JB
2125};
2126
764fe753 2127static void print_vendor(unsigned char len, unsigned char *data,
febeb0c0 2128 bool unknown, enum print_ie_type ptype)
3563f4c5
JB
2129{
2130 int i;
2131
fbf80af5 2132 if (len < 3) {
4673a894 2133 printf("\tVendor specific: <too short> data:");
fbf80af5
JB
2134 for(i = 0; i < len; i++)
2135 printf(" %.02x", data[i]);
2136 printf("\n");
2137 return;
2138 }
2139
3bd60ef1 2140 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
febeb0c0
JB
2141 if (data[3] < ARRAY_SIZE(wifiprinters) &&
2142 wifiprinters[data[3]].name &&
2143 wifiprinters[data[3]].flags & BIT(ptype)) {
5f310d83 2144 print_ie(&wifiprinters[data[3]],
bcdceae1
JB
2145 data[3], len - 4, data + 4,
2146 NULL);
83b4934c
JB
2147 return;
2148 }
febeb0c0 2149 if (!unknown)
4673a894 2150 return;
3bd60ef1 2151 printf("\tMS/WiFi %#.2x, data:", data[3]);
4673a894
JB
2152 for(i = 0; i < len - 4; i++)
2153 printf(" %.02x", data[i + 4]);
2154 printf("\n");
2155 return;
2156 }
2157
9a22374a
JB
2158 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
2159 if (data[3] < ARRAY_SIZE(wfa_printers) &&
2160 wfa_printers[data[3]].name &&
2161 wfa_printers[data[3]].flags & BIT(ptype)) {
5f310d83 2162 print_ie(&wfa_printers[data[3]],
bcdceae1
JB
2163 data[3], len - 4, data + 4,
2164 NULL);
9a22374a
JB
2165 return;
2166 }
2167 if (!unknown)
2168 return;
2169 printf("\tWFA %#.2x, data:", data[3]);
2170 for(i = 0; i < len - 4; i++)
2171 printf(" %.02x", data[i + 4]);
2172 printf("\n");
2173 return;
2174 }
2175
febeb0c0 2176 if (!unknown)
764fe753
JB
2177 return;
2178
fbf80af5 2179 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
3563f4c5 2180 data[0], data[1], data[2]);
fbf80af5
JB
2181 for (i = 3; i < len; i++)
2182 printf(" %.2x", data[i]);
3563f4c5
JB
2183 printf("\n");
2184}
2185
febeb0c0
JB
2186void print_ies(unsigned char *ie, int ielen, bool unknown,
2187 enum print_ie_type ptype)
3563f4c5 2188{
5f310d83
SM
2189 struct print_ies_data ie_buffer = {
2190 .ie = ie,
2191 .ielen = ielen };
2192
cb13b9df
MT
2193 if (ie == NULL || ielen < 0)
2194 return;
2195
2196 while (ielen >= 2 && ielen - 2 >= ie[1]) {
febeb0c0
JB
2197 if (ie[0] < ARRAY_SIZE(ieprinters) &&
2198 ieprinters[ie[0]].name &&
2199 ieprinters[ie[0]].flags & BIT(ptype)) {
5f310d83
SM
2200 print_ie(&ieprinters[ie[0]],
2201 ie[0], ie[1], ie + 2, &ie_buffer);
764fe753 2202 } else if (ie[0] == 221 /* vendor */) {
febeb0c0
JB
2203 print_vendor(ie[1], ie + 2, unknown, ptype);
2204 } else if (unknown) {
3563f4c5
JB
2205 int i;
2206
8086b700 2207 printf("\tUnknown IE (%d):", ie[0]);
3563f4c5 2208 for (i=0; i<ie[1]; i++)
8086b700 2209 printf(" %.2x", ie[2+i]);
3563f4c5
JB
2210 printf("\n");
2211 }
2212 ielen -= ie[1] + 2;
2213 ie += ie[1] + 2;
2214 }
2215}
2216
2e8b82c1
VK
2217static void print_capa_dmg(__u16 capa)
2218{
2219 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
2220 case WLAN_CAPABILITY_DMG_TYPE_AP:
2221 printf(" DMG_ESS");
2222 break;
2223 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
2224 printf(" DMG_PCP");
2225 break;
2226 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
2227 printf(" DMG_IBSS");
2228 break;
2229 }
2230
2231 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
2232 printf(" CBAP_Only");
2233 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
2234 printf(" CBAP_Src");
2235 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
2236 printf(" Privacy");
2237 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
2238 printf(" ECPAC");
2239 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
2240 printf(" SpectrumMgmt");
2241 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
2242 printf(" RadioMeasure");
2243}
2244
2245static void print_capa_non_dmg(__u16 capa)
2246{
2247 if (capa & WLAN_CAPABILITY_ESS)
2248 printf(" ESS");
2249 if (capa & WLAN_CAPABILITY_IBSS)
2250 printf(" IBSS");
2251 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
2252 printf(" CfPollable");
2253 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
2254 printf(" CfPollReq");
2255 if (capa & WLAN_CAPABILITY_PRIVACY)
2256 printf(" Privacy");
2257 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
2258 printf(" ShortPreamble");
2259 if (capa & WLAN_CAPABILITY_PBCC)
2260 printf(" PBCC");
2261 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
2262 printf(" ChannelAgility");
2263 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
2264 printf(" SpectrumMgmt");
2265 if (capa & WLAN_CAPABILITY_QOS)
2266 printf(" QoS");
2267 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2268 printf(" ShortSlotTime");
2269 if (capa & WLAN_CAPABILITY_APSD)
2270 printf(" APSD");
2271 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
2272 printf(" RadioMeasure");
2273 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
2274 printf(" DSSS-OFDM");
2275 if (capa & WLAN_CAPABILITY_DEL_BACK)
2276 printf(" DelayedBACK");
2277 if (capa & WLAN_CAPABILITY_IMM_BACK)
2278 printf(" ImmediateBACK");
2279}
2280
3563f4c5
JB
2281static int print_bss_handler(struct nl_msg *msg, void *arg)
2282{
2283 struct nlattr *tb[NL80211_ATTR_MAX + 1];
2284 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
2285 struct nlattr *bss[NL80211_BSS_MAX + 1];
2286 char mac_addr[20], dev[20];
2287 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
2288 [NL80211_BSS_TSF] = { .type = NLA_U64 },
2289 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
2290 [NL80211_BSS_BSSID] = { },
2291 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
2292 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
2293 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
f2e17e1f
JB
2294 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
2295 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
a56117a6 2296 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
c04a78df 2297 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
575280cc 2298 [NL80211_BSS_BEACON_IES] = { },
3563f4c5 2299 };
febeb0c0 2300 struct scan_params *params = arg;
1c5bcd9c 2301 int show = params->show_both_ie_sets ? 2 : 1;
2e8b82c1 2302 bool is_dmg = false;
3563f4c5
JB
2303
2304 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
2305 genlmsg_attrlen(gnlh, 0), NULL);
2306
2307 if (!tb[NL80211_ATTR_BSS]) {
5fe70c0e 2308 fprintf(stderr, "bss info missing!\n");
3563f4c5
JB
2309 return NL_SKIP;
2310 }
2311 if (nla_parse_nested(bss, NL80211_BSS_MAX,
2312 tb[NL80211_ATTR_BSS],
2313 bss_policy)) {
5fe70c0e 2314 fprintf(stderr, "failed to parse nested attributes!\n");
3563f4c5
JB
2315 return NL_SKIP;
2316 }
2317
2318 if (!bss[NL80211_BSS_BSSID])
2319 return NL_SKIP;
2320
2321 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
00889fb2
JB
2322 printf("BSS %s", mac_addr);
2323 if (tb[NL80211_ATTR_IFINDEX]) {
2324 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
2325 printf("(on %s)", dev);
2326 }
a56117a6
JB
2327
2328 if (bss[NL80211_BSS_STATUS]) {
2329 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
2330 case NL80211_BSS_STATUS_AUTHENTICATED:
2331 printf(" -- authenticated");
2332 break;
2333 case NL80211_BSS_STATUS_ASSOCIATED:
2334 printf(" -- associated");
2335 break;
2336 case NL80211_BSS_STATUS_IBSS_JOINED:
2337 printf(" -- joined");
2338 break;
2339 default:
2340 printf(" -- unknown status: %d",
2341 nla_get_u32(bss[NL80211_BSS_STATUS]));
2342 break;
2343 }
2344 }
2345 printf("\n");
3563f4c5 2346
f6fca12c
JB
2347 if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
2348 unsigned long long bt;
2349 bt = (unsigned long long)nla_get_u64(bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
2350 printf("\tlast seen: %llu.%.3llus [boottime]\n", bt/1000000000, (bt%1000000000)/1000000);
2351 }
2352
e7109a8a
JB
2353 if (bss[NL80211_BSS_TSF]) {
2354 unsigned long long tsf;
2355 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
2356 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
2357 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
2358 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
2359 }
2e8b82c1
VK
2360 if (bss[NL80211_BSS_FREQUENCY]) {
2361 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
2362 printf("\tfreq: %d\n", freq);
2363 if (freq > 45000)
2364 is_dmg = true;
2365 }
3563f4c5 2366 if (bss[NL80211_BSS_BEACON_INTERVAL])
792172b0 2367 printf("\tbeacon interval: %d TUs\n",
3563f4c5 2368 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
92a04ecd
MH
2369 if (bss[NL80211_BSS_CAPABILITY]) {
2370 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
2371 printf("\tcapability:");
2e8b82c1
VK
2372 if (is_dmg)
2373 print_capa_dmg(capa);
2374 else
2375 print_capa_non_dmg(capa);
92a04ecd
MH
2376 printf(" (0x%.4x)\n", capa);
2377 }
f2e17e1f
JB
2378 if (bss[NL80211_BSS_SIGNAL_MBM]) {
2379 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
2380 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
2381 }
2382 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
2383 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
2384 printf("\tsignal: %d/100\n", s);
2385 }
c04a78df
HS
2386 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
2387 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
2388 printf("\tlast seen: %d ms ago\n", age);
2389 }
c551449a 2390
1c5bcd9c 2391 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
b50d1456
JB
2392 struct nlattr *ies = bss[NL80211_BSS_INFORMATION_ELEMENTS];
2393 struct nlattr *bcnies = bss[NL80211_BSS_BEACON_IES];
2394
2395 if (bss[NL80211_BSS_PRESP_DATA] ||
86d907a3 2396 (bcnies && (nla_len(ies) != nla_len(bcnies) ||
c9c3b69d
JB
2397 memcmp(nla_data(ies), nla_data(bcnies),
2398 nla_len(ies)))))
575280cc
JM
2399 printf("\tInformation elements from Probe Response "
2400 "frame:\n");
b50d1456 2401 print_ies(nla_data(ies), nla_len(ies),
febeb0c0 2402 params->unknown, params->type);
575280cc 2403 }
1c5bcd9c 2404 if (bss[NL80211_BSS_BEACON_IES] && show--) {
575280cc
JM
2405 printf("\tInformation elements from Beacon frame:\n");
2406 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
2407 nla_len(bss[NL80211_BSS_BEACON_IES]),
2408 params->unknown, params->type);
2409 }
3563f4c5
JB
2410
2411 return NL_SKIP;
2412}
2413
764fe753 2414static struct scan_params scan_params;
3563f4c5 2415
7c37a24d 2416static int handle_scan_dump(struct nl80211_state *state,
3563f4c5 2417 struct nl_msg *msg,
05514f95
JB
2418 int argc, char **argv,
2419 enum id_input id)
3563f4c5 2420{
764fe753
JB
2421 if (argc > 1)
2422 return 1;
2423
1c5bcd9c
JB
2424 memset(&scan_params, 0, sizeof(scan_params));
2425
764fe753
JB
2426 if (argc == 1 && !strcmp(argv[0], "-u"))
2427 scan_params.unknown = true;
575280cc
JM
2428 else if (argc == 1 && !strcmp(argv[0], "-b"))
2429 scan_params.show_both_ie_sets = true;
764fe753 2430
febeb0c0
JB
2431 scan_params.type = PRINT_SCAN;
2432
f3c96669 2433 register_handler(print_bss_handler, &scan_params);
3563f4c5
JB
2434 return 0;
2435}
a5fe4ef2
JB
2436
2437static int handle_scan_combined(struct nl80211_state *state,
a5fe4ef2 2438 struct nl_msg *msg,
05514f95
JB
2439 int argc, char **argv,
2440 enum id_input id)
a5fe4ef2 2441{
559a1713 2442 char **trig_argv;
a5fe4ef2
JB
2443 static char *dump_argv[] = {
2444 NULL,
2445 "scan",
2446 "dump",
92649eab 2447 NULL,
a5fe4ef2
JB
2448 };
2449 static const __u32 cmds[] = {
2450 NL80211_CMD_NEW_SCAN_RESULTS,
2451 NL80211_CMD_SCAN_ABORTED,
2452 };
559a1713 2453 int trig_argc, dump_argc, err;
7d19e35d 2454 int i;
a5fe4ef2 2455
559a1713
JB
2456 if (argc >= 3 && !strcmp(argv[2], "-u")) {
2457 dump_argc = 4;
2458 dump_argv[3] = "-u";
575280cc
JM
2459 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
2460 dump_argc = 4;
2461 dump_argv[3] = "-b";
559a1713
JB
2462 } else
2463 dump_argc = 3;
2464
2465 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
2466 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
2467 if (!trig_argv)
2468 return -ENOMEM;
a5fe4ef2 2469 trig_argv[0] = argv[0];
559a1713
JB
2470 trig_argv[1] = "scan";
2471 trig_argv[2] = "trigger";
7d19e35d 2472
559a1713
JB
2473 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
2474 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
75f4204c 2475 err = handle_cmd(state, id, trig_argc, trig_argv);
559a1713 2476 free(trig_argv);
a5fe4ef2
JB
2477 if (err)
2478 return err;
2479
61725dbe
JB
2480 /*
2481 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
2482 *
2483 * This code has a bug, which requires creating a separate
2484 * nl80211 socket to fix:
2485 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
2486 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
2487 * before (!) we listen to it, because we only start listening
2488 * after we send our scan request.
2489 *
2490 * Doing it the other way around has a race condition as well,
2491 * if you first open the events socket you may get a notification
2492 * for a previous scan.
2493 *
2494 * The only proper way to fix this would be to listen to events
2495 * before sending the command, and for the kernel to send the
2496 * scan request along with the event, so that you can match up
2497 * whether the scan you requested was finished or aborted (this
2498 * may result in processing a scan that another application
2499 * requested, but that doesn't seem to be a problem).
2500 *
2501 * Alas, the kernel doesn't do that (yet).
2502 */
2503
a5fe4ef2
JB
2504 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
2505 NL80211_CMD_SCAN_ABORTED) {
2506 printf("scan aborted!\n");
2507 return 0;
2508 }
2509
2510 dump_argv[0] = argv[0];
75f4204c 2511 return handle_cmd(state, id, dump_argc, dump_argv);
a5fe4ef2 2512}
3a99ff6d 2513TOPLEVEL(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
2514 CIB_NETDEV, handle_scan_combined,
2515 "Scan on the given frequencies and probe for the given SSIDs\n"
2516 "(or wildcard if not given) unless passive scanning is requested.\n"
64797a7f
JB
2517 "If -u is specified print unknown data in the scan results.\n"
2518 "Specified (vendor) IEs must be well-formed.");
4698bfc2
JB
2519COMMAND(scan, dump, "[-u]",
2520 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
2521 "Dump the current scan results. If -u is specified, print unknown\n"
2522 "data in scan results.");
3a99ff6d 2523COMMAND(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
2524 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
2525 "Trigger a scan on the given frequencies with probing for the given\n"
3a99ff6d
PKC
2526 "SSIDs (or wildcard if not given) unless passive scanning is requested.\n"
2527 "Duration(in TUs), if specified, will be used to set dwell times.\n");
3fce58aa
LC
2528
2529
0a12cf8d
EP
2530static int handle_scan_abort(struct nl80211_state *state,
2531 struct nl_msg *msg,
2532 int argc, char **argv,
2533 enum id_input id)
2534{
2535 return 0;
2536}
2537COMMAND(scan, abort, "",
2538 NL80211_CMD_ABORT_SCAN, 0, CIB_NETDEV, handle_scan_abort,
2539 "Abort ongoing scan");
2540
3fce58aa 2541static int handle_start_sched_scan(struct nl80211_state *state,
34b23014 2542 struct nl_msg *msg,
3fce58aa
LC
2543 int argc, char **argv, enum id_input id)
2544{
2545 return parse_sched_scan(msg, &argc, &argv);
2546}
2547
34b23014 2548static int handle_stop_sched_scan(struct nl80211_state *state,
3fce58aa
LC
2549 struct nl_msg *msg, int argc, char **argv,
2550 enum id_input id)
2551{
2552 if (argc != 0)
2553 return 1;
2554
2555 return 0;
2556}
2557
2558COMMAND(scan, sched_start,
2559 SCHED_SCAN_OPTIONS,
2560 NL80211_CMD_START_SCHED_SCAN, 0, CIB_NETDEV, handle_start_sched_scan,
2561 "Start a scheduled scan at the specified interval on the given frequencies\n"
2562 "with probing for the given SSIDs (or wildcard if not given) unless passive\n"
2563 "scanning is requested. If matches are specified, only matching results\n"
2564 "will be returned.");
2565COMMAND(scan, sched_stop, "",
2566 NL80211_CMD_STOP_SCHED_SCAN, 0, CIB_NETDEV, handle_stop_sched_scan,
2567 "Stop an ongoing scheduled scan.");