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