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