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