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