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