]> git.ipfire.org Git - thirdparty/iw.git/blame - scan.c
align TDLS output
[thirdparty/iw.git] / scan.c
CommitLineData
3563f4c5
JB
1#include <net/if.h>
2#include <errno.h>
3#include <string.h>
4#include <ctype.h>
764fe753 5#include <stdbool.h>
3563f4c5
JB
6
7#include <netlink/genl/genl.h>
8#include <netlink/genl/family.h>
9#include <netlink/genl/ctrl.h>
10#include <netlink/msg.h>
11#include <netlink/attr.h>
12
13#include "nl80211.h"
14#include "iw.h"
15
92a04ecd
MH
16#define WLAN_CAPABILITY_ESS (1<<0)
17#define WLAN_CAPABILITY_IBSS (1<<1)
18#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
19#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
20#define WLAN_CAPABILITY_PRIVACY (1<<4)
21#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
22#define WLAN_CAPABILITY_PBCC (1<<6)
23#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
24#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
25#define WLAN_CAPABILITY_QOS (1<<9)
26#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
27#define WLAN_CAPABILITY_APSD (1<<11)
2e8b82c1 28#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12)
92a04ecd 29#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
2e8b82c1
VK
30#define WLAN_CAPABILITY_DEL_BACK (1<<14)
31#define WLAN_CAPABILITY_IMM_BACK (1<<15)
32/* DMG (60gHz) 802.11ad */
33/* type - bits 0..1 */
34#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0)
35
36#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */
37#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */
38#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */
39
40#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2)
41#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
42#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4)
43#define WLAN_CAPABILITY_DMG_ECPAC (1<<5)
44
45#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8)
46#define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12)
92a04ecd 47
3bd60ef1
JB
48static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
49static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
9a22374a 50static unsigned char wfa_oui[3] = { 0x50, 0x6f, 0x9a };
857d966e 51
764fe753
JB
52struct scan_params {
53 bool unknown;
febeb0c0 54 enum print_ie_type type;
575280cc 55 bool show_both_ie_sets;
764fe753
JB
56};
57
2b690f0a
LR
58#define IEEE80211_COUNTRY_EXTENSION_ID 201
59
72725719
JB
60union ieee80211_country_ie_triplet {
61 struct {
62 __u8 first_channel;
63 __u8 num_channels;
64 __s8 max_power;
65 } __attribute__ ((packed)) chans;
66 struct {
67 __u8 reg_extension_id;
68 __u8 reg_class;
69 __u8 coverage_class;
70 } __attribute__ ((packed)) ext;
2b690f0a
LR
71} __attribute__ ((packed));
72
7c37a24d
JB
73static int handle_scan(struct nl80211_state *state,
74 struct nl_cb *cb,
3563f4c5 75 struct nl_msg *msg,
05514f95
JB
76 int argc, char **argv,
77 enum id_input id)
3563f4c5 78{
559a1713
JB
79 struct nl_msg *ssids = NULL, *freqs = NULL;
80 char *eptr;
3563f4c5 81 int err = -ENOBUFS;
559a1713
JB
82 int i;
83 enum {
84 NONE,
85 FREQ,
64797a7f 86 IES,
559a1713
JB
87 SSID,
88 DONE,
89 } parse = NONE;
90 int freq;
91 bool passive = false, have_ssids = false, have_freqs = false;
64797a7f
JB
92 size_t tmp;
93 unsigned char *ies;
fe862239 94 int flags = 0;
3563f4c5
JB
95
96 ssids = nlmsg_alloc();
97 if (!ssids)
98 return -ENOMEM;
559a1713
JB
99
100 freqs = nlmsg_alloc();
101 if (!freqs) {
102 nlmsg_free(ssids);
103 return -ENOMEM;
104 }
105
106 for (i = 0; i < argc; i++) {
559a1713
JB
107 switch (parse) {
108 case NONE:
64797a7f
JB
109 if (strcmp(argv[i], "freq") == 0) {
110 parse = FREQ;
111 have_freqs = true;
112 break;
113 } else if (strcmp(argv[i], "ies") == 0) {
114 parse = IES;
115 break;
fe862239
SL
116 } else if (strcmp(argv[i], "lowpri") == 0) {
117 parse = NONE;
118 flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
119 break;
120 } else if (strcmp(argv[i], "flush") == 0) {
121 parse = NONE;
122 flags |= NL80211_SCAN_FLAG_FLUSH;
123 break;
ced94d5f
AQ
124 } else if (strcmp(argv[i], "ap-force") == 0) {
125 parse = NONE;
126 flags |= NL80211_SCAN_FLAG_AP;
127 break;
64797a7f
JB
128 } else if (strcmp(argv[i], "ssid") == 0) {
129 parse = SSID;
130 have_ssids = true;
131 break;
132 } else if (strcmp(argv[i], "passive") == 0) {
133 parse = DONE;
134 passive = true;
135 break;
136 }
559a1713
JB
137 case DONE:
138 return 1;
139 case FREQ:
140 freq = strtoul(argv[i], &eptr, 10);
cc12e895
JB
141 if (eptr != argv[i] + strlen(argv[i])) {
142 /* failed to parse as number -- maybe a tag? */
143 i--;
144 parse = NONE;
145 continue;
146 }
559a1713 147 NLA_PUT_U32(freqs, i, freq);
64797a7f
JB
148 break;
149 case IES:
150 ies = parse_hex(argv[i], &tmp);
151 if (!ies)
152 goto nla_put_failure;
153 NLA_PUT(msg, NL80211_ATTR_IE, tmp, ies);
154 free(ies);
155 parse = NONE;
559a1713
JB
156 break;
157 case SSID:
158 NLA_PUT(ssids, i, strlen(argv[i]), argv[i]);
159 break;
160 }
161 }
162
163 if (!have_ssids)
164 NLA_PUT(ssids, 1, 0, "");
165 if (!passive)
166 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
167
168 if (have_freqs)
169 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
fe862239
SL
170 if (flags)
171 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
3563f4c5
JB
172
173 err = 0;
174 nla_put_failure:
175 nlmsg_free(ssids);
559a1713 176 nlmsg_free(freqs);
3563f4c5
JB
177 return err;
178}
3563f4c5 179
857d966e
MH
180static void tab_on_first(bool *first)
181{
182 if (!*first)
183 printf("\t");
184 else
185 *first = false;
186}
187
83b4934c 188static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5 189{
83b4934c 190 printf(" ");
748f8489 191 print_ssid_escaped(len, data);
3563f4c5
JB
192 printf("\n");
193}
194
ca159934 195#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
1fd19c39
CL
196#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
197
83b4934c 198static void print_supprates(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5
JB
199{
200 int i;
201
83b4934c 202 printf(" ");
3563f4c5 203
83b4934c 204 for (i = 0; i < len; i++) {
3563f4c5 205 int r = data[i] & 0x7f;
1fd19c39 206
ca159934
JB
207 if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
208 printf("VHT");
209 else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
1fd19c39
CL
210 printf("HT");
211 else
212 printf("%d.%d", r/2, 5*(r&1));
213
214 printf("%s ", data[i] & 0x80 ? "*" : "");
3563f4c5
JB
215 }
216 printf("\n");
217}
218
83b4934c 219static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5 220{
83b4934c 221 printf(" channel %d\n", data[0]);
3563f4c5
JB
222}
223
2b690f0a 224static const char *country_env_str(char environment)
b7e8fa37 225{
2b690f0a 226 switch (environment) {
b7e8fa37 227 case 'I':
2b690f0a 228 return "Indoor only";
b7e8fa37 229 case 'O':
2b690f0a 230 return "Outdoor only";
b6c0d634 231 case ' ':
2b690f0a 232 return "Indoor/Outdoor";
b6c0d634 233 default:
2b690f0a 234 return "bogus";
b7e8fa37 235 }
2b690f0a
LR
236}
237
238static void print_country(const uint8_t type, uint8_t len, const uint8_t *data)
239{
240 printf(" %.*s", 2, data);
241
242 printf("\tEnvironment: %s\n", country_env_str(data[2]));
243
244 data += 3;
245 len -= 3;
246
247 if (len < 3) {
248 printf("\t\tNo country IE triplets present\n");
249 return;
250 }
251
252 while (len >= 3) {
253 int end_channel;
72725719 254 union ieee80211_country_ie_triplet *triplet = (void *) data;
2b690f0a
LR
255
256 if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
257 printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
258 triplet->ext.reg_extension_id,
259 triplet->ext.reg_class,
260 triplet->ext.coverage_class,
261 triplet->ext.coverage_class * 450);
262
263 data += 3;
264 len -= 3;
265 continue;
266 }
267
268 /* 2 GHz */
269 if (triplet->chans.first_channel <= 14)
270 end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
271 else
272 end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
273
a9859d30 274 printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
2b690f0a
LR
275
276 data += 3;
277 len -= 3;
278 }
279
280 return;
b7e8fa37
MH
281}
282
d1563a1b
MH
283static void print_powerconstraint(const uint8_t type, uint8_t len, const uint8_t *data)
284{
285 printf(" %d dB\n", data[0]);
286}
287
83b4934c 288static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data)
fc4d1484
MH
289{
290 if (data[0] == 0x00)
83b4934c 291 printf(" <no flags>");
fc4d1484
MH
292 if (data[0] & 0x01)
293 printf(" NonERP_Present");
294 if (data[0] & 0x02)
295 printf(" Use_Protection");
296 if (data[0] & 0x04)
297 printf(" Barker_Preamble_Mode");
298 printf("\n");
299}
300
83b4934c 301static void print_cipher(const uint8_t *data)
857d966e 302{
3bd60ef1 303 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 304 switch (data[3]) {
510e0e2f 305 case 0:
857d966e
MH
306 printf("Use group cipher suite");
307 break;
510e0e2f 308 case 1:
857d966e
MH
309 printf("WEP-40");
310 break;
510e0e2f 311 case 2:
857d966e
MH
312 printf("TKIP");
313 break;
510e0e2f 314 case 4:
857d966e
MH
315 printf("CCMP");
316 break;
510e0e2f 317 case 5:
857d966e
MH
318 printf("WEP-104");
319 break;
320 default:
332769c6 321 printf("%.02x-%.02x-%.02x:%d",
5594fd23 322 data[0], data[1] ,data[2], data[3]);
857d966e
MH
323 break;
324 }
325 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
326 switch (data[3]) {
510e0e2f 327 case 0:
857d966e
MH
328 printf("Use group cipher suite");
329 break;
510e0e2f 330 case 1:
857d966e
MH
331 printf("WEP-40");
332 break;
510e0e2f 333 case 2:
857d966e
MH
334 printf("TKIP");
335 break;
510e0e2f 336 case 4:
857d966e
MH
337 printf("CCMP");
338 break;
510e0e2f 339 case 5:
857d966e
MH
340 printf("WEP-104");
341 break;
510e0e2f 342 case 6:
857d966e
MH
343 printf("AES-128-CMAC");
344 break;
a8b3da9d
VK
345 case 8:
346 printf("GCMP");
347 break;
857d966e 348 default:
332769c6 349 printf("%.02x-%.02x-%.02x:%d",
5594fd23 350 data[0], data[1] ,data[2], data[3]);
857d966e
MH
351 break;
352 }
353 } else
332769c6 354 printf("%.02x-%.02x-%.02x:%d",
5594fd23 355 data[0], data[1] ,data[2], data[3]);
857d966e
MH
356}
357
83b4934c 358static void print_auth(const uint8_t *data)
857d966e 359{
3bd60ef1 360 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 361 switch (data[3]) {
510e0e2f 362 case 1:
857d966e
MH
363 printf("IEEE 802.1X");
364 break;
510e0e2f 365 case 2:
857d966e
MH
366 printf("PSK");
367 break;
368 default:
332769c6 369 printf("%.02x-%.02x-%.02x:%d",
5594fd23 370 data[0], data[1] ,data[2], data[3]);
857d966e
MH
371 break;
372 }
373 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
374 switch (data[3]) {
510e0e2f 375 case 1:
857d966e
MH
376 printf("IEEE 802.1X");
377 break;
510e0e2f 378 case 2:
857d966e
MH
379 printf("PSK");
380 break;
0fe1c415
JB
381 case 3:
382 printf("FT/IEEE 802.1X");
383 break;
384 case 4:
385 printf("FT/PSK");
386 break;
387 case 5:
388 printf("IEEE 802.1X/SHA-256");
389 break;
390 case 6:
391 printf("PSK/SHA-256");
392 break;
857d966e 393 default:
332769c6 394 printf("%.02x-%.02x-%.02x:%d",
5594fd23 395 data[0], data[1] ,data[2], data[3]);
857d966e
MH
396 break;
397 }
398 } else
332769c6 399 printf("%.02x-%.02x-%.02x:%d",
5594fd23 400 data[0], data[1] ,data[2], data[3]);
857d966e
MH
401}
402
83b4934c
JB
403static void print_rsn_ie(const char *defcipher, const char *defauth,
404 uint8_t len, const uint8_t *data)
857d966e
MH
405{
406 bool first = true;
407 __u16 version, count, capa;
408 int i;
409
857d966e
MH
410 version = data[0] + (data[1] << 8);
411 tab_on_first(&first);
412 printf("\t * Version: %d\n", version);
413
414 data += 2;
415 len -= 2;
416
417 if (len < 4) {
418 tab_on_first(&first);
419 printf("\t * Group cipher: %s\n", defcipher);
420 printf("\t * Pairwise ciphers: %s\n", defcipher);
421 return;
422 }
423
424 tab_on_first(&first);
425 printf("\t * Group cipher: ");
426 print_cipher(data);
427 printf("\n");
428
429 data += 4;
430 len -= 4;
431
432 if (len < 2) {
433 tab_on_first(&first);
434 printf("\t * Pairwise ciphers: %s\n", defcipher);
435 return;
436 }
437
438 count = data[0] | (data[1] << 8);
31d477fb
MH
439 if (2 + (count * 4) > len)
440 goto invalid;
441
857d966e
MH
442 tab_on_first(&first);
443 printf("\t * Pairwise ciphers:");
31d477fb 444 for (i = 0; i < count; i++) {
857d966e
MH
445 printf(" ");
446 print_cipher(data + 2 + (i * 4));
447 }
448 printf("\n");
449
450 data += 2 + (count * 4);
451 len -= 2 + (count * 4);
452
453 if (len < 2) {
454 tab_on_first(&first);
455 printf("\t * Authentication suites: %s\n", defauth);
456 return;
457 }
458
459 count = data[0] | (data[1] << 8);
31d477fb
MH
460 if (2 + (count * 4) > len)
461 goto invalid;
462
857d966e
MH
463 tab_on_first(&first);
464 printf("\t * Authentication suites:");
83b4934c 465 for (i = 0; i < count; i++) {
857d966e
MH
466 printf(" ");
467 print_auth(data + 2 + (i * 4));
468 }
469 printf("\n");
470
471 data += 2 + (count * 4);
472 len -= 2 + (count * 4);
473
6a4f24e8
JB
474 if (len >= 2) {
475 capa = data[0] | (data[1] << 8);
476 tab_on_first(&first);
cadbe89a
JB
477 printf("\t * Capabilities:");
478 if (capa & 0x0001)
479 printf(" PreAuth");
480 if (capa & 0x0002)
481 printf(" NoPairwise");
482 switch ((capa & 0x000c) >> 2) {
483 case 0:
484 break;
485 case 1:
486 printf(" 2-PTKSA-RC");
487 break;
488 case 2:
489 printf(" 4-PTKSA-RC");
490 break;
491 case 3:
492 printf(" 16-PTKSA-RC");
493 break;
494 }
495 switch ((capa & 0x0030) >> 4) {
496 case 0:
497 break;
498 case 1:
499 printf(" 2-GTKSA-RC");
500 break;
501 case 2:
502 printf(" 4-GTKSA-RC");
503 break;
504 case 3:
505 printf(" 16-GTKSA-RC");
506 break;
507 }
508 if (capa & 0x0040)
509 printf(" MFP-required");
510 if (capa & 0x0080)
511 printf(" MFP-capable");
512 if (capa & 0x0200)
513 printf(" Peerkey-enabled");
514 if (capa & 0x0400)
515 printf(" SPP-AMSDU-capable");
516 if (capa & 0x0800)
517 printf(" SPP-AMSDU-required");
518 printf(" (0x%.4x)\n", capa);
6a4f24e8
JB
519 data += 2;
520 len -= 2;
521 }
31d477fb 522
5ba3f523
JB
523 if (len >= 2) {
524 int pmkid_count = data[0] | (data[1] << 8);
525
526 if (len >= 2 + 16 * pmkid_count) {
527 tab_on_first(&first);
528 printf("\t * %d PMKIDs\n", pmkid_count);
529 /* not printing PMKID values */
530 data += 2 + 16 * pmkid_count;
531 len -= 2 + 16 * pmkid_count;
532 } else
533 goto invalid;
534 }
535
536 if (len >= 4) {
537 tab_on_first(&first);
538 printf("\t * Group mgmt cipher suite: ");
539 print_cipher(data);
540 printf("\n");
541 data += 4;
542 len -= 4;
543 }
544
cadbe89a 545 invalid:
31d477fb
MH
546 if (len != 0) {
547 printf("\t\t * bogus tail data (%d):", len);
548 while (len) {
549 printf(" %.2x", *data);
550 data++;
551 len--;
552 }
553 printf("\n");
554 }
857d966e
MH
555}
556
83b4934c 557static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data)
857d966e 558{
83b4934c 559 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
857d966e
MH
560}
561
0c445c24
LR
562static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
563{
357c1a5d 564 printf("\n");
7ddfb679
JB
565 print_ht_capability(data[0] | (data[1] << 8));
566 print_ampdu_length(data[2] & 3);
c79c7464 567 print_ampdu_spacing((data[2] >> 2) & 7);
7ddfb679 568 print_ht_mcs(data + 3);
0c445c24
LR
569}
570
29f7579e
JB
571static const char *ht_secondary_offset[4] = {
572 "no secondary",
573 "above",
574 "[reserved!]",
575 "below",
576};
577
be7602fb
JB
578static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data)
579{
be7602fb
JB
580 static const char *protection[4] = {
581 "no",
582 "nonmember",
583 "20 MHz",
584 "non-HT mixed",
585 };
586 static const char *sta_chan_width[2] = {
587 "20 MHz",
588 "any",
589 };
590
591 printf("\n");
592 printf("\t\t * primary channel: %d\n", data[0]);
593 printf("\t\t * secondary channel offset: %s\n",
29f7579e 594 ht_secondary_offset[data[1] & 0x3]);
be7602fb
JB
595 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
596 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
597 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
598 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
599 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
600 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
601 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
602 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
603 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
604 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
605 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
606}
607
83b4934c 608static void print_capabilities(const uint8_t type, uint8_t len, const uint8_t *data)
9b880b00 609{
31d2d259
JB
610 int i, base, bit;
611 bool first = true;
612
613
614 for (i = 0; i < len; i++) {
615 base = i * 8;
616
617 for (bit = 0; bit < 8; bit++) {
618 if (!(data[i] & (1 << bit)))
619 continue;
620
621 if (!first)
622 printf(",");
623 else
624 first = false;
625
70c649d2
JB
626#define CAPA(bit, name) case bit: printf(" " name); break
627
31d2d259 628 switch (bit + base) {
70c649d2
JB
629 CAPA(0, "HT Information Exchange Supported");
630 CAPA(1, "reserved (On-demand Beacon)");
631 CAPA(2, "Extended Channel Switching");
632 CAPA(3, "reserved (Wave Indication)");
633 CAPA(4, "PSMP Capability");
634 CAPA(5, "reserved (Service Interval Granularity)");
635 CAPA(6, "S-PSMP Capability");
636 CAPA(7, "Event");
637 CAPA(8, "Diagnostics");
638 CAPA(9, "Multicast Diagnostics");
639 CAPA(10, "Location Tracking");
640 CAPA(11, "FMS");
641 CAPA(12, "Proxy ARP Service");
642 CAPA(13, "Collocated Interference Reporting");
643 CAPA(14, "Civic Location");
644 CAPA(15, "Geospatial Location");
645 CAPA(16, "TFS");
646 CAPA(17, "WNM-Sleep Mode");
647 CAPA(18, "TIM Broadcast");
648 CAPA(19, "BSS Transition");
649 CAPA(20, "QoS Traffic Capability");
650 CAPA(21, "AC Station Count");
651 CAPA(22, "Multiple BSSID");
652 CAPA(23, "Timing Measurement");
653 CAPA(24, "Channel Usage");
654 CAPA(25, "SSID List");
655 CAPA(26, "DMS");
656 CAPA(27, "UTC TSF Offset");
657 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
658 CAPA(29, "TDLS Peer PSM Support");
659 CAPA(30, "TDLS channel switching");
660 CAPA(31, "Interworking");
661 CAPA(32, "QoS Map");
662 CAPA(33, "EBR");
663 CAPA(34, "SSPN Interface");
664 CAPA(35, "Reserved");
665 CAPA(36, "MSGCF Capability");
666 CAPA(37, "TDLS Support");
667 CAPA(38, "TDLS Prohibited");
668 CAPA(39, "TDLS Channel Switching Prohibited");
669 CAPA(40, "Reject Unadmitted Frame");
670 CAPA(44, "Identifier Location");
671 CAPA(45, "U-APSD Coexistence");
672 CAPA(46, "WNM-Notification");
673 CAPA(47, "Reserved");
674 CAPA(48, "UTF-8 SSID");
31d2d259
JB
675 default:
676 printf(" %d", bit);
677 break;
678 }
70c649d2 679#undef CAPA
31d2d259
JB
680 }
681 }
9b880b00 682
9b880b00
MH
683 printf("\n");
684}
685
575280cc
JM
686static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data)
687{
688 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
689 "Bitmap[0] 0x%x",
690 data[0], data[1], data[2], data[3]);
691 if (len - 4)
692 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
693 printf("\n");
694}
695
54eb1613
JB
696static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
697{
698 printf("\n");
699 print_vht_info(data[0] | (data[1] << 8) |
700 (data[2] << 16) | (data[3] << 24),
701 data + 4);
702}
703
ca159934
JB
704static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data)
705{
706 const char *chandwidths[] = {
707 [0] = "20 or 40 MHz",
708 [1] = "80 MHz",
709 [3] = "80+80 MHz",
710 [2] = "160 MHz",
711 };
712
713 printf("\n");
714 printf("\t\t * channel width: %d (%s)\n", data[0],
715 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
716 printf("\t\t * center freq segment 1: %d\n", data[1]);
717 printf("\t\t * center freq segment 2: %d\n", data[2]);
718 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
719}
720
29f7579e
JB
721static void print_obss_scan_params(const uint8_t type, uint8_t len, const uint8_t *data)
722{
723 printf("\n");
724 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
725 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
726 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
727 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
728 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
729 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
730 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
731 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
732}
733
734static void print_secchan_offs(const uint8_t type, uint8_t len, const uint8_t *data)
735{
736 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
737 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
738 else
739 printf(" %d\n", data[0]);
740}
741
742static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data)
743{
744 printf("\n");
745 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
746 printf("\t\t * channel utilisation: %d/255\n", data[2]);
747 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
748}
749
83b4934c
JB
750struct ie_print {
751 const char *name;
752 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data);
753 uint8_t minlen, maxlen;
febeb0c0 754 uint8_t flags;
764fe753
JB
755};
756
83b4934c
JB
757static void print_ie(const struct ie_print *p, const uint8_t type,
758 uint8_t len, const uint8_t *data)
4673a894 759{
83b4934c
JB
760 int i;
761
762 if (!p->print)
763 return;
764
765 printf("\t%s:", p->name);
766 if (len < p->minlen || len > p->maxlen) {
767 if (len > 1) {
768 printf(" <invalid: %d bytes:", len);
769 for (i = 0; i < len; i++)
770 printf(" %.02x", data[i]);
771 printf(">\n");
772 } else if (len)
773 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
774 else
775 printf(" <invalid: no data>\n");
776 return;
777 }
778
779 p->print(type, len, data);
780}
781
782#define PRINT_IGN { \
783 .name = "IGNORE", \
784 .print = NULL, \
785 .minlen = 0, \
786 .maxlen = 255, \
4673a894
JB
787}
788
83b4934c 789static const struct ie_print ieprinters[] = {
febeb0c0
JB
790 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
791 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
014d581a 792 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
575280cc 793 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
febeb0c0 794 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
29f7579e 795 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
febeb0c0
JB
796 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
797 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
a2e61861 798 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
29f7579e 799 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
be7602fb 800 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
29f7579e 801 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
54eb1613 802 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
ca159934 803 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
febeb0c0
JB
804 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
805 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
720583e9 806 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
febeb0c0 807 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
83b4934c
JB
808};
809
810static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data)
811{
812 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
813}
814
1cab57eb
JB
815static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
816{
817 int i;
818 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
819
820 if (len < 19)
821 goto invalid;
822
823 if (data[0] != 1) {
cee4fe20 824 printf("Parameter: not version 1: ");
1cab57eb
JB
825 return false;
826 }
827
89ea706f 828 printf("\t * Parameter version 1");
1cab57eb
JB
829
830 data++;
831
832 if (data[0] & 0x80)
89ea706f 833 printf("\n\t\t * u-APSD");
1cab57eb
JB
834
835 data += 2;
836
837 for (i = 0; i < 4; i++) {
89ea706f 838 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
87181516 839 if (data[0] & 0x10)
1cab57eb
JB
840 printf(" acm");
841 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
842 (1 << (data[1] >> 4)) - 1);
a2a4c265 843 printf(", AIFSN %d", data[0] & 0xf);
1cab57eb 844 if (data[2] | data[3])
cee4fe20 845 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
1cab57eb
JB
846 data += 4;
847 }
848
849 printf("\n");
850 return true;
851
852 invalid:
853 printf("invalid: ");
854 return false;
855}
856
83b4934c 857static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data)
6ff0c93a
MH
858{
859 int i;
860
6ff0c93a
MH
861 switch (data[0]) {
862 case 0x00:
83b4934c 863 printf(" information:");
6ff0c93a
MH
864 break;
865 case 0x01:
1cab57eb
JB
866 if (print_wifi_wmm_param(data + 1, len - 1))
867 return;
6ff0c93a
MH
868 break;
869 default:
83b4934c 870 printf(" type %d:", data[0]);
6ff0c93a
MH
871 break;
872 }
873
1cab57eb
JB
874 for(i = 1; i < len; i++)
875 printf(" %.02x", data[i]);
6ff0c93a
MH
876 printf("\n");
877}
878
a6816965
JM
879static const char * wifi_wps_dev_passwd_id(uint16_t id)
880{
881 switch (id) {
882 case 0:
883 return "Default (PIN)";
884 case 1:
885 return "User-specified";
886 case 2:
887 return "Machine-specified";
888 case 3:
889 return "Rekey";
890 case 4:
891 return "PushButton";
892 case 5:
893 return "Registrar-specified";
894 default:
895 return "??";
896 }
897}
898
83b4934c 899static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data)
4673a894
JB
900{
901 bool first = true;
902 __u16 subtype, sublen;
903
4673a894
JB
904 while (len >= 4) {
905 subtype = (data[0] << 8) + data[1];
906 sublen = (data[2] << 8) + data[3];
907 if (sublen > len)
908 break;
909
910 switch (subtype) {
911 case 0x104a:
912 tab_on_first(&first);
dffc6750 913 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
4673a894
JB
914 break;
915 case 0x1011:
916 tab_on_first(&first);
917 printf("\t * Device name: %.*s\n", sublen, data + 4);
918 break;
a6816965
JM
919 case 0x1012: {
920 uint16_t id;
921 tab_on_first(&first);
922 if (sublen != 2) {
923 printf("\t * Device Password ID: (invalid "
924 "length %d)\n", sublen);
925 break;
926 }
927 id = data[4] << 8 | data[5];
928 printf("\t * Device Password ID: %u (%s)\n",
929 id, wifi_wps_dev_passwd_id(id));
930 break;
931 }
4673a894
JB
932 case 0x1021:
933 tab_on_first(&first);
934 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
935 break;
936 case 0x1023:
937 tab_on_first(&first);
938 printf("\t * Model: %.*s\n", sublen, data + 4);
939 break;
a6816965
JM
940 case 0x1024:
941 tab_on_first(&first);
942 printf("\t * Model Number: %.*s\n", sublen, data + 4);
943 break;
944 case 0x103b: {
945 __u8 val = data[4];
946 tab_on_first(&first);
947 printf("\t * Response Type: %d%s\n",
948 val, val == 3 ? " (AP)" : "");
949 break;
950 }
951 case 0x103c: {
952 __u8 val = data[4];
953 tab_on_first(&first);
954 printf("\t * RF Bands: 0x%x\n", val);
955 break;
956 }
957 case 0x1041: {
958 __u8 val = data[4];
959 tab_on_first(&first);
960 printf("\t * Selected Registrar: 0x%x\n", val);
961 break;
962 }
963 case 0x1042:
964 tab_on_first(&first);
965 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
966 break;
967 case 0x1044: {
968 __u8 val = data[4];
969 tab_on_first(&first);
970 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
971 val,
972 val == 1 ? " (Unconfigured)" : "",
973 val == 2 ? " (Configured)" : "");
974 break;
975 }
09fe09dc
JB
976 case 0x1047:
977 tab_on_first(&first);
978 printf("\t * UUID: ");
979 if (sublen != 16) {
980 printf("(invalid, length=%d)\n", sublen);
981 break;
982 }
983 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
984 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
985 data[4], data[5], data[6], data[7],
986 data[8], data[9], data[10], data[11],
987 data[12], data[13], data[14], data[15],
988 data[16], data[17], data[18], data[19]);
989 break;
a6816965
JM
990 case 0x1054: {
991 tab_on_first(&first);
992 if (sublen != 8) {
993 printf("\t * Primary Device Type: (invalid "
994 "length %d)\n", sublen);
995 break;
996 }
997 printf("\t * Primary Device Type: "
998 "%u-%02x%02x%02x%02x-%u\n",
999 data[4] << 8 | data[5],
1000 data[6], data[7], data[8], data[9],
1001 data[10] << 8 | data[11]);
1002 break;
1003 }
7ee5a865 1004 case 0x1057: {
fe31a22e 1005 __u8 val = data[4];
7ee5a865 1006 tab_on_first(&first);
fe31a22e 1007 printf("\t * AP setup locked: 0x%.2x\n", val);
7ee5a865
JB
1008 break;
1009 }
a6816965
JM
1010 case 0x1008:
1011 case 0x1053: {
4673a894
JB
1012 __u16 meth = (data[4] << 8) + data[5];
1013 bool comma = false;
1014 tab_on_first(&first);
a6816965
JM
1015 printf("\t * %sConfig methods:",
1016 subtype == 0x1053 ? "Selected Registrar ": "");
4673a894
JB
1017#define T(bit, name) do { \
1018 if (meth & (1<<bit)) { \
1019 if (comma) \
1020 printf(","); \
1021 comma = true; \
1022 printf(" " name); \
1023 } } while (0)
1024 T(0, "USB");
1025 T(1, "Ethernet");
1026 T(2, "Label");
1027 T(3, "Display");
1028 T(4, "Ext. NFC");
1029 T(5, "Int. NFC");
1030 T(6, "NFC Intf.");
1031 T(7, "PBC");
1032 T(8, "Keypad");
1033 printf("\n");
1034 break;
1035#undef T
1036 }
2650d46b
JB
1037 default: {
1038 const __u8 *subdata = data + 4;
1039 __u16 tmplen = sublen;
1040
1041 tab_on_first(&first);
1042 printf("\t * Unknown TLV (%#.4x, %d bytes):",
1043 subtype, tmplen);
1044 while (tmplen) {
1045 printf(" %.2x", *subdata);
1046 subdata++;
1047 tmplen--;
1048 }
1049 printf("\n");
4673a894
JB
1050 break;
1051 }
2650d46b 1052 }
4673a894
JB
1053
1054 data += sublen + 4;
1055 len -= sublen + 4;
1056 }
1057
1058 if (len != 0) {
1059 printf("\t\t * bogus tail data (%d):", len);
1060 while (len) {
1061 printf(" %.2x", *data);
1062 data++;
1063 len--;
1064 }
1065 printf("\n");
1066 }
1067}
1068
83b4934c 1069static const struct ie_print wifiprinters[] = {
febeb0c0
JB
1070 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
1071 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
1072 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
4673a894
JB
1073};
1074
9a22374a
JB
1075static inline void print_p2p(const uint8_t type, uint8_t len, const uint8_t *data)
1076{
1077 bool first = true;
1078 __u8 subtype;
1079 __u16 sublen;
1080
1081 while (len >= 3) {
1082 subtype = data[0];
1083 sublen = (data[2] << 8) + data[1];
1084
1085 if (sublen > len - 3)
1086 break;
1087
1088 switch (subtype) {
1089 case 0x02: /* capability */
1090 tab_on_first(&first);
1091 if (sublen < 2) {
1092 printf("\t * malformed capability\n");
1093 break;
1094 }
1095 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
1096 data[3], data[4]);
1097 break;
1098 case 0x0d: /* device info */
1099 if (sublen < 6 + 2 + 8 + 1) {
1100 printf("\t * malformed device info\n");
1101 break;
1102 }
1103 /* fall through for now */
1104 case 0x00: /* status */
1105 case 0x01: /* minor reason */
1106 case 0x03: /* device ID */
1107 case 0x04: /* GO intent */
1108 case 0x05: /* configuration timeout */
1109 case 0x06: /* listen channel */
1110 case 0x07: /* group BSSID */
1111 case 0x08: /* ext listen timing */
1112 case 0x09: /* intended interface address */
1113 case 0x0a: /* manageability */
1114 case 0x0b: /* channel list */
1115 case 0x0c: /* NoA */
1116 case 0x0e: /* group info */
1117 case 0x0f: /* group ID */
1118 case 0x10: /* interface */
1119 case 0x11: /* operating channel */
1120 case 0x12: /* invitation flags */
1121 case 0xdd: /* vendor specific */
1122 default: {
1123 const __u8 *subdata = data + 4;
1124 __u16 tmplen = sublen;
1125
1126 tab_on_first(&first);
1127 printf("\t * Unknown TLV (%#.2x, %d bytes):",
1128 subtype, tmplen);
1129 while (tmplen) {
1130 printf(" %.2x", *subdata);
1131 subdata++;
1132 tmplen--;
1133 }
1134 printf("\n");
1135 break;
1136 }
1137 }
1138
1139 data += sublen + 3;
1140 len -= sublen + 3;
1141 }
1142
1143 if (len != 0) {
1144 tab_on_first(&first);
1145 printf("\t * bogus tail data (%d):", len);
1146 while (len) {
1147 printf(" %.2x", *data);
1148 data++;
1149 len--;
1150 }
1151 printf("\n");
1152 }
1153}
1154
1155static const struct ie_print wfa_printers[] = {
1156 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
1157};
1158
764fe753 1159static void print_vendor(unsigned char len, unsigned char *data,
febeb0c0 1160 bool unknown, enum print_ie_type ptype)
3563f4c5
JB
1161{
1162 int i;
1163
fbf80af5 1164 if (len < 3) {
4673a894 1165 printf("\tVendor specific: <too short> data:");
fbf80af5
JB
1166 for(i = 0; i < len; i++)
1167 printf(" %.02x", data[i]);
1168 printf("\n");
1169 return;
1170 }
1171
3bd60ef1 1172 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
febeb0c0
JB
1173 if (data[3] < ARRAY_SIZE(wifiprinters) &&
1174 wifiprinters[data[3]].name &&
1175 wifiprinters[data[3]].flags & BIT(ptype)) {
83b4934c
JB
1176 print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4);
1177 return;
1178 }
febeb0c0 1179 if (!unknown)
4673a894 1180 return;
3bd60ef1 1181 printf("\tMS/WiFi %#.2x, data:", data[3]);
4673a894
JB
1182 for(i = 0; i < len - 4; i++)
1183 printf(" %.02x", data[i + 4]);
1184 printf("\n");
1185 return;
1186 }
1187
9a22374a
JB
1188 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
1189 if (data[3] < ARRAY_SIZE(wfa_printers) &&
1190 wfa_printers[data[3]].name &&
1191 wfa_printers[data[3]].flags & BIT(ptype)) {
1192 print_ie(&wfa_printers[data[3]], data[3], len - 4, data + 4);
1193 return;
1194 }
1195 if (!unknown)
1196 return;
1197 printf("\tWFA %#.2x, data:", data[3]);
1198 for(i = 0; i < len - 4; i++)
1199 printf(" %.02x", data[i + 4]);
1200 printf("\n");
1201 return;
1202 }
1203
febeb0c0 1204 if (!unknown)
764fe753
JB
1205 return;
1206
fbf80af5 1207 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
3563f4c5 1208 data[0], data[1], data[2]);
fbf80af5
JB
1209 for (i = 3; i < len; i++)
1210 printf(" %.2x", data[i]);
3563f4c5
JB
1211 printf("\n");
1212}
1213
febeb0c0
JB
1214void print_ies(unsigned char *ie, int ielen, bool unknown,
1215 enum print_ie_type ptype)
3563f4c5
JB
1216{
1217 while (ielen >= 2 && ielen >= ie[1]) {
febeb0c0
JB
1218 if (ie[0] < ARRAY_SIZE(ieprinters) &&
1219 ieprinters[ie[0]].name &&
1220 ieprinters[ie[0]].flags & BIT(ptype)) {
83b4934c 1221 print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2);
764fe753 1222 } else if (ie[0] == 221 /* vendor */) {
febeb0c0
JB
1223 print_vendor(ie[1], ie + 2, unknown, ptype);
1224 } else if (unknown) {
3563f4c5
JB
1225 int i;
1226
8086b700 1227 printf("\tUnknown IE (%d):", ie[0]);
3563f4c5 1228 for (i=0; i<ie[1]; i++)
8086b700 1229 printf(" %.2x", ie[2+i]);
3563f4c5
JB
1230 printf("\n");
1231 }
1232 ielen -= ie[1] + 2;
1233 ie += ie[1] + 2;
1234 }
1235}
1236
2e8b82c1
VK
1237static void print_capa_dmg(__u16 capa)
1238{
1239 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1240 case WLAN_CAPABILITY_DMG_TYPE_AP:
1241 printf(" DMG_ESS");
1242 break;
1243 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1244 printf(" DMG_PCP");
1245 break;
1246 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
1247 printf(" DMG_IBSS");
1248 break;
1249 }
1250
1251 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
1252 printf(" CBAP_Only");
1253 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
1254 printf(" CBAP_Src");
1255 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
1256 printf(" Privacy");
1257 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
1258 printf(" ECPAC");
1259 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
1260 printf(" SpectrumMgmt");
1261 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
1262 printf(" RadioMeasure");
1263}
1264
1265static void print_capa_non_dmg(__u16 capa)
1266{
1267 if (capa & WLAN_CAPABILITY_ESS)
1268 printf(" ESS");
1269 if (capa & WLAN_CAPABILITY_IBSS)
1270 printf(" IBSS");
1271 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
1272 printf(" CfPollable");
1273 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
1274 printf(" CfPollReq");
1275 if (capa & WLAN_CAPABILITY_PRIVACY)
1276 printf(" Privacy");
1277 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
1278 printf(" ShortPreamble");
1279 if (capa & WLAN_CAPABILITY_PBCC)
1280 printf(" PBCC");
1281 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
1282 printf(" ChannelAgility");
1283 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
1284 printf(" SpectrumMgmt");
1285 if (capa & WLAN_CAPABILITY_QOS)
1286 printf(" QoS");
1287 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
1288 printf(" ShortSlotTime");
1289 if (capa & WLAN_CAPABILITY_APSD)
1290 printf(" APSD");
1291 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
1292 printf(" RadioMeasure");
1293 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
1294 printf(" DSSS-OFDM");
1295 if (capa & WLAN_CAPABILITY_DEL_BACK)
1296 printf(" DelayedBACK");
1297 if (capa & WLAN_CAPABILITY_IMM_BACK)
1298 printf(" ImmediateBACK");
1299}
1300
3563f4c5
JB
1301static int print_bss_handler(struct nl_msg *msg, void *arg)
1302{
1303 struct nlattr *tb[NL80211_ATTR_MAX + 1];
1304 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1305 struct nlattr *bss[NL80211_BSS_MAX + 1];
1306 char mac_addr[20], dev[20];
1307 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1308 [NL80211_BSS_TSF] = { .type = NLA_U64 },
1309 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1310 [NL80211_BSS_BSSID] = { },
1311 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
1312 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
1313 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
f2e17e1f
JB
1314 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
1315 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
a56117a6 1316 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
c04a78df 1317 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
575280cc 1318 [NL80211_BSS_BEACON_IES] = { },
3563f4c5 1319 };
febeb0c0 1320 struct scan_params *params = arg;
1c5bcd9c 1321 int show = params->show_both_ie_sets ? 2 : 1;
2e8b82c1 1322 bool is_dmg = false;
3563f4c5
JB
1323
1324 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1325 genlmsg_attrlen(gnlh, 0), NULL);
1326
1327 if (!tb[NL80211_ATTR_BSS]) {
5fe70c0e 1328 fprintf(stderr, "bss info missing!\n");
3563f4c5
JB
1329 return NL_SKIP;
1330 }
1331 if (nla_parse_nested(bss, NL80211_BSS_MAX,
1332 tb[NL80211_ATTR_BSS],
1333 bss_policy)) {
5fe70c0e 1334 fprintf(stderr, "failed to parse nested attributes!\n");
3563f4c5
JB
1335 return NL_SKIP;
1336 }
1337
1338 if (!bss[NL80211_BSS_BSSID])
1339 return NL_SKIP;
1340
1341 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
1342 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
a56117a6
JB
1343 printf("BSS %s (on %s)", mac_addr, dev);
1344
1345 if (bss[NL80211_BSS_STATUS]) {
1346 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
1347 case NL80211_BSS_STATUS_AUTHENTICATED:
1348 printf(" -- authenticated");
1349 break;
1350 case NL80211_BSS_STATUS_ASSOCIATED:
1351 printf(" -- associated");
1352 break;
1353 case NL80211_BSS_STATUS_IBSS_JOINED:
1354 printf(" -- joined");
1355 break;
1356 default:
1357 printf(" -- unknown status: %d",
1358 nla_get_u32(bss[NL80211_BSS_STATUS]));
1359 break;
1360 }
1361 }
1362 printf("\n");
3563f4c5 1363
e7109a8a
JB
1364 if (bss[NL80211_BSS_TSF]) {
1365 unsigned long long tsf;
1366 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
1367 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
1368 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
1369 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
1370 }
2e8b82c1
VK
1371 if (bss[NL80211_BSS_FREQUENCY]) {
1372 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
1373 printf("\tfreq: %d\n", freq);
1374 if (freq > 45000)
1375 is_dmg = true;
1376 }
3563f4c5
JB
1377 if (bss[NL80211_BSS_BEACON_INTERVAL])
1378 printf("\tbeacon interval: %d\n",
1379 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
92a04ecd
MH
1380 if (bss[NL80211_BSS_CAPABILITY]) {
1381 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
1382 printf("\tcapability:");
2e8b82c1
VK
1383 if (is_dmg)
1384 print_capa_dmg(capa);
1385 else
1386 print_capa_non_dmg(capa);
92a04ecd
MH
1387 printf(" (0x%.4x)\n", capa);
1388 }
f2e17e1f
JB
1389 if (bss[NL80211_BSS_SIGNAL_MBM]) {
1390 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
1391 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
1392 }
1393 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
1394 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
1395 printf("\tsignal: %d/100\n", s);
1396 }
c04a78df
HS
1397 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
1398 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
1399 printf("\tlast seen: %d ms ago\n", age);
1400 }
c551449a 1401
1c5bcd9c 1402 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
575280cc
JM
1403 if (bss[NL80211_BSS_BEACON_IES])
1404 printf("\tInformation elements from Probe Response "
1405 "frame:\n");
3563f4c5 1406 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
764fe753 1407 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
febeb0c0 1408 params->unknown, params->type);
575280cc 1409 }
1c5bcd9c 1410 if (bss[NL80211_BSS_BEACON_IES] && show--) {
575280cc
JM
1411 printf("\tInformation elements from Beacon frame:\n");
1412 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
1413 nla_len(bss[NL80211_BSS_BEACON_IES]),
1414 params->unknown, params->type);
1415 }
3563f4c5
JB
1416
1417 return NL_SKIP;
1418}
1419
764fe753 1420static struct scan_params scan_params;
3563f4c5 1421
7c37a24d
JB
1422static int handle_scan_dump(struct nl80211_state *state,
1423 struct nl_cb *cb,
3563f4c5 1424 struct nl_msg *msg,
05514f95
JB
1425 int argc, char **argv,
1426 enum id_input id)
3563f4c5 1427{
764fe753
JB
1428 if (argc > 1)
1429 return 1;
1430
1c5bcd9c
JB
1431 memset(&scan_params, 0, sizeof(scan_params));
1432
764fe753
JB
1433 if (argc == 1 && !strcmp(argv[0], "-u"))
1434 scan_params.unknown = true;
575280cc
JM
1435 else if (argc == 1 && !strcmp(argv[0], "-b"))
1436 scan_params.show_both_ie_sets = true;
764fe753 1437
febeb0c0
JB
1438 scan_params.type = PRINT_SCAN;
1439
764fe753
JB
1440 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler,
1441 &scan_params);
3563f4c5
JB
1442 return 0;
1443}
a5fe4ef2
JB
1444
1445static int handle_scan_combined(struct nl80211_state *state,
1446 struct nl_cb *cb,
1447 struct nl_msg *msg,
05514f95
JB
1448 int argc, char **argv,
1449 enum id_input id)
a5fe4ef2 1450{
559a1713 1451 char **trig_argv;
a5fe4ef2
JB
1452 static char *dump_argv[] = {
1453 NULL,
1454 "scan",
1455 "dump",
92649eab 1456 NULL,
a5fe4ef2
JB
1457 };
1458 static const __u32 cmds[] = {
1459 NL80211_CMD_NEW_SCAN_RESULTS,
1460 NL80211_CMD_SCAN_ABORTED,
1461 };
559a1713 1462 int trig_argc, dump_argc, err;
a5fe4ef2 1463
559a1713
JB
1464 if (argc >= 3 && !strcmp(argv[2], "-u")) {
1465 dump_argc = 4;
1466 dump_argv[3] = "-u";
575280cc
JM
1467 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
1468 dump_argc = 4;
1469 dump_argv[3] = "-b";
559a1713
JB
1470 } else
1471 dump_argc = 3;
1472
1473 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
1474 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
1475 if (!trig_argv)
1476 return -ENOMEM;
a5fe4ef2 1477 trig_argv[0] = argv[0];
559a1713
JB
1478 trig_argv[1] = "scan";
1479 trig_argv[2] = "trigger";
1480 int i;
1481 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
1482 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
75f4204c 1483 err = handle_cmd(state, id, trig_argc, trig_argv);
559a1713 1484 free(trig_argv);
a5fe4ef2
JB
1485 if (err)
1486 return err;
1487
61725dbe
JB
1488 /*
1489 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
1490 *
1491 * This code has a bug, which requires creating a separate
1492 * nl80211 socket to fix:
1493 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
1494 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
1495 * before (!) we listen to it, because we only start listening
1496 * after we send our scan request.
1497 *
1498 * Doing it the other way around has a race condition as well,
1499 * if you first open the events socket you may get a notification
1500 * for a previous scan.
1501 *
1502 * The only proper way to fix this would be to listen to events
1503 * before sending the command, and for the kernel to send the
1504 * scan request along with the event, so that you can match up
1505 * whether the scan you requested was finished or aborted (this
1506 * may result in processing a scan that another application
1507 * requested, but that doesn't seem to be a problem).
1508 *
1509 * Alas, the kernel doesn't do that (yet).
1510 */
1511
a5fe4ef2
JB
1512 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
1513 NL80211_CMD_SCAN_ABORTED) {
1514 printf("scan aborted!\n");
1515 return 0;
1516 }
1517
1518 dump_argv[0] = argv[0];
75f4204c 1519 return handle_cmd(state, id, dump_argc, dump_argv);
a5fe4ef2 1520}
ced94d5f 1521TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]", 0, 0,
6ca98d28
JB
1522 CIB_NETDEV, handle_scan_combined,
1523 "Scan on the given frequencies and probe for the given SSIDs\n"
1524 "(or wildcard if not given) unless passive scanning is requested.\n"
64797a7f
JB
1525 "If -u is specified print unknown data in the scan results.\n"
1526 "Specified (vendor) IEs must be well-formed.");
4698bfc2
JB
1527COMMAND(scan, dump, "[-u]",
1528 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
1529 "Dump the current scan results. If -u is specified, print unknown\n"
1530 "data in scan results.");
ced94d5f 1531COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]",
4698bfc2
JB
1532 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
1533 "Trigger a scan on the given frequencies with probing for the given\n"
1534 "SSIDs (or wildcard if not given) unless passive scanning is requested.");