]> git.ipfire.org Git - thirdparty/iw.git/blame - scan.c
iw: remove cb from arguments and simplify valid handler
[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
5085aa2b
JB
73static int parse_random_mac_addr(struct nl_msg *msg, char *arg)
74{
75 char *a_addr, *a_mask, *sep;
76 unsigned char addr[ETH_ALEN], mask[ETH_ALEN];
77 char *addrs = arg + 9;
78
79 if (*addrs != '=')
80 return 0;
81
82 addrs++;
83 sep = strchr(addrs, '/');
84 a_addr = addrs;
85
86 if (!sep)
87 return 1;
88
89 *sep = 0;
90 a_mask = sep + 1;
91 if (mac_addr_a2n(addr, a_addr) || mac_addr_a2n(mask, a_mask))
92 return 1;
93
94 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
95 NLA_PUT(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, mask);
96
97 return 0;
98 nla_put_failure:
99 return -ENOBUFS;
100}
101
b1622287
LC
102int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv)
103{
302a378a 104 struct nl_msg *matchset = NULL, *freqs = NULL, *ssids = NULL;
b1622287
LC
105 struct nlattr *match = NULL;
106 enum {
107 ND_TOPLEVEL,
108 ND_MATCH,
109 ND_FREQS,
302a378a 110 ND_ACTIVE,
b1622287
LC
111 } parse_state = ND_TOPLEVEL;
112 int c = *argc;
113 char *end, **v = *argv;
114 int err = 0, i = 0;
115 unsigned int freq, interval = 0, delay = 0;
302a378a
LC
116 bool have_matchset = false, have_freqs = false, have_ssids = false;
117 bool have_active = false, have_passive = false;
5130bba0 118 uint32_t flags = 0;
b1622287
LC
119
120 matchset = nlmsg_alloc();
121 if (!matchset) {
122 err = -ENOBUFS;
123 goto out;
124 }
125
126 freqs = nlmsg_alloc();
127 if (!freqs) {
128 err = -ENOBUFS;
129 goto out;
130 }
131
302a378a
LC
132 ssids = nlmsg_alloc();
133 if (!ssids) {
134 err = -ENOMEM;
135 goto out;
136 }
137
b1622287
LC
138 while (c) {
139 switch (parse_state) {
140 case ND_TOPLEVEL:
141 if (!strcmp(v[0], "interval")) {
142 c--; v++;
143 if (c == 0) {
144 err = -EINVAL;
145 goto nla_put_failure;
146 }
147
148 if (interval) {
149 err = -EINVAL;
150 goto nla_put_failure;
151 }
152 interval = strtoul(v[0], &end, 10);
153 if (*end || !interval) {
154 err = -EINVAL;
155 goto nla_put_failure;
156 }
157 NLA_PUT_U32(msg,
158 NL80211_ATTR_SCHED_SCAN_INTERVAL,
159 interval);
160 } else if (!strcmp(v[0], "delay")) {
161 c--; v++;
162 if (c == 0) {
163 err = -EINVAL;
164 goto nla_put_failure;
165 }
166
167 if (delay) {
168 err = -EINVAL;
169 goto nla_put_failure;
170 }
171 delay = strtoul(v[0], &end, 10);
172 if (*end) {
173 err = -EINVAL;
174 goto nla_put_failure;
175 }
176 NLA_PUT_U32(msg,
177 NL80211_ATTR_SCHED_SCAN_DELAY,
178 delay);
179 } else if (!strcmp(v[0], "matches")) {
180 parse_state = ND_MATCH;
181 if (have_matchset) {
182 err = -EINVAL;
183 goto nla_put_failure;
184 }
185
186 i = 0;
187 } else if (!strcmp(v[0], "freqs")) {
188 parse_state = ND_FREQS;
189 if (have_freqs) {
190 err = -EINVAL;
191 goto nla_put_failure;
192 }
193
194 have_freqs = true;
195 i = 0;
302a378a
LC
196 } else if (!strcmp(v[0], "active")) {
197 parse_state = ND_ACTIVE;
198 if (have_active || have_passive) {
199 err = -EINVAL;
200 goto nla_put_failure;
201 }
202
203 have_active = true;
204 i = 0;
205 } else if (!strcmp(v[0], "passive")) {
206 if (have_active || have_passive) {
207 err = -EINVAL;
208 goto nla_put_failure;
209 }
210
211 have_passive = true;
5130bba0
LC
212 } else if (!strncmp(v[0], "randomise", 9) ||
213 !strncmp(v[0], "randomize", 9)) {
214 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
215 if (c > 0) {
216 err = parse_random_mac_addr(msg, v[0]);
217 if (err)
218 goto nla_put_failure;
219 }
b1622287
LC
220 } else {
221 /* this element is not for us, so
222 * return to continue parsing.
223 */
224 goto nla_put_failure;
225 }
226 c--; v++;
227
228 break;
229 case ND_MATCH:
230 if (!strcmp(v[0], "ssid")) {
231 c--; v++;
232 if (c == 0) {
233 err = -EINVAL;
234 goto nla_put_failure;
235 }
236
237 /* TODO: for now we can only have an
238 * SSID in the match, so we can start
239 * the match nest here.
240 */
241 match = nla_nest_start(matchset, i);
242 if (!match) {
243 err = -ENOBUFS;
244 goto nla_put_failure;
245 }
246
247 NLA_PUT(matchset,
248 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
249 strlen(v[0]), v[0]);
250 nla_nest_end(matchset, match);
251 match = NULL;
252
253 have_matchset = true;
254 i++;
255 c--; v++;
256 } else {
257 /* other element that cannot be part
258 * of a match indicates the end of the
259 * match. */
260 /* need at least one match in the matchset */
261 if (i == 0) {
262 err = -EINVAL;
263 goto nla_put_failure;
264 }
265
266 parse_state = ND_TOPLEVEL;
267 }
268
269 break;
270 case ND_FREQS:
271 freq = strtoul(v[0], &end, 10);
272 if (*end) {
273 if (i == 0) {
274 err = -EINVAL;
275 goto nla_put_failure;
276 }
277
278 parse_state = ND_TOPLEVEL;
279 } else {
280 NLA_PUT_U32(freqs, i, freq);
281 i++;
282 c--; v++;
283 }
284 break;
302a378a
LC
285 case ND_ACTIVE:
286 if (!strcmp(v[0], "ssid")) {
287 c--; v++;
288 if (c == 0) {
289 err = -EINVAL;
290 goto nla_put_failure;
291 }
292
293 NLA_PUT(ssids,
294 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
295 strlen(v[0]), v[0]);
296
297 have_ssids = true;
298 i++;
299 c--; v++;
300 } else {
301 /* other element that cannot be part
302 * of a match indicates the end of the
303 * active set. */
304 /* need at least one item in the set */
305 if (i == 0) {
306 err = -EINVAL;
307 goto nla_put_failure;
308 }
309
310 parse_state = ND_TOPLEVEL;
311 }
312 break;
b1622287
LC
313 }
314 }
315
302a378a
LC
316 if (!have_ssids)
317 NLA_PUT(ssids, 1, 0, "");
318 if (!have_passive)
319 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
b1622287
LC
320 if (have_freqs)
321 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
322 if (have_matchset)
323 nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
5130bba0
LC
324 if (flags)
325 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
b1622287
LC
326
327nla_put_failure:
328 if (match)
329 nla_nest_end(msg, match);
330 nlmsg_free(freqs);
331 nlmsg_free(matchset);
332
333out:
334 *argc = c;
335 *argv = v;
336 return err;
337}
338
7c37a24d 339static int handle_scan(struct nl80211_state *state,
3563f4c5 340 struct nl_msg *msg,
05514f95
JB
341 int argc, char **argv,
342 enum id_input id)
3563f4c5 343{
559a1713
JB
344 struct nl_msg *ssids = NULL, *freqs = NULL;
345 char *eptr;
3563f4c5 346 int err = -ENOBUFS;
559a1713
JB
347 int i;
348 enum {
349 NONE,
350 FREQ,
64797a7f 351 IES,
559a1713 352 SSID,
851d35c2 353 MESHID,
559a1713
JB
354 DONE,
355 } parse = NONE;
356 int freq;
357 bool passive = false, have_ssids = false, have_freqs = false;
851d35c2 358 size_t ies_len = 0, meshid_len = 0;
359 unsigned char *ies = NULL, *meshid = NULL, *tmpies;
5085aa2b 360 unsigned int flags = 0;
3563f4c5
JB
361
362 ssids = nlmsg_alloc();
363 if (!ssids)
364 return -ENOMEM;
559a1713
JB
365
366 freqs = nlmsg_alloc();
367 if (!freqs) {
368 nlmsg_free(ssids);
369 return -ENOMEM;
370 }
371
372 for (i = 0; i < argc; i++) {
559a1713
JB
373 switch (parse) {
374 case NONE:
64797a7f
JB
375 if (strcmp(argv[i], "freq") == 0) {
376 parse = FREQ;
377 have_freqs = true;
378 break;
379 } else if (strcmp(argv[i], "ies") == 0) {
380 parse = IES;
381 break;
fe862239 382 } else if (strcmp(argv[i], "lowpri") == 0) {
fe862239
SL
383 flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
384 break;
385 } else if (strcmp(argv[i], "flush") == 0) {
fe862239
SL
386 flags |= NL80211_SCAN_FLAG_FLUSH;
387 break;
ced94d5f 388 } else if (strcmp(argv[i], "ap-force") == 0) {
ced94d5f
AQ
389 flags |= NL80211_SCAN_FLAG_AP;
390 break;
5085aa2b
JB
391 } else if (strncmp(argv[i], "randomise", 9) == 0 ||
392 strncmp(argv[i], "randomize", 9) == 0) {
393 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
394 err = parse_random_mac_addr(msg, argv[i]);
395 if (err)
396 goto nla_put_failure;
397 break;
64797a7f
JB
398 } else if (strcmp(argv[i], "ssid") == 0) {
399 parse = SSID;
400 have_ssids = true;
401 break;
402 } else if (strcmp(argv[i], "passive") == 0) {
403 parse = DONE;
404 passive = true;
405 break;
851d35c2 406 } else if (strcmp(argv[i], "meshid") == 0) {
407 parse = MESHID;
408 break;
64797a7f 409 }
559a1713
JB
410 case DONE:
411 return 1;
412 case FREQ:
413 freq = strtoul(argv[i], &eptr, 10);
cc12e895
JB
414 if (eptr != argv[i] + strlen(argv[i])) {
415 /* failed to parse as number -- maybe a tag? */
416 i--;
417 parse = NONE;
418 continue;
419 }
559a1713 420 NLA_PUT_U32(freqs, i, freq);
64797a7f
JB
421 break;
422 case IES:
851d35c2 423 ies = parse_hex(argv[i], &ies_len);
64797a7f
JB
424 if (!ies)
425 goto nla_put_failure;
64797a7f 426 parse = NONE;
559a1713
JB
427 break;
428 case SSID:
429 NLA_PUT(ssids, i, strlen(argv[i]), argv[i]);
430 break;
851d35c2 431 case MESHID:
432 meshid_len = strlen(argv[i]);
433 meshid = (unsigned char *) malloc(meshid_len + 2);
434 if (!meshid)
435 goto nla_put_failure;
436 meshid[0] = 114; /* mesh element id */
437 meshid[1] = meshid_len;
438 memcpy(&meshid[2], argv[i], meshid_len);
439 meshid_len += 2;
440 parse = NONE;
441 break;
442 }
443 }
444
445 if (ies || meshid) {
446 tmpies = (unsigned char *) malloc(ies_len + meshid_len);
447 if (!tmpies)
448 goto nla_put_failure;
449 if (ies) {
450 memcpy(tmpies, ies, ies_len);
451 free(ies);
452 }
453 if (meshid) {
454 memcpy(&tmpies[ies_len], meshid, meshid_len);
455 free(meshid);
559a1713 456 }
851d35c2 457 NLA_PUT(msg, NL80211_ATTR_IE, ies_len + meshid_len, tmpies);
458 free(tmpies);
559a1713
JB
459 }
460
461 if (!have_ssids)
462 NLA_PUT(ssids, 1, 0, "");
463 if (!passive)
464 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
465
466 if (have_freqs)
467 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
fe862239
SL
468 if (flags)
469 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
3563f4c5
JB
470
471 err = 0;
472 nla_put_failure:
473 nlmsg_free(ssids);
559a1713 474 nlmsg_free(freqs);
3563f4c5
JB
475 return err;
476}
3563f4c5 477
857d966e
MH
478static void tab_on_first(bool *first)
479{
480 if (!*first)
481 printf("\t");
482 else
483 *first = false;
484}
485
83b4934c 486static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5 487{
83b4934c 488 printf(" ");
748f8489 489 print_ssid_escaped(len, data);
3563f4c5
JB
490 printf("\n");
491}
492
ca159934 493#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
1fd19c39
CL
494#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
495
83b4934c 496static void print_supprates(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5
JB
497{
498 int i;
499
83b4934c 500 printf(" ");
3563f4c5 501
83b4934c 502 for (i = 0; i < len; i++) {
3563f4c5 503 int r = data[i] & 0x7f;
1fd19c39 504
ca159934
JB
505 if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
506 printf("VHT");
507 else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
1fd19c39
CL
508 printf("HT");
509 else
510 printf("%d.%d", r/2, 5*(r&1));
511
512 printf("%s ", data[i] & 0x80 ? "*" : "");
3563f4c5
JB
513 }
514 printf("\n");
515}
516
83b4934c 517static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data)
3563f4c5 518{
83b4934c 519 printf(" channel %d\n", data[0]);
3563f4c5
JB
520}
521
2b690f0a 522static const char *country_env_str(char environment)
b7e8fa37 523{
2b690f0a 524 switch (environment) {
b7e8fa37 525 case 'I':
2b690f0a 526 return "Indoor only";
b7e8fa37 527 case 'O':
2b690f0a 528 return "Outdoor only";
b6c0d634 529 case ' ':
2b690f0a 530 return "Indoor/Outdoor";
b6c0d634 531 default:
2b690f0a 532 return "bogus";
b7e8fa37 533 }
2b690f0a
LR
534}
535
536static void print_country(const uint8_t type, uint8_t len, const uint8_t *data)
537{
538 printf(" %.*s", 2, data);
539
540 printf("\tEnvironment: %s\n", country_env_str(data[2]));
541
542 data += 3;
543 len -= 3;
544
545 if (len < 3) {
546 printf("\t\tNo country IE triplets present\n");
547 return;
548 }
549
550 while (len >= 3) {
551 int end_channel;
72725719 552 union ieee80211_country_ie_triplet *triplet = (void *) data;
2b690f0a
LR
553
554 if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
555 printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
556 triplet->ext.reg_extension_id,
557 triplet->ext.reg_class,
558 triplet->ext.coverage_class,
559 triplet->ext.coverage_class * 450);
560
561 data += 3;
562 len -= 3;
563 continue;
564 }
565
566 /* 2 GHz */
567 if (triplet->chans.first_channel <= 14)
568 end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
569 else
570 end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
571
a9859d30 572 printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
2b690f0a
LR
573
574 data += 3;
575 len -= 3;
576 }
577
578 return;
b7e8fa37
MH
579}
580
d1563a1b
MH
581static void print_powerconstraint(const uint8_t type, uint8_t len, const uint8_t *data)
582{
583 printf(" %d dB\n", data[0]);
584}
585
792172b0
JM
586static void print_tpcreport(const uint8_t type, uint8_t len, const uint8_t *data)
587{
588 printf(" TX power: %d dBm\n", data[0]);
589 /* printf(" Link Margin (%d dB) is reserved in Beacons\n", data[1]); */
590}
591
83b4934c 592static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data)
fc4d1484
MH
593{
594 if (data[0] == 0x00)
83b4934c 595 printf(" <no flags>");
fc4d1484
MH
596 if (data[0] & 0x01)
597 printf(" NonERP_Present");
598 if (data[0] & 0x02)
599 printf(" Use_Protection");
600 if (data[0] & 0x04)
601 printf(" Barker_Preamble_Mode");
602 printf("\n");
603}
604
83b4934c 605static void print_cipher(const uint8_t *data)
857d966e 606{
3bd60ef1 607 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 608 switch (data[3]) {
510e0e2f 609 case 0:
857d966e
MH
610 printf("Use group cipher suite");
611 break;
510e0e2f 612 case 1:
857d966e
MH
613 printf("WEP-40");
614 break;
510e0e2f 615 case 2:
857d966e
MH
616 printf("TKIP");
617 break;
510e0e2f 618 case 4:
857d966e
MH
619 printf("CCMP");
620 break;
510e0e2f 621 case 5:
857d966e
MH
622 printf("WEP-104");
623 break;
624 default:
332769c6 625 printf("%.02x-%.02x-%.02x:%d",
5594fd23 626 data[0], data[1] ,data[2], data[3]);
857d966e
MH
627 break;
628 }
629 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
630 switch (data[3]) {
510e0e2f 631 case 0:
857d966e
MH
632 printf("Use group cipher suite");
633 break;
510e0e2f 634 case 1:
857d966e
MH
635 printf("WEP-40");
636 break;
510e0e2f 637 case 2:
857d966e
MH
638 printf("TKIP");
639 break;
510e0e2f 640 case 4:
857d966e
MH
641 printf("CCMP");
642 break;
510e0e2f 643 case 5:
857d966e
MH
644 printf("WEP-104");
645 break;
510e0e2f 646 case 6:
857d966e
MH
647 printf("AES-128-CMAC");
648 break;
f188ab67
BG
649 case 7:
650 printf("NO-GROUP");
651 break;
a8b3da9d
VK
652 case 8:
653 printf("GCMP");
654 break;
857d966e 655 default:
332769c6 656 printf("%.02x-%.02x-%.02x:%d",
5594fd23 657 data[0], data[1] ,data[2], data[3]);
857d966e
MH
658 break;
659 }
660 } else
332769c6 661 printf("%.02x-%.02x-%.02x:%d",
5594fd23 662 data[0], data[1] ,data[2], data[3]);
857d966e
MH
663}
664
83b4934c 665static void print_auth(const uint8_t *data)
857d966e 666{
3bd60ef1 667 if (memcmp(data, ms_oui, 3) == 0) {
857d966e 668 switch (data[3]) {
510e0e2f 669 case 1:
857d966e
MH
670 printf("IEEE 802.1X");
671 break;
510e0e2f 672 case 2:
857d966e
MH
673 printf("PSK");
674 break;
675 default:
332769c6 676 printf("%.02x-%.02x-%.02x:%d",
5594fd23 677 data[0], data[1] ,data[2], data[3]);
857d966e
MH
678 break;
679 }
680 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
681 switch (data[3]) {
510e0e2f 682 case 1:
857d966e
MH
683 printf("IEEE 802.1X");
684 break;
510e0e2f 685 case 2:
857d966e
MH
686 printf("PSK");
687 break;
0fe1c415
JB
688 case 3:
689 printf("FT/IEEE 802.1X");
690 break;
691 case 4:
692 printf("FT/PSK");
693 break;
694 case 5:
695 printf("IEEE 802.1X/SHA-256");
696 break;
697 case 6:
698 printf("PSK/SHA-256");
699 break;
4361eef3
JM
700 case 7:
701 printf("TDLS/TPK");
702 break;
857d966e 703 default:
332769c6 704 printf("%.02x-%.02x-%.02x:%d",
5594fd23 705 data[0], data[1] ,data[2], data[3]);
857d966e
MH
706 break;
707 }
f188ab67
BG
708 } else if (memcmp(data, wfa_oui, 3) == 0) {
709 switch (data[3]) {
710 case 1:
711 printf("OSEN");
712 break;
713 default:
714 printf("%.02x-%.02x-%.02x:%d",
715 data[0], data[1] ,data[2], data[3]);
716 break;
717 }
857d966e 718 } else
332769c6 719 printf("%.02x-%.02x-%.02x:%d",
5594fd23 720 data[0], data[1] ,data[2], data[3]);
857d966e
MH
721}
722
f188ab67
BG
723static void _print_rsn_ie(const char *defcipher, const char *defauth,
724 uint8_t len, const uint8_t *data, int is_osen)
857d966e
MH
725{
726 bool first = true;
f188ab67 727 __u16 count, capa;
857d966e
MH
728 int i;
729
f188ab67
BG
730 if (!is_osen) {
731 __u16 version;
732 version = data[0] + (data[1] << 8);
733 tab_on_first(&first);
734 printf("\t * Version: %d\n", version);
857d966e 735
f188ab67
BG
736 data += 2;
737 len -= 2;
738 }
857d966e
MH
739
740 if (len < 4) {
741 tab_on_first(&first);
742 printf("\t * Group cipher: %s\n", defcipher);
743 printf("\t * Pairwise ciphers: %s\n", defcipher);
744 return;
745 }
746
747 tab_on_first(&first);
748 printf("\t * Group cipher: ");
749 print_cipher(data);
750 printf("\n");
751
752 data += 4;
753 len -= 4;
754
755 if (len < 2) {
756 tab_on_first(&first);
757 printf("\t * Pairwise ciphers: %s\n", defcipher);
758 return;
759 }
760
761 count = data[0] | (data[1] << 8);
31d477fb
MH
762 if (2 + (count * 4) > len)
763 goto invalid;
764
857d966e
MH
765 tab_on_first(&first);
766 printf("\t * Pairwise ciphers:");
31d477fb 767 for (i = 0; i < count; i++) {
857d966e
MH
768 printf(" ");
769 print_cipher(data + 2 + (i * 4));
770 }
771 printf("\n");
772
773 data += 2 + (count * 4);
774 len -= 2 + (count * 4);
775
776 if (len < 2) {
777 tab_on_first(&first);
778 printf("\t * Authentication suites: %s\n", defauth);
779 return;
780 }
781
782 count = data[0] | (data[1] << 8);
31d477fb
MH
783 if (2 + (count * 4) > len)
784 goto invalid;
785
857d966e
MH
786 tab_on_first(&first);
787 printf("\t * Authentication suites:");
83b4934c 788 for (i = 0; i < count; i++) {
857d966e
MH
789 printf(" ");
790 print_auth(data + 2 + (i * 4));
791 }
792 printf("\n");
793
794 data += 2 + (count * 4);
795 len -= 2 + (count * 4);
796
6a4f24e8
JB
797 if (len >= 2) {
798 capa = data[0] | (data[1] << 8);
799 tab_on_first(&first);
cadbe89a
JB
800 printf("\t * Capabilities:");
801 if (capa & 0x0001)
802 printf(" PreAuth");
803 if (capa & 0x0002)
804 printf(" NoPairwise");
805 switch ((capa & 0x000c) >> 2) {
806 case 0:
4361eef3 807 printf(" 1-PTKSA-RC");
cadbe89a
JB
808 break;
809 case 1:
810 printf(" 2-PTKSA-RC");
811 break;
812 case 2:
813 printf(" 4-PTKSA-RC");
814 break;
815 case 3:
816 printf(" 16-PTKSA-RC");
817 break;
818 }
819 switch ((capa & 0x0030) >> 4) {
820 case 0:
4361eef3 821 printf(" 1-GTKSA-RC");
cadbe89a
JB
822 break;
823 case 1:
824 printf(" 2-GTKSA-RC");
825 break;
826 case 2:
827 printf(" 4-GTKSA-RC");
828 break;
829 case 3:
830 printf(" 16-GTKSA-RC");
831 break;
832 }
833 if (capa & 0x0040)
834 printf(" MFP-required");
835 if (capa & 0x0080)
836 printf(" MFP-capable");
837 if (capa & 0x0200)
838 printf(" Peerkey-enabled");
839 if (capa & 0x0400)
840 printf(" SPP-AMSDU-capable");
841 if (capa & 0x0800)
842 printf(" SPP-AMSDU-required");
843 printf(" (0x%.4x)\n", capa);
6a4f24e8
JB
844 data += 2;
845 len -= 2;
846 }
31d477fb 847
5ba3f523
JB
848 if (len >= 2) {
849 int pmkid_count = data[0] | (data[1] << 8);
850
851 if (len >= 2 + 16 * pmkid_count) {
852 tab_on_first(&first);
853 printf("\t * %d PMKIDs\n", pmkid_count);
854 /* not printing PMKID values */
855 data += 2 + 16 * pmkid_count;
856 len -= 2 + 16 * pmkid_count;
857 } else
858 goto invalid;
859 }
860
861 if (len >= 4) {
862 tab_on_first(&first);
863 printf("\t * Group mgmt cipher suite: ");
864 print_cipher(data);
865 printf("\n");
866 data += 4;
867 len -= 4;
868 }
869
cadbe89a 870 invalid:
31d477fb
MH
871 if (len != 0) {
872 printf("\t\t * bogus tail data (%d):", len);
873 while (len) {
874 printf(" %.2x", *data);
875 data++;
876 len--;
877 }
878 printf("\n");
879 }
857d966e
MH
880}
881
f188ab67
BG
882static void print_rsn_ie(const char *defcipher, const char *defauth,
883 uint8_t len, const uint8_t *data)
884{
885 _print_rsn_ie(defcipher, defauth, len, data, 0);
886}
887
888static void print_osen_ie(const char *defcipher, const char *defauth,
889 uint8_t len, const uint8_t *data)
890{
891 printf("\n\t");
892 _print_rsn_ie(defcipher, defauth, len, data, 1);
893}
894
83b4934c 895static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data)
857d966e 896{
83b4934c 897 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
857d966e
MH
898}
899
0c445c24
LR
900static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
901{
357c1a5d 902 printf("\n");
7ddfb679
JB
903 print_ht_capability(data[0] | (data[1] << 8));
904 print_ampdu_length(data[2] & 3);
c79c7464 905 print_ampdu_spacing((data[2] >> 2) & 7);
7ddfb679 906 print_ht_mcs(data + 3);
0c445c24
LR
907}
908
489f0ea9
BG
909static const char* ntype_11u(uint8_t t)
910{
911 switch (t) {
912 case 0: return "Private";
913 case 1: return "Private with Guest";
914 case 2: return "Chargeable Public";
915 case 3: return "Free Public";
916 case 4: return "Personal Device";
917 case 5: return "Emergency Services Only";
918 case 14: return "Test or Experimental";
919 case 15: return "Wildcard";
920 default: return "Reserved";
921 }
922}
923
924static const char* vgroup_11u(uint8_t t)
925{
926 switch (t) {
927 case 0: return "Unspecified";
928 case 1: return "Assembly";
929 case 2: return "Business";
930 case 3: return "Educational";
931 case 4: return "Factory and Industrial";
932 case 5: return "Institutional";
933 case 6: return "Mercantile";
934 case 7: return "Residential";
935 case 8: return "Storage";
936 case 9: return "Utility and Miscellaneous";
937 case 10: return "Vehicular";
938 case 11: return "Outdoor";
939 default: return "Reserved";
940 }
941}
942
943static void print_interworking(const uint8_t type, uint8_t len, const uint8_t *data)
944{
945 /* See Section 7.3.2.92 in the 802.11u spec. */
946 printf("\n");
947 if (len >= 1) {
948 uint8_t ano = data[0];
949 printf("\t\tNetwork Options: 0x%hx\n", (unsigned short)(ano));
950 printf("\t\t\tNetwork Type: %i (%s)\n",
951 (int)(ano & 0xf), ntype_11u(ano & 0xf));
952 if (ano & (1<<4))
953 printf("\t\t\tInternet\n");
954 if (ano & (1<<5))
955 printf("\t\t\tASRA\n");
956 if (ano & (1<<6))
957 printf("\t\t\tESR\n");
958 if (ano & (1<<7))
959 printf("\t\t\tUESA\n");
960 }
961 if ((len == 3) || (len == 9)) {
962 printf("\t\tVenue Group: %i (%s)\n",
963 (int)(data[1]), vgroup_11u(data[1]));
964 printf("\t\tVenue Type: %i\n", (int)(data[2]));
965 }
966 if (len == 9)
967 printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
968 data[3], data[4], data[5], data[6], data[7], data[8]);
969 else if (len == 7)
970 printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
971 data[1], data[2], data[3], data[4], data[5], data[6]);
972}
973
3f818f6d
BG
974static void print_11u_advert(const uint8_t type, uint8_t len, const uint8_t *data)
975{
976 /* See Section 7.3.2.93 in the 802.11u spec. */
977 /* TODO: This code below does not decode private protocol IDs */
978 int idx = 0;
979 printf("\n");
980 while (idx < (len - 1)) {
981 uint8_t qri = data[idx];
982 uint8_t proto_id = data[idx + 1];
983 printf("\t\tQuery Response Info: 0x%hx\n", (unsigned short)(qri));
984 printf("\t\t\tQuery Response Length Limit: %i\n",
985 (qri & 0x7f));
986 if (qri & (1<<7))
987 printf("\t\t\tPAME-BI\n");
988 switch(proto_id) {
989 case 0:
990 printf("\t\t\tANQP\n"); break;
991 case 1:
992 printf("\t\t\tMIH Information Service\n"); break;
993 case 2:
994 printf("\t\t\tMIH Command and Event Services Capability Discovery\n"); break;
995 case 3:
996 printf("\t\t\tEmergency Alert System (EAS)\n"); break;
997 case 221:
998 printf("\t\t\tVendor Specific\n"); break;
999 default:
1000 printf("\t\t\tReserved: %i\n", proto_id); break;
1001 }
1002 idx += 2;
1003 }
1004}
1005
bc9e14f1
BG
1006static void print_11u_rcon(const uint8_t type, uint8_t len, const uint8_t *data)
1007{
1008 /* See Section 7.3.2.96 in the 802.11u spec. */
1009 int idx = 0;
1010 int ln0 = data[1] & 0xf;
1011 int ln1 = ((data[1] & 0xf0) >> 4);
1012 int ln2 = 0;
1013 printf("\n");
1014
1015 if (ln1)
1016 ln2 = len - 2 - ln0 - ln1;
1017
1018 printf("\t\tANQP OIs: %i\n", data[0]);
1019
1020 if (ln0 > 0) {
1021 printf("\t\tOI 1: ");
1022 if (2 + ln0 > len) {
1023 printf("Invalid IE length.\n");
1024 } else {
1025 for (idx = 0; idx < ln0; idx++) {
1026 printf("%02hx", data[2 + idx]);
1027 }
1028 printf("\n");
1029 }
1030 }
1031
1032 if (ln1 > 0) {
1033 printf("\t\tOI 2: ");
1034 if (2 + ln0 + ln1 > len) {
1035 printf("Invalid IE length.\n");
1036 } else {
1037 for (idx = 0; idx < ln1; idx++) {
1038 printf("%02hx", data[2 + ln0 + idx]);
1039 }
1040 printf("\n");
1041 }
1042 }
1043
1044 if (ln2 > 0) {
1045 printf("\t\tOI 3: ");
1046 if (2 + ln0 + ln1 + ln2 > len) {
1047 printf("Invalid IE length.\n");
1048 } else {
1049 for (idx = 0; idx < ln2; idx++) {
1050 printf("%02hx", data[2 + ln0 + ln1 + idx]);
1051 }
1052 printf("\n");
1053 }
1054 }
1055}
1056
29f7579e
JB
1057static const char *ht_secondary_offset[4] = {
1058 "no secondary",
1059 "above",
1060 "[reserved!]",
1061 "below",
1062};
1063
be7602fb
JB
1064static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data)
1065{
be7602fb
JB
1066 static const char *protection[4] = {
1067 "no",
1068 "nonmember",
1069 "20 MHz",
1070 "non-HT mixed",
1071 };
1072 static const char *sta_chan_width[2] = {
1073 "20 MHz",
1074 "any",
1075 };
1076
1077 printf("\n");
1078 printf("\t\t * primary channel: %d\n", data[0]);
1079 printf("\t\t * secondary channel offset: %s\n",
29f7579e 1080 ht_secondary_offset[data[1] & 0x3]);
be7602fb
JB
1081 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
1082 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
1083 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
1084 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
1085 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
1086 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
1087 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
1088 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
1089 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
1090 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
1091 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
1092}
1093
83b4934c 1094static void print_capabilities(const uint8_t type, uint8_t len, const uint8_t *data)
9b880b00 1095{
31d2d259
JB
1096 int i, base, bit;
1097 bool first = true;
1098
1099
1100 for (i = 0; i < len; i++) {
1101 base = i * 8;
1102
1103 for (bit = 0; bit < 8; bit++) {
1104 if (!(data[i] & (1 << bit)))
1105 continue;
1106
1107 if (!first)
1108 printf(",");
1109 else
1110 first = false;
1111
70c649d2
JB
1112#define CAPA(bit, name) case bit: printf(" " name); break
1113
31d2d259 1114 switch (bit + base) {
70c649d2
JB
1115 CAPA(0, "HT Information Exchange Supported");
1116 CAPA(1, "reserved (On-demand Beacon)");
1117 CAPA(2, "Extended Channel Switching");
1118 CAPA(3, "reserved (Wave Indication)");
1119 CAPA(4, "PSMP Capability");
1120 CAPA(5, "reserved (Service Interval Granularity)");
1121 CAPA(6, "S-PSMP Capability");
1122 CAPA(7, "Event");
1123 CAPA(8, "Diagnostics");
1124 CAPA(9, "Multicast Diagnostics");
1125 CAPA(10, "Location Tracking");
1126 CAPA(11, "FMS");
1127 CAPA(12, "Proxy ARP Service");
1128 CAPA(13, "Collocated Interference Reporting");
1129 CAPA(14, "Civic Location");
1130 CAPA(15, "Geospatial Location");
1131 CAPA(16, "TFS");
1132 CAPA(17, "WNM-Sleep Mode");
1133 CAPA(18, "TIM Broadcast");
1134 CAPA(19, "BSS Transition");
1135 CAPA(20, "QoS Traffic Capability");
1136 CAPA(21, "AC Station Count");
1137 CAPA(22, "Multiple BSSID");
1138 CAPA(23, "Timing Measurement");
1139 CAPA(24, "Channel Usage");
1140 CAPA(25, "SSID List");
1141 CAPA(26, "DMS");
1142 CAPA(27, "UTC TSF Offset");
1143 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
1144 CAPA(29, "TDLS Peer PSM Support");
1145 CAPA(30, "TDLS channel switching");
1146 CAPA(31, "Interworking");
1147 CAPA(32, "QoS Map");
1148 CAPA(33, "EBR");
1149 CAPA(34, "SSPN Interface");
1150 CAPA(35, "Reserved");
1151 CAPA(36, "MSGCF Capability");
1152 CAPA(37, "TDLS Support");
1153 CAPA(38, "TDLS Prohibited");
1154 CAPA(39, "TDLS Channel Switching Prohibited");
1155 CAPA(40, "Reject Unadmitted Frame");
1156 CAPA(44, "Identifier Location");
1157 CAPA(45, "U-APSD Coexistence");
1158 CAPA(46, "WNM-Notification");
1159 CAPA(47, "Reserved");
1160 CAPA(48, "UTF-8 SSID");
31d2d259
JB
1161 default:
1162 printf(" %d", bit);
1163 break;
1164 }
70c649d2 1165#undef CAPA
31d2d259
JB
1166 }
1167 }
9b880b00 1168
9b880b00
MH
1169 printf("\n");
1170}
1171
575280cc
JM
1172static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data)
1173{
1174 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
1175 "Bitmap[0] 0x%x",
1176 data[0], data[1], data[2], data[3]);
1177 if (len - 4)
1178 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
1179 printf("\n");
1180}
1181
792172b0
JM
1182static void print_ibssatim(const uint8_t type, uint8_t len, const uint8_t *data)
1183{
1184 printf(" %d TUs", (data[1] << 8) + data[0]);
1185}
1186
54eb1613
JB
1187static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
1188{
1189 printf("\n");
1190 print_vht_info(data[0] | (data[1] << 8) |
1191 (data[2] << 16) | (data[3] << 24),
1192 data + 4);
1193}
1194
ca159934
JB
1195static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data)
1196{
1197 const char *chandwidths[] = {
1198 [0] = "20 or 40 MHz",
1199 [1] = "80 MHz",
1200 [3] = "80+80 MHz",
1201 [2] = "160 MHz",
1202 };
1203
1204 printf("\n");
1205 printf("\t\t * channel width: %d (%s)\n", data[0],
1206 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
1207 printf("\t\t * center freq segment 1: %d\n", data[1]);
1208 printf("\t\t * center freq segment 2: %d\n", data[2]);
1209 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
1210}
1211
29f7579e
JB
1212static void print_obss_scan_params(const uint8_t type, uint8_t len, const uint8_t *data)
1213{
1214 printf("\n");
1215 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
1216 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
1217 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
1218 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
1219 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
1220 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
1221 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
1222 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
1223}
1224
1225static void print_secchan_offs(const uint8_t type, uint8_t len, const uint8_t *data)
1226{
1227 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
1228 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
1229 else
1230 printf(" %d\n", data[0]);
1231}
1232
1233static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data)
1234{
1235 printf("\n");
1236 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
1237 printf("\t\t * channel utilisation: %d/255\n", data[2]);
1238 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
1239}
1240
2d86b0f3
CYY
1241static void print_mesh_conf(const uint8_t type, uint8_t len, const uint8_t *data)
1242{
1243 printf("\n");
1244 printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
1245 printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
1246 printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
1247 printf("\t\t * Synchronization Method ID: %d\n", data[3]);
1248 printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
1249 printf("\t\t * Mesh Formation Info:\n");
1250 printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
1251 if (data[5] & 0x01)
1252 printf("\t\t\t Connected to Mesh Gate\n");
1253 if (data[5] & 0x80)
1254 printf("\t\t\t Connected to AS\n");
1255 printf("\t\t * Mesh Capability\n");
1256 if (data[6] & 0x01)
1257 printf("\t\t\t Accepting Additional Mesh Peerings\n");
1258 if (data[6] & 0x02)
1259 printf("\t\t\t MCCA Supported\n");
1260 if (data[6] & 0x04)
1261 printf("\t\t\t MCCA Enabled\n");
1262 if (data[6] & 0x08)
1263 printf("\t\t\t Forwarding\n");
1264 if (data[6] & 0x10)
1265 printf("\t\t\t MBCA Supported\n");
1266 if (data[6] & 0x20)
1267 printf("\t\t\t TBTT Adjusting\n");
1268 if (data[6] & 0x40)
1269 printf("\t\t\t Mesh Power Save Level\n");
1270}
1271
83b4934c
JB
1272struct ie_print {
1273 const char *name;
1274 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data);
1275 uint8_t minlen, maxlen;
febeb0c0 1276 uint8_t flags;
764fe753
JB
1277};
1278
83b4934c
JB
1279static void print_ie(const struct ie_print *p, const uint8_t type,
1280 uint8_t len, const uint8_t *data)
4673a894 1281{
83b4934c
JB
1282 int i;
1283
1284 if (!p->print)
1285 return;
1286
1287 printf("\t%s:", p->name);
1288 if (len < p->minlen || len > p->maxlen) {
1289 if (len > 1) {
1290 printf(" <invalid: %d bytes:", len);
1291 for (i = 0; i < len; i++)
1292 printf(" %.02x", data[i]);
1293 printf(">\n");
1294 } else if (len)
1295 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
1296 else
1297 printf(" <invalid: no data>\n");
1298 return;
1299 }
1300
1301 p->print(type, len, data);
1302}
1303
1304#define PRINT_IGN { \
1305 .name = "IGNORE", \
1306 .print = NULL, \
1307 .minlen = 0, \
1308 .maxlen = 255, \
4673a894
JB
1309}
1310
83b4934c 1311static const struct ie_print ieprinters[] = {
febeb0c0
JB
1312 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
1313 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
014d581a 1314 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
575280cc 1315 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
792172b0 1316 [6] = { "IBSS ATIM window", print_ibssatim, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1317 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
29f7579e 1318 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
febeb0c0 1319 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
792172b0 1320 [35] = { "TPC report", print_tpcreport, 2, 2, BIT(PRINT_SCAN), },
febeb0c0 1321 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
a2e61861 1322 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
792172b0 1323 [47] = { "ERP D4.0", print_erp, 1, 255, BIT(PRINT_SCAN), },
29f7579e 1324 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
be7602fb 1325 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
29f7579e 1326 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
54eb1613 1327 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
ca159934 1328 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
febeb0c0
JB
1329 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
1330 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
2d86b0f3 1331 [113] = { "MESH Configuration", print_mesh_conf, 7, 7, BIT(PRINT_SCAN), },
720583e9 1332 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
febeb0c0 1333 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
489f0ea9 1334 [107] = { "802.11u Interworking", print_interworking, 0, 255, BIT(PRINT_SCAN), },
3f818f6d 1335 [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
bc9e14f1 1336 [111] = { "802.11u Roaming Consortium", print_11u_rcon, 0, 255, BIT(PRINT_SCAN), },
83b4934c
JB
1337};
1338
1339static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data)
1340{
1341 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
1342}
1343
f188ab67
BG
1344static void print_wifi_osen(const uint8_t type, uint8_t len, const uint8_t *data)
1345{
1346 print_osen_ie("OSEN", "OSEN", len, data);
1347}
1348
1cab57eb
JB
1349static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
1350{
1351 int i;
1352 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
1353
1354 if (len < 19)
1355 goto invalid;
1356
1357 if (data[0] != 1) {
cee4fe20 1358 printf("Parameter: not version 1: ");
1cab57eb
JB
1359 return false;
1360 }
1361
89ea706f 1362 printf("\t * Parameter version 1");
1cab57eb
JB
1363
1364 data++;
1365
1366 if (data[0] & 0x80)
89ea706f 1367 printf("\n\t\t * u-APSD");
1cab57eb
JB
1368
1369 data += 2;
1370
1371 for (i = 0; i < 4; i++) {
89ea706f 1372 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
87181516 1373 if (data[0] & 0x10)
1cab57eb
JB
1374 printf(" acm");
1375 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
1376 (1 << (data[1] >> 4)) - 1);
a2a4c265 1377 printf(", AIFSN %d", data[0] & 0xf);
1cab57eb 1378 if (data[2] | data[3])
cee4fe20 1379 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
1cab57eb
JB
1380 data += 4;
1381 }
1382
1383 printf("\n");
1384 return true;
1385
1386 invalid:
1387 printf("invalid: ");
1388 return false;
1389}
1390
83b4934c 1391static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data)
6ff0c93a
MH
1392{
1393 int i;
1394
6ff0c93a
MH
1395 switch (data[0]) {
1396 case 0x00:
83b4934c 1397 printf(" information:");
6ff0c93a
MH
1398 break;
1399 case 0x01:
1cab57eb
JB
1400 if (print_wifi_wmm_param(data + 1, len - 1))
1401 return;
6ff0c93a
MH
1402 break;
1403 default:
83b4934c 1404 printf(" type %d:", data[0]);
6ff0c93a
MH
1405 break;
1406 }
1407
1cab57eb
JB
1408 for(i = 1; i < len; i++)
1409 printf(" %.02x", data[i]);
6ff0c93a
MH
1410 printf("\n");
1411}
1412
a6816965
JM
1413static const char * wifi_wps_dev_passwd_id(uint16_t id)
1414{
1415 switch (id) {
1416 case 0:
1417 return "Default (PIN)";
1418 case 1:
1419 return "User-specified";
1420 case 2:
1421 return "Machine-specified";
1422 case 3:
1423 return "Rekey";
1424 case 4:
1425 return "PushButton";
1426 case 5:
1427 return "Registrar-specified";
1428 default:
1429 return "??";
1430 }
1431}
1432
83b4934c 1433static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data)
4673a894
JB
1434{
1435 bool first = true;
1436 __u16 subtype, sublen;
1437
4673a894
JB
1438 while (len >= 4) {
1439 subtype = (data[0] << 8) + data[1];
1440 sublen = (data[2] << 8) + data[3];
1441 if (sublen > len)
1442 break;
1443
1444 switch (subtype) {
1445 case 0x104a:
1446 tab_on_first(&first);
dffc6750 1447 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
4673a894
JB
1448 break;
1449 case 0x1011:
1450 tab_on_first(&first);
1451 printf("\t * Device name: %.*s\n", sublen, data + 4);
1452 break;
a6816965
JM
1453 case 0x1012: {
1454 uint16_t id;
1455 tab_on_first(&first);
1456 if (sublen != 2) {
1457 printf("\t * Device Password ID: (invalid "
1458 "length %d)\n", sublen);
1459 break;
1460 }
1461 id = data[4] << 8 | data[5];
1462 printf("\t * Device Password ID: %u (%s)\n",
1463 id, wifi_wps_dev_passwd_id(id));
1464 break;
1465 }
4673a894
JB
1466 case 0x1021:
1467 tab_on_first(&first);
1468 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
1469 break;
1470 case 0x1023:
1471 tab_on_first(&first);
1472 printf("\t * Model: %.*s\n", sublen, data + 4);
1473 break;
a6816965
JM
1474 case 0x1024:
1475 tab_on_first(&first);
1476 printf("\t * Model Number: %.*s\n", sublen, data + 4);
1477 break;
1478 case 0x103b: {
1479 __u8 val = data[4];
1480 tab_on_first(&first);
1481 printf("\t * Response Type: %d%s\n",
1482 val, val == 3 ? " (AP)" : "");
1483 break;
1484 }
1485 case 0x103c: {
1486 __u8 val = data[4];
1487 tab_on_first(&first);
1488 printf("\t * RF Bands: 0x%x\n", val);
1489 break;
1490 }
1491 case 0x1041: {
1492 __u8 val = data[4];
1493 tab_on_first(&first);
1494 printf("\t * Selected Registrar: 0x%x\n", val);
1495 break;
1496 }
1497 case 0x1042:
1498 tab_on_first(&first);
1499 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
1500 break;
1501 case 0x1044: {
1502 __u8 val = data[4];
1503 tab_on_first(&first);
1504 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
1505 val,
1506 val == 1 ? " (Unconfigured)" : "",
1507 val == 2 ? " (Configured)" : "");
1508 break;
1509 }
09fe09dc
JB
1510 case 0x1047:
1511 tab_on_first(&first);
1512 printf("\t * UUID: ");
1513 if (sublen != 16) {
1514 printf("(invalid, length=%d)\n", sublen);
1515 break;
1516 }
1517 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
1518 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
1519 data[4], data[5], data[6], data[7],
1520 data[8], data[9], data[10], data[11],
1521 data[12], data[13], data[14], data[15],
1522 data[16], data[17], data[18], data[19]);
1523 break;
a6816965
JM
1524 case 0x1054: {
1525 tab_on_first(&first);
1526 if (sublen != 8) {
1527 printf("\t * Primary Device Type: (invalid "
1528 "length %d)\n", sublen);
1529 break;
1530 }
1531 printf("\t * Primary Device Type: "
1532 "%u-%02x%02x%02x%02x-%u\n",
1533 data[4] << 8 | data[5],
1534 data[6], data[7], data[8], data[9],
1535 data[10] << 8 | data[11]);
1536 break;
1537 }
7ee5a865 1538 case 0x1057: {
fe31a22e 1539 __u8 val = data[4];
7ee5a865 1540 tab_on_first(&first);
fe31a22e 1541 printf("\t * AP setup locked: 0x%.2x\n", val);
7ee5a865
JB
1542 break;
1543 }
a6816965
JM
1544 case 0x1008:
1545 case 0x1053: {
4673a894
JB
1546 __u16 meth = (data[4] << 8) + data[5];
1547 bool comma = false;
1548 tab_on_first(&first);
a6816965
JM
1549 printf("\t * %sConfig methods:",
1550 subtype == 0x1053 ? "Selected Registrar ": "");
4673a894
JB
1551#define T(bit, name) do { \
1552 if (meth & (1<<bit)) { \
1553 if (comma) \
1554 printf(","); \
1555 comma = true; \
1556 printf(" " name); \
1557 } } while (0)
1558 T(0, "USB");
1559 T(1, "Ethernet");
1560 T(2, "Label");
1561 T(3, "Display");
1562 T(4, "Ext. NFC");
1563 T(5, "Int. NFC");
1564 T(6, "NFC Intf.");
1565 T(7, "PBC");
1566 T(8, "Keypad");
1567 printf("\n");
1568 break;
1569#undef T
1570 }
2650d46b
JB
1571 default: {
1572 const __u8 *subdata = data + 4;
1573 __u16 tmplen = sublen;
1574
1575 tab_on_first(&first);
1576 printf("\t * Unknown TLV (%#.4x, %d bytes):",
1577 subtype, tmplen);
1578 while (tmplen) {
1579 printf(" %.2x", *subdata);
1580 subdata++;
1581 tmplen--;
1582 }
1583 printf("\n");
4673a894
JB
1584 break;
1585 }
2650d46b 1586 }
4673a894
JB
1587
1588 data += sublen + 4;
1589 len -= sublen + 4;
1590 }
1591
1592 if (len != 0) {
1593 printf("\t\t * bogus tail data (%d):", len);
1594 while (len) {
1595 printf(" %.2x", *data);
1596 data++;
1597 len--;
1598 }
1599 printf("\n");
1600 }
1601}
1602
83b4934c 1603static const struct ie_print wifiprinters[] = {
febeb0c0
JB
1604 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
1605 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
1606 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
4673a894
JB
1607};
1608
9a22374a
JB
1609static inline void print_p2p(const uint8_t type, uint8_t len, const uint8_t *data)
1610{
1611 bool first = true;
1612 __u8 subtype;
1613 __u16 sublen;
1614
1615 while (len >= 3) {
1616 subtype = data[0];
1617 sublen = (data[2] << 8) + data[1];
1618
1619 if (sublen > len - 3)
1620 break;
1621
1622 switch (subtype) {
1623 case 0x02: /* capability */
1624 tab_on_first(&first);
1625 if (sublen < 2) {
1626 printf("\t * malformed capability\n");
1627 break;
1628 }
1629 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
1630 data[3], data[4]);
1631 break;
1632 case 0x0d: /* device info */
1633 if (sublen < 6 + 2 + 8 + 1) {
1634 printf("\t * malformed device info\n");
1635 break;
1636 }
1637 /* fall through for now */
1638 case 0x00: /* status */
1639 case 0x01: /* minor reason */
1640 case 0x03: /* device ID */
1641 case 0x04: /* GO intent */
1642 case 0x05: /* configuration timeout */
1643 case 0x06: /* listen channel */
1644 case 0x07: /* group BSSID */
1645 case 0x08: /* ext listen timing */
1646 case 0x09: /* intended interface address */
1647 case 0x0a: /* manageability */
1648 case 0x0b: /* channel list */
1649 case 0x0c: /* NoA */
1650 case 0x0e: /* group info */
1651 case 0x0f: /* group ID */
1652 case 0x10: /* interface */
1653 case 0x11: /* operating channel */
1654 case 0x12: /* invitation flags */
1655 case 0xdd: /* vendor specific */
1656 default: {
1657 const __u8 *subdata = data + 4;
1658 __u16 tmplen = sublen;
1659
1660 tab_on_first(&first);
1661 printf("\t * Unknown TLV (%#.2x, %d bytes):",
1662 subtype, tmplen);
1663 while (tmplen) {
1664 printf(" %.2x", *subdata);
1665 subdata++;
1666 tmplen--;
1667 }
1668 printf("\n");
1669 break;
1670 }
1671 }
1672
1673 data += sublen + 3;
1674 len -= sublen + 3;
1675 }
1676
1677 if (len != 0) {
1678 tab_on_first(&first);
1679 printf("\t * bogus tail data (%d):", len);
1680 while (len) {
1681 printf(" %.2x", *data);
1682 data++;
1683 len--;
1684 }
1685 printf("\n");
1686 }
1687}
1688
7e10c4ab
BG
1689static inline void print_hs20_ind(const uint8_t type, uint8_t len, const uint8_t *data)
1690{
1691 /* I can't find the spec for this...just going off what wireshark uses. */
1692 printf("\n");
1693 if (len > 0)
1694 printf("\t\tDGAF: %i\n", (int)(data[0] & 0x1));
1695 else
1696 printf("\t\tUnexpected length: %i\n", len);
1697}
1698
9a22374a
JB
1699static const struct ie_print wfa_printers[] = {
1700 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
7e10c4ab 1701 [16] = { "HotSpot 2.0 Indication", print_hs20_ind, 1, 255, BIT(PRINT_SCAN), },
f188ab67 1702 [18] = { "HotSpot 2.0 OSEN", print_wifi_osen, 1, 255, BIT(PRINT_SCAN), },
9a22374a
JB
1703};
1704
764fe753 1705static void print_vendor(unsigned char len, unsigned char *data,
febeb0c0 1706 bool unknown, enum print_ie_type ptype)
3563f4c5
JB
1707{
1708 int i;
1709
fbf80af5 1710 if (len < 3) {
4673a894 1711 printf("\tVendor specific: <too short> data:");
fbf80af5
JB
1712 for(i = 0; i < len; i++)
1713 printf(" %.02x", data[i]);
1714 printf("\n");
1715 return;
1716 }
1717
3bd60ef1 1718 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
febeb0c0
JB
1719 if (data[3] < ARRAY_SIZE(wifiprinters) &&
1720 wifiprinters[data[3]].name &&
1721 wifiprinters[data[3]].flags & BIT(ptype)) {
83b4934c
JB
1722 print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4);
1723 return;
1724 }
febeb0c0 1725 if (!unknown)
4673a894 1726 return;
3bd60ef1 1727 printf("\tMS/WiFi %#.2x, data:", data[3]);
4673a894
JB
1728 for(i = 0; i < len - 4; i++)
1729 printf(" %.02x", data[i + 4]);
1730 printf("\n");
1731 return;
1732 }
1733
9a22374a
JB
1734 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
1735 if (data[3] < ARRAY_SIZE(wfa_printers) &&
1736 wfa_printers[data[3]].name &&
1737 wfa_printers[data[3]].flags & BIT(ptype)) {
1738 print_ie(&wfa_printers[data[3]], data[3], len - 4, data + 4);
1739 return;
1740 }
1741 if (!unknown)
1742 return;
1743 printf("\tWFA %#.2x, data:", data[3]);
1744 for(i = 0; i < len - 4; i++)
1745 printf(" %.02x", data[i + 4]);
1746 printf("\n");
1747 return;
1748 }
1749
febeb0c0 1750 if (!unknown)
764fe753
JB
1751 return;
1752
fbf80af5 1753 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
3563f4c5 1754 data[0], data[1], data[2]);
fbf80af5
JB
1755 for (i = 3; i < len; i++)
1756 printf(" %.2x", data[i]);
3563f4c5
JB
1757 printf("\n");
1758}
1759
febeb0c0
JB
1760void print_ies(unsigned char *ie, int ielen, bool unknown,
1761 enum print_ie_type ptype)
3563f4c5
JB
1762{
1763 while (ielen >= 2 && ielen >= ie[1]) {
febeb0c0
JB
1764 if (ie[0] < ARRAY_SIZE(ieprinters) &&
1765 ieprinters[ie[0]].name &&
1766 ieprinters[ie[0]].flags & BIT(ptype)) {
83b4934c 1767 print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2);
764fe753 1768 } else if (ie[0] == 221 /* vendor */) {
febeb0c0
JB
1769 print_vendor(ie[1], ie + 2, unknown, ptype);
1770 } else if (unknown) {
3563f4c5
JB
1771 int i;
1772
8086b700 1773 printf("\tUnknown IE (%d):", ie[0]);
3563f4c5 1774 for (i=0; i<ie[1]; i++)
8086b700 1775 printf(" %.2x", ie[2+i]);
3563f4c5
JB
1776 printf("\n");
1777 }
1778 ielen -= ie[1] + 2;
1779 ie += ie[1] + 2;
1780 }
1781}
1782
2e8b82c1
VK
1783static void print_capa_dmg(__u16 capa)
1784{
1785 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1786 case WLAN_CAPABILITY_DMG_TYPE_AP:
1787 printf(" DMG_ESS");
1788 break;
1789 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1790 printf(" DMG_PCP");
1791 break;
1792 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
1793 printf(" DMG_IBSS");
1794 break;
1795 }
1796
1797 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
1798 printf(" CBAP_Only");
1799 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
1800 printf(" CBAP_Src");
1801 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
1802 printf(" Privacy");
1803 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
1804 printf(" ECPAC");
1805 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
1806 printf(" SpectrumMgmt");
1807 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
1808 printf(" RadioMeasure");
1809}
1810
1811static void print_capa_non_dmg(__u16 capa)
1812{
1813 if (capa & WLAN_CAPABILITY_ESS)
1814 printf(" ESS");
1815 if (capa & WLAN_CAPABILITY_IBSS)
1816 printf(" IBSS");
1817 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
1818 printf(" CfPollable");
1819 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
1820 printf(" CfPollReq");
1821 if (capa & WLAN_CAPABILITY_PRIVACY)
1822 printf(" Privacy");
1823 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
1824 printf(" ShortPreamble");
1825 if (capa & WLAN_CAPABILITY_PBCC)
1826 printf(" PBCC");
1827 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
1828 printf(" ChannelAgility");
1829 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
1830 printf(" SpectrumMgmt");
1831 if (capa & WLAN_CAPABILITY_QOS)
1832 printf(" QoS");
1833 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
1834 printf(" ShortSlotTime");
1835 if (capa & WLAN_CAPABILITY_APSD)
1836 printf(" APSD");
1837 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
1838 printf(" RadioMeasure");
1839 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
1840 printf(" DSSS-OFDM");
1841 if (capa & WLAN_CAPABILITY_DEL_BACK)
1842 printf(" DelayedBACK");
1843 if (capa & WLAN_CAPABILITY_IMM_BACK)
1844 printf(" ImmediateBACK");
1845}
1846
3563f4c5
JB
1847static int print_bss_handler(struct nl_msg *msg, void *arg)
1848{
1849 struct nlattr *tb[NL80211_ATTR_MAX + 1];
1850 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1851 struct nlattr *bss[NL80211_BSS_MAX + 1];
1852 char mac_addr[20], dev[20];
1853 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1854 [NL80211_BSS_TSF] = { .type = NLA_U64 },
1855 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1856 [NL80211_BSS_BSSID] = { },
1857 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
1858 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
1859 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
f2e17e1f
JB
1860 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
1861 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
a56117a6 1862 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
c04a78df 1863 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
575280cc 1864 [NL80211_BSS_BEACON_IES] = { },
3563f4c5 1865 };
febeb0c0 1866 struct scan_params *params = arg;
1c5bcd9c 1867 int show = params->show_both_ie_sets ? 2 : 1;
2e8b82c1 1868 bool is_dmg = false;
3563f4c5
JB
1869
1870 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1871 genlmsg_attrlen(gnlh, 0), NULL);
1872
1873 if (!tb[NL80211_ATTR_BSS]) {
5fe70c0e 1874 fprintf(stderr, "bss info missing!\n");
3563f4c5
JB
1875 return NL_SKIP;
1876 }
1877 if (nla_parse_nested(bss, NL80211_BSS_MAX,
1878 tb[NL80211_ATTR_BSS],
1879 bss_policy)) {
5fe70c0e 1880 fprintf(stderr, "failed to parse nested attributes!\n");
3563f4c5
JB
1881 return NL_SKIP;
1882 }
1883
1884 if (!bss[NL80211_BSS_BSSID])
1885 return NL_SKIP;
1886
1887 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
00889fb2
JB
1888 printf("BSS %s", mac_addr);
1889 if (tb[NL80211_ATTR_IFINDEX]) {
1890 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
1891 printf("(on %s)", dev);
1892 }
a56117a6
JB
1893
1894 if (bss[NL80211_BSS_STATUS]) {
1895 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
1896 case NL80211_BSS_STATUS_AUTHENTICATED:
1897 printf(" -- authenticated");
1898 break;
1899 case NL80211_BSS_STATUS_ASSOCIATED:
1900 printf(" -- associated");
1901 break;
1902 case NL80211_BSS_STATUS_IBSS_JOINED:
1903 printf(" -- joined");
1904 break;
1905 default:
1906 printf(" -- unknown status: %d",
1907 nla_get_u32(bss[NL80211_BSS_STATUS]));
1908 break;
1909 }
1910 }
1911 printf("\n");
3563f4c5 1912
e7109a8a
JB
1913 if (bss[NL80211_BSS_TSF]) {
1914 unsigned long long tsf;
1915 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
1916 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
1917 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
1918 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
1919 }
2e8b82c1
VK
1920 if (bss[NL80211_BSS_FREQUENCY]) {
1921 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
1922 printf("\tfreq: %d\n", freq);
1923 if (freq > 45000)
1924 is_dmg = true;
1925 }
3563f4c5 1926 if (bss[NL80211_BSS_BEACON_INTERVAL])
792172b0 1927 printf("\tbeacon interval: %d TUs\n",
3563f4c5 1928 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
92a04ecd
MH
1929 if (bss[NL80211_BSS_CAPABILITY]) {
1930 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
1931 printf("\tcapability:");
2e8b82c1
VK
1932 if (is_dmg)
1933 print_capa_dmg(capa);
1934 else
1935 print_capa_non_dmg(capa);
92a04ecd
MH
1936 printf(" (0x%.4x)\n", capa);
1937 }
f2e17e1f
JB
1938 if (bss[NL80211_BSS_SIGNAL_MBM]) {
1939 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
1940 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
1941 }
1942 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
1943 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
1944 printf("\tsignal: %d/100\n", s);
1945 }
c04a78df
HS
1946 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
1947 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
1948 printf("\tlast seen: %d ms ago\n", age);
1949 }
c551449a 1950
1c5bcd9c 1951 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
575280cc
JM
1952 if (bss[NL80211_BSS_BEACON_IES])
1953 printf("\tInformation elements from Probe Response "
1954 "frame:\n");
3563f4c5 1955 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
764fe753 1956 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
febeb0c0 1957 params->unknown, params->type);
575280cc 1958 }
1c5bcd9c 1959 if (bss[NL80211_BSS_BEACON_IES] && show--) {
575280cc
JM
1960 printf("\tInformation elements from Beacon frame:\n");
1961 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
1962 nla_len(bss[NL80211_BSS_BEACON_IES]),
1963 params->unknown, params->type);
1964 }
3563f4c5
JB
1965
1966 return NL_SKIP;
1967}
1968
764fe753 1969static struct scan_params scan_params;
3563f4c5 1970
7c37a24d 1971static int handle_scan_dump(struct nl80211_state *state,
3563f4c5 1972 struct nl_msg *msg,
05514f95
JB
1973 int argc, char **argv,
1974 enum id_input id)
3563f4c5 1975{
764fe753
JB
1976 if (argc > 1)
1977 return 1;
1978
1c5bcd9c
JB
1979 memset(&scan_params, 0, sizeof(scan_params));
1980
764fe753
JB
1981 if (argc == 1 && !strcmp(argv[0], "-u"))
1982 scan_params.unknown = true;
575280cc
JM
1983 else if (argc == 1 && !strcmp(argv[0], "-b"))
1984 scan_params.show_both_ie_sets = true;
764fe753 1985
febeb0c0
JB
1986 scan_params.type = PRINT_SCAN;
1987
34b23014 1988 register_handler(print_bss_handler,
764fe753 1989 &scan_params);
3563f4c5
JB
1990 return 0;
1991}
a5fe4ef2
JB
1992
1993static int handle_scan_combined(struct nl80211_state *state,
a5fe4ef2 1994 struct nl_msg *msg,
05514f95
JB
1995 int argc, char **argv,
1996 enum id_input id)
a5fe4ef2 1997{
559a1713 1998 char **trig_argv;
a5fe4ef2
JB
1999 static char *dump_argv[] = {
2000 NULL,
2001 "scan",
2002 "dump",
92649eab 2003 NULL,
a5fe4ef2
JB
2004 };
2005 static const __u32 cmds[] = {
2006 NL80211_CMD_NEW_SCAN_RESULTS,
2007 NL80211_CMD_SCAN_ABORTED,
2008 };
559a1713 2009 int trig_argc, dump_argc, err;
a5fe4ef2 2010
559a1713
JB
2011 if (argc >= 3 && !strcmp(argv[2], "-u")) {
2012 dump_argc = 4;
2013 dump_argv[3] = "-u";
575280cc
JM
2014 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
2015 dump_argc = 4;
2016 dump_argv[3] = "-b";
559a1713
JB
2017 } else
2018 dump_argc = 3;
2019
2020 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
2021 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
2022 if (!trig_argv)
2023 return -ENOMEM;
a5fe4ef2 2024 trig_argv[0] = argv[0];
559a1713
JB
2025 trig_argv[1] = "scan";
2026 trig_argv[2] = "trigger";
2027 int i;
2028 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
2029 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
75f4204c 2030 err = handle_cmd(state, id, trig_argc, trig_argv);
559a1713 2031 free(trig_argv);
a5fe4ef2
JB
2032 if (err)
2033 return err;
2034
61725dbe
JB
2035 /*
2036 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
2037 *
2038 * This code has a bug, which requires creating a separate
2039 * nl80211 socket to fix:
2040 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
2041 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
2042 * before (!) we listen to it, because we only start listening
2043 * after we send our scan request.
2044 *
2045 * Doing it the other way around has a race condition as well,
2046 * if you first open the events socket you may get a notification
2047 * for a previous scan.
2048 *
2049 * The only proper way to fix this would be to listen to events
2050 * before sending the command, and for the kernel to send the
2051 * scan request along with the event, so that you can match up
2052 * whether the scan you requested was finished or aborted (this
2053 * may result in processing a scan that another application
2054 * requested, but that doesn't seem to be a problem).
2055 *
2056 * Alas, the kernel doesn't do that (yet).
2057 */
2058
a5fe4ef2
JB
2059 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
2060 NL80211_CMD_SCAN_ABORTED) {
2061 printf("scan aborted!\n");
2062 return 0;
2063 }
2064
2065 dump_argv[0] = argv[0];
75f4204c 2066 return handle_cmd(state, id, dump_argc, dump_argv);
a5fe4ef2 2067}
5085aa2b 2068TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]", 0, 0,
6ca98d28
JB
2069 CIB_NETDEV, handle_scan_combined,
2070 "Scan on the given frequencies and probe for the given SSIDs\n"
2071 "(or wildcard if not given) unless passive scanning is requested.\n"
64797a7f
JB
2072 "If -u is specified print unknown data in the scan results.\n"
2073 "Specified (vendor) IEs must be well-formed.");
4698bfc2
JB
2074COMMAND(scan, dump, "[-u]",
2075 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
2076 "Dump the current scan results. If -u is specified, print unknown\n"
2077 "data in scan results.");
5085aa2b 2078COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
4698bfc2
JB
2079 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
2080 "Trigger a scan on the given frequencies with probing for the given\n"
2081 "SSIDs (or wildcard if not given) unless passive scanning is requested.");
3fce58aa
LC
2082
2083
2084static int handle_start_sched_scan(struct nl80211_state *state,
34b23014 2085 struct nl_msg *msg,
3fce58aa
LC
2086 int argc, char **argv, enum id_input id)
2087{
2088 return parse_sched_scan(msg, &argc, &argv);
2089}
2090
34b23014 2091static int handle_stop_sched_scan(struct nl80211_state *state,
3fce58aa
LC
2092 struct nl_msg *msg, int argc, char **argv,
2093 enum id_input id)
2094{
2095 if (argc != 0)
2096 return 1;
2097
2098 return 0;
2099}
2100
2101COMMAND(scan, sched_start,
2102 SCHED_SCAN_OPTIONS,
2103 NL80211_CMD_START_SCHED_SCAN, 0, CIB_NETDEV, handle_start_sched_scan,
2104 "Start a scheduled scan at the specified interval on the given frequencies\n"
2105 "with probing for the given SSIDs (or wildcard if not given) unless passive\n"
2106 "scanning is requested. If matches are specified, only matching results\n"
2107 "will be returned.");
2108COMMAND(scan, sched_stop, "",
2109 NL80211_CMD_STOP_SCHED_SCAN, 0, CIB_NETDEV, handle_stop_sched_scan,
2110 "Stop an ongoing scheduled scan.");