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