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