blob: bf39f34be3141a4de01922ae6443e0932e3ad51a [file] [log] [blame]
Johannes Berg3563f4c2008-09-19 05:08:11 +02001#include <net/if.h>
2#include <errno.h>
3#include <string.h>
4#include <ctype.h>
Johannes Berg764fe752009-02-12 10:30:32 +01005#include <stdbool.h>
Johannes Berg3563f4c2008-09-19 05:08:11 +02006
7#include <netlink/genl/genl.h>
8#include <netlink/genl/family.h>
9#include <netlink/genl/ctrl.h>
10#include <netlink/msg.h>
11#include <netlink/attr.h>
12
13#include "nl80211.h"
14#include "iw.h"
15
Marcel Holtmann92a04ec2009-05-04 01:48:45 -070016#define WLAN_CAPABILITY_ESS (1<<0)
17#define WLAN_CAPABILITY_IBSS (1<<1)
18#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
19#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
20#define WLAN_CAPABILITY_PRIVACY (1<<4)
21#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
22#define WLAN_CAPABILITY_PBCC (1<<6)
23#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
24#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
25#define WLAN_CAPABILITY_QOS (1<<9)
26#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
27#define WLAN_CAPABILITY_APSD (1<<11)
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +020028#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12)
Marcel Holtmann92a04ec2009-05-04 01:48:45 -070029#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +020030#define WLAN_CAPABILITY_DEL_BACK (1<<14)
31#define WLAN_CAPABILITY_IMM_BACK (1<<15)
32/* DMG (60gHz) 802.11ad */
33/* type - bits 0..1 */
34#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0)
35
36#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */
37#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */
38#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */
39
40#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2)
41#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3)
42#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4)
43#define WLAN_CAPABILITY_DMG_ECPAC (1<<5)
44
45#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8)
46#define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12)
Marcel Holtmann92a04ec2009-05-04 01:48:45 -070047
Johannes Berg3bd60ef2011-06-09 20:36:59 +020048static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
49static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
Johannes Berg9a223742011-06-09 20:55:44 +020050static unsigned char wfa_oui[3] = { 0x50, 0x6f, 0x9a };
Marcel Holtmann857d9662009-05-04 01:48:47 -070051
Johannes Berg764fe752009-02-12 10:30:32 +010052struct scan_params {
53 bool unknown;
Johannes Bergfebeb0c2009-07-25 17:31:08 +020054 enum print_ie_type type;
Jouni Malinen575280c2010-01-06 17:53:59 +020055 bool show_both_ie_sets;
Johannes Berg764fe752009-02-12 10:30:32 +010056};
57
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -050058#define IEEE80211_COUNTRY_EXTENSION_ID 201
59
Johannes Berg72725712010-09-22 11:32:54 +020060union ieee80211_country_ie_triplet {
61 struct {
62 __u8 first_channel;
63 __u8 num_channels;
64 __s8 max_power;
65 } __attribute__ ((packed)) chans;
66 struct {
67 __u8 reg_extension_id;
68 __u8 reg_class;
69 __u8 coverage_class;
70 } __attribute__ ((packed)) ext;
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -050071} __attribute__ ((packed));
72
Johannes Berg5085aa22014-11-19 13:44:43 +010073static int parse_random_mac_addr(struct nl_msg *msg, char *arg)
74{
75 char *a_addr, *a_mask, *sep;
76 unsigned char addr[ETH_ALEN], mask[ETH_ALEN];
77 char *addrs = arg + 9;
78
79 if (*addrs != '=')
80 return 0;
81
82 addrs++;
83 sep = strchr(addrs, '/');
84 a_addr = addrs;
85
86 if (!sep)
87 return 1;
88
89 *sep = 0;
90 a_mask = sep + 1;
91 if (mac_addr_a2n(addr, a_addr) || mac_addr_a2n(mask, a_mask))
92 return 1;
93
94 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
95 NLA_PUT(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, mask);
96
97 return 0;
98 nla_put_failure:
99 return -ENOBUFS;
100}
101
Luciano Coelhob1622282015-03-17 16:11:47 +0200102int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv)
103{
Luciano Coelho302a3782015-03-17 16:11:49 +0200104 struct nl_msg *matchset = NULL, *freqs = NULL, *ssids = NULL;
Luciano Coelhob1622282015-03-17 16:11:47 +0200105 struct nlattr *match = NULL;
106 enum {
107 ND_TOPLEVEL,
108 ND_MATCH,
109 ND_FREQS,
Luciano Coelho302a3782015-03-17 16:11:49 +0200110 ND_ACTIVE,
Luciano Coelhob1622282015-03-17 16:11:47 +0200111 } parse_state = ND_TOPLEVEL;
112 int c = *argc;
113 char *end, **v = *argv;
114 int err = 0, i = 0;
115 unsigned int freq, interval = 0, delay = 0;
Luciano Coelho302a3782015-03-17 16:11:49 +0200116 bool have_matchset = false, have_freqs = false, have_ssids = false;
117 bool have_active = false, have_passive = false;
Luciano Coelho5130bba2015-03-17 16:11:50 +0200118 uint32_t flags = 0;
Luciano Coelhob1622282015-03-17 16:11:47 +0200119
120 matchset = nlmsg_alloc();
121 if (!matchset) {
122 err = -ENOBUFS;
123 goto out;
124 }
125
126 freqs = nlmsg_alloc();
127 if (!freqs) {
128 err = -ENOBUFS;
129 goto out;
130 }
131
Luciano Coelho302a3782015-03-17 16:11:49 +0200132 ssids = nlmsg_alloc();
133 if (!ssids) {
134 err = -ENOMEM;
135 goto out;
136 }
137
Luciano Coelhob1622282015-03-17 16:11:47 +0200138 while (c) {
139 switch (parse_state) {
140 case ND_TOPLEVEL:
141 if (!strcmp(v[0], "interval")) {
142 c--; v++;
143 if (c == 0) {
144 err = -EINVAL;
145 goto nla_put_failure;
146 }
147
148 if (interval) {
149 err = -EINVAL;
150 goto nla_put_failure;
151 }
152 interval = strtoul(v[0], &end, 10);
153 if (*end || !interval) {
154 err = -EINVAL;
155 goto nla_put_failure;
156 }
157 NLA_PUT_U32(msg,
158 NL80211_ATTR_SCHED_SCAN_INTERVAL,
159 interval);
160 } else if (!strcmp(v[0], "delay")) {
161 c--; v++;
162 if (c == 0) {
163 err = -EINVAL;
164 goto nla_put_failure;
165 }
166
167 if (delay) {
168 err = -EINVAL;
169 goto nla_put_failure;
170 }
171 delay = strtoul(v[0], &end, 10);
172 if (*end) {
173 err = -EINVAL;
174 goto nla_put_failure;
175 }
176 NLA_PUT_U32(msg,
177 NL80211_ATTR_SCHED_SCAN_DELAY,
178 delay);
179 } else if (!strcmp(v[0], "matches")) {
180 parse_state = ND_MATCH;
181 if (have_matchset) {
182 err = -EINVAL;
183 goto nla_put_failure;
184 }
185
186 i = 0;
187 } else if (!strcmp(v[0], "freqs")) {
188 parse_state = ND_FREQS;
189 if (have_freqs) {
190 err = -EINVAL;
191 goto nla_put_failure;
192 }
193
194 have_freqs = true;
195 i = 0;
Luciano Coelho302a3782015-03-17 16:11:49 +0200196 } else if (!strcmp(v[0], "active")) {
197 parse_state = ND_ACTIVE;
198 if (have_active || have_passive) {
199 err = -EINVAL;
200 goto nla_put_failure;
201 }
202
203 have_active = true;
204 i = 0;
205 } else if (!strcmp(v[0], "passive")) {
206 if (have_active || have_passive) {
207 err = -EINVAL;
208 goto nla_put_failure;
209 }
210
211 have_passive = true;
Luciano Coelho5130bba2015-03-17 16:11:50 +0200212 } else if (!strncmp(v[0], "randomise", 9) ||
213 !strncmp(v[0], "randomize", 9)) {
214 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
215 if (c > 0) {
216 err = parse_random_mac_addr(msg, v[0]);
217 if (err)
218 goto nla_put_failure;
219 }
Luciano Coelhob1622282015-03-17 16:11:47 +0200220 } else {
221 /* this element is not for us, so
222 * return to continue parsing.
223 */
224 goto nla_put_failure;
225 }
226 c--; v++;
227
228 break;
229 case ND_MATCH:
230 if (!strcmp(v[0], "ssid")) {
231 c--; v++;
232 if (c == 0) {
233 err = -EINVAL;
234 goto nla_put_failure;
235 }
236
237 /* TODO: for now we can only have an
238 * SSID in the match, so we can start
239 * the match nest here.
240 */
241 match = nla_nest_start(matchset, i);
242 if (!match) {
243 err = -ENOBUFS;
244 goto nla_put_failure;
245 }
246
247 NLA_PUT(matchset,
248 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
249 strlen(v[0]), v[0]);
250 nla_nest_end(matchset, match);
251 match = NULL;
252
253 have_matchset = true;
254 i++;
255 c--; v++;
256 } else {
257 /* other element that cannot be part
258 * of a match indicates the end of the
259 * match. */
260 /* need at least one match in the matchset */
261 if (i == 0) {
262 err = -EINVAL;
263 goto nla_put_failure;
264 }
265
266 parse_state = ND_TOPLEVEL;
267 }
268
269 break;
270 case ND_FREQS:
271 freq = strtoul(v[0], &end, 10);
272 if (*end) {
273 if (i == 0) {
274 err = -EINVAL;
275 goto nla_put_failure;
276 }
277
278 parse_state = ND_TOPLEVEL;
279 } else {
280 NLA_PUT_U32(freqs, i, freq);
281 i++;
282 c--; v++;
283 }
284 break;
Luciano Coelho302a3782015-03-17 16:11:49 +0200285 case ND_ACTIVE:
286 if (!strcmp(v[0], "ssid")) {
287 c--; v++;
288 if (c == 0) {
289 err = -EINVAL;
290 goto nla_put_failure;
291 }
292
293 NLA_PUT(ssids,
294 NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
295 strlen(v[0]), v[0]);
296
297 have_ssids = true;
298 i++;
299 c--; v++;
300 } else {
301 /* other element that cannot be part
302 * of a match indicates the end of the
303 * active set. */
304 /* need at least one item in the set */
305 if (i == 0) {
306 err = -EINVAL;
307 goto nla_put_failure;
308 }
309
310 parse_state = ND_TOPLEVEL;
311 }
312 break;
Luciano Coelhob1622282015-03-17 16:11:47 +0200313 }
314 }
315
Luciano Coelho302a3782015-03-17 16:11:49 +0200316 if (!have_ssids)
317 NLA_PUT(ssids, 1, 0, "");
318 if (!have_passive)
319 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
Luciano Coelhob1622282015-03-17 16:11:47 +0200320 if (have_freqs)
321 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
322 if (have_matchset)
323 nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
Luciano Coelho5130bba2015-03-17 16:11:50 +0200324 if (flags)
325 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
Luciano Coelhob1622282015-03-17 16:11:47 +0200326
327nla_put_failure:
328 if (match)
329 nla_nest_end(msg, match);
330 nlmsg_free(freqs);
331 nlmsg_free(matchset);
332
333out:
334 *argc = c;
335 *argv = v;
336 return err;
337}
338
Johannes Berg7c37a242009-04-08 13:13:28 +0200339static int handle_scan(struct nl80211_state *state,
340 struct nl_cb *cb,
Johannes Berg3563f4c2008-09-19 05:08:11 +0200341 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +0200342 int argc, char **argv,
343 enum id_input id)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200344{
Johannes Berg559a1712009-05-04 14:06:33 +0200345 struct nl_msg *ssids = NULL, *freqs = NULL;
346 char *eptr;
Johannes Berg3563f4c2008-09-19 05:08:11 +0200347 int err = -ENOBUFS;
Johannes Berg559a1712009-05-04 14:06:33 +0200348 int i;
349 enum {
350 NONE,
351 FREQ,
Johannes Berg64797a72010-03-24 23:38:25 -0700352 IES,
Johannes Berg559a1712009-05-04 14:06:33 +0200353 SSID,
jacob minshall851d35c2013-04-25 15:02:19 -0700354 MESHID,
Johannes Berg559a1712009-05-04 14:06:33 +0200355 DONE,
356 } parse = NONE;
357 int freq;
358 bool passive = false, have_ssids = false, have_freqs = false;
jacob minshall851d35c2013-04-25 15:02:19 -0700359 size_t ies_len = 0, meshid_len = 0;
360 unsigned char *ies = NULL, *meshid = NULL, *tmpies;
Johannes Berg5085aa22014-11-19 13:44:43 +0100361 unsigned int flags = 0;
Johannes Berg3563f4c2008-09-19 05:08:11 +0200362
363 ssids = nlmsg_alloc();
364 if (!ssids)
365 return -ENOMEM;
Johannes Berg559a1712009-05-04 14:06:33 +0200366
367 freqs = nlmsg_alloc();
368 if (!freqs) {
369 nlmsg_free(ssids);
370 return -ENOMEM;
371 }
372
373 for (i = 0; i < argc; i++) {
Johannes Berg559a1712009-05-04 14:06:33 +0200374 switch (parse) {
375 case NONE:
Johannes Berg64797a72010-03-24 23:38:25 -0700376 if (strcmp(argv[i], "freq") == 0) {
377 parse = FREQ;
378 have_freqs = true;
379 break;
380 } else if (strcmp(argv[i], "ies") == 0) {
381 parse = IES;
382 break;
Sam Lefflerfe862232012-10-17 12:20:04 -0700383 } else if (strcmp(argv[i], "lowpri") == 0) {
Sam Lefflerfe862232012-10-17 12:20:04 -0700384 flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
385 break;
386 } else if (strcmp(argv[i], "flush") == 0) {
Sam Lefflerfe862232012-10-17 12:20:04 -0700387 flags |= NL80211_SCAN_FLAG_FLUSH;
388 break;
Antonio Quartulliced94d52012-10-26 16:41:48 +0200389 } else if (strcmp(argv[i], "ap-force") == 0) {
Antonio Quartulliced94d52012-10-26 16:41:48 +0200390 flags |= NL80211_SCAN_FLAG_AP;
391 break;
Johannes Berg5085aa22014-11-19 13:44:43 +0100392 } else if (strncmp(argv[i], "randomise", 9) == 0 ||
393 strncmp(argv[i], "randomize", 9) == 0) {
394 flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
395 err = parse_random_mac_addr(msg, argv[i]);
396 if (err)
397 goto nla_put_failure;
398 break;
Johannes Berg64797a72010-03-24 23:38:25 -0700399 } else if (strcmp(argv[i], "ssid") == 0) {
400 parse = SSID;
401 have_ssids = true;
402 break;
403 } else if (strcmp(argv[i], "passive") == 0) {
404 parse = DONE;
405 passive = true;
406 break;
jacob minshall851d35c2013-04-25 15:02:19 -0700407 } else if (strcmp(argv[i], "meshid") == 0) {
408 parse = MESHID;
409 break;
Johannes Berg64797a72010-03-24 23:38:25 -0700410 }
Johannes Berg559a1712009-05-04 14:06:33 +0200411 case DONE:
412 return 1;
413 case FREQ:
414 freq = strtoul(argv[i], &eptr, 10);
Johannes Bergcc12e892011-03-02 14:45:32 +0100415 if (eptr != argv[i] + strlen(argv[i])) {
416 /* failed to parse as number -- maybe a tag? */
417 i--;
418 parse = NONE;
419 continue;
420 }
Johannes Berg559a1712009-05-04 14:06:33 +0200421 NLA_PUT_U32(freqs, i, freq);
Johannes Berg64797a72010-03-24 23:38:25 -0700422 break;
423 case IES:
jacob minshall851d35c2013-04-25 15:02:19 -0700424 ies = parse_hex(argv[i], &ies_len);
Johannes Berg64797a72010-03-24 23:38:25 -0700425 if (!ies)
426 goto nla_put_failure;
Johannes Berg64797a72010-03-24 23:38:25 -0700427 parse = NONE;
Johannes Berg559a1712009-05-04 14:06:33 +0200428 break;
429 case SSID:
430 NLA_PUT(ssids, i, strlen(argv[i]), argv[i]);
431 break;
jacob minshall851d35c2013-04-25 15:02:19 -0700432 case MESHID:
433 meshid_len = strlen(argv[i]);
434 meshid = (unsigned char *) malloc(meshid_len + 2);
435 if (!meshid)
436 goto nla_put_failure;
437 meshid[0] = 114; /* mesh element id */
438 meshid[1] = meshid_len;
439 memcpy(&meshid[2], argv[i], meshid_len);
440 meshid_len += 2;
441 parse = NONE;
442 break;
Johannes Berg559a1712009-05-04 14:06:33 +0200443 }
444 }
445
jacob minshall851d35c2013-04-25 15:02:19 -0700446 if (ies || meshid) {
447 tmpies = (unsigned char *) malloc(ies_len + meshid_len);
448 if (!tmpies)
449 goto nla_put_failure;
450 if (ies) {
451 memcpy(tmpies, ies, ies_len);
452 free(ies);
453 }
454 if (meshid) {
455 memcpy(&tmpies[ies_len], meshid, meshid_len);
456 free(meshid);
457 }
458 NLA_PUT(msg, NL80211_ATTR_IE, ies_len + meshid_len, tmpies);
459 free(tmpies);
460 }
461
Johannes Berg559a1712009-05-04 14:06:33 +0200462 if (!have_ssids)
463 NLA_PUT(ssids, 1, 0, "");
464 if (!passive)
465 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
466
467 if (have_freqs)
468 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
Sam Lefflerfe862232012-10-17 12:20:04 -0700469 if (flags)
470 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200471
472 err = 0;
473 nla_put_failure:
474 nlmsg_free(ssids);
Johannes Berg559a1712009-05-04 14:06:33 +0200475 nlmsg_free(freqs);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200476 return err;
477}
Johannes Berg3563f4c2008-09-19 05:08:11 +0200478
Marcel Holtmann857d9662009-05-04 01:48:47 -0700479static void tab_on_first(bool *first)
480{
481 if (!*first)
482 printf("\t");
483 else
484 *first = false;
485}
486
Johannes Berg83b49342009-05-04 13:35:22 +0200487static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200488{
Johannes Berg83b49342009-05-04 13:35:22 +0200489 printf(" ");
Johannes Berg748f8482009-05-24 16:48:17 +0200490 print_ssid_escaped(len, data);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200491 printf("\n");
492}
493
Johannes Bergca159932012-11-20 17:26:14 +0100494#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200495#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
496
Johannes Berg83b49342009-05-04 13:35:22 +0200497static void print_supprates(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200498{
499 int i;
500
Johannes Berg83b49342009-05-04 13:35:22 +0200501 printf(" ");
Johannes Berg3563f4c2008-09-19 05:08:11 +0200502
Johannes Berg83b49342009-05-04 13:35:22 +0200503 for (i = 0; i < len; i++) {
Johannes Berg3563f4c2008-09-19 05:08:11 +0200504 int r = data[i] & 0x7f;
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200505
Johannes Bergca159932012-11-20 17:26:14 +0100506 if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
507 printf("VHT");
508 else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200509 printf("HT");
510 else
511 printf("%d.%d", r/2, 5*(r&1));
512
513 printf("%s ", data[i] & 0x80 ? "*" : "");
Johannes Berg3563f4c2008-09-19 05:08:11 +0200514 }
515 printf("\n");
516}
517
Johannes Berg83b49342009-05-04 13:35:22 +0200518static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200519{
Johannes Berg83b49342009-05-04 13:35:22 +0200520 printf(" channel %d\n", data[0]);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200521}
522
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500523static const char *country_env_str(char environment)
524{
525 switch (environment) {
526 case 'I':
527 return "Indoor only";
528 case 'O':
529 return "Outdoor only";
530 case ' ':
531 return "Indoor/Outdoor";
532 default:
533 return "bogus";
534 }
535}
536
Johannes Berg83b49342009-05-04 13:35:22 +0200537static void print_country(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700538{
Johannes Berg83b49342009-05-04 13:35:22 +0200539 printf(" %.*s", 2, data);
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500540
541 printf("\tEnvironment: %s\n", country_env_str(data[2]));
542
543 data += 3;
544 len -= 3;
545
546 if (len < 3) {
547 printf("\t\tNo country IE triplets present\n");
548 return;
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700549 }
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500550
551 while (len >= 3) {
552 int end_channel;
Johannes Berg72725712010-09-22 11:32:54 +0200553 union ieee80211_country_ie_triplet *triplet = (void *) data;
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500554
555 if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
556 printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
557 triplet->ext.reg_extension_id,
558 triplet->ext.reg_class,
559 triplet->ext.coverage_class,
560 triplet->ext.coverage_class * 450);
561
562 data += 3;
563 len -= 3;
564 continue;
565 }
566
567 /* 2 GHz */
568 if (triplet->chans.first_channel <= 14)
569 end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
570 else
571 end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
572
Luis R. Rodrigueza9859d32010-10-18 11:37:56 -0700573 printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500574
575 data += 3;
576 len -= 3;
577 }
578
579 return;
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700580}
581
Marcel Holtmannd1563a12009-05-04 09:06:07 -0700582static void print_powerconstraint(const uint8_t type, uint8_t len, const uint8_t *data)
583{
584 printf(" %d dB\n", data[0]);
585}
586
Joerg Mayer792172b2013-04-19 22:39:05 +0200587static void print_tpcreport(const uint8_t type, uint8_t len, const uint8_t *data)
588{
589 printf(" TX power: %d dBm\n", data[0]);
590 /* printf(" Link Margin (%d dB) is reserved in Beacons\n", data[1]); */
591}
592
Johannes Berg83b49342009-05-04 13:35:22 +0200593static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmannfc4d1482009-05-04 01:48:42 -0700594{
595 if (data[0] == 0x00)
Johannes Berg83b49342009-05-04 13:35:22 +0200596 printf(" <no flags>");
Marcel Holtmannfc4d1482009-05-04 01:48:42 -0700597 if (data[0] & 0x01)
598 printf(" NonERP_Present");
599 if (data[0] & 0x02)
600 printf(" Use_Protection");
601 if (data[0] & 0x04)
602 printf(" Barker_Preamble_Mode");
603 printf("\n");
604}
605
Johannes Berg83b49342009-05-04 13:35:22 +0200606static void print_cipher(const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700607{
Johannes Berg3bd60ef2011-06-09 20:36:59 +0200608 if (memcmp(data, ms_oui, 3) == 0) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700609 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200610 case 0:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700611 printf("Use group cipher suite");
612 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200613 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700614 printf("WEP-40");
615 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200616 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700617 printf("TKIP");
618 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200619 case 4:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700620 printf("CCMP");
621 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200622 case 5:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700623 printf("WEP-104");
624 break;
625 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200626 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200627 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700628 break;
629 }
630 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
631 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200632 case 0:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700633 printf("Use group cipher suite");
634 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200635 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700636 printf("WEP-40");
637 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200638 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700639 printf("TKIP");
640 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200641 case 4:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700642 printf("CCMP");
643 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200644 case 5:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700645 printf("WEP-104");
646 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200647 case 6:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700648 printf("AES-128-CMAC");
649 break;
Ben Greearf188ab62015-03-17 15:02:33 -0700650 case 7:
651 printf("NO-GROUP");
652 break;
Vladimir Kondratieva8b3da92012-07-05 14:36:20 +0300653 case 8:
654 printf("GCMP");
655 break;
Marcel Holtmann857d9662009-05-04 01:48:47 -0700656 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200657 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200658 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700659 break;
660 }
661 } else
Johannes Berg332769c2009-05-05 10:43:33 +0200662 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200663 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700664}
665
Johannes Berg83b49342009-05-04 13:35:22 +0200666static void print_auth(const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700667{
Johannes Berg3bd60ef2011-06-09 20:36:59 +0200668 if (memcmp(data, ms_oui, 3) == 0) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700669 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200670 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700671 printf("IEEE 802.1X");
672 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200673 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700674 printf("PSK");
675 break;
676 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200677 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200678 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700679 break;
680 }
681 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
682 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200683 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700684 printf("IEEE 802.1X");
685 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200686 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700687 printf("PSK");
688 break;
Johannes Berg0fe1c412009-05-05 11:21:13 +0200689 case 3:
690 printf("FT/IEEE 802.1X");
691 break;
692 case 4:
693 printf("FT/PSK");
694 break;
695 case 5:
696 printf("IEEE 802.1X/SHA-256");
697 break;
698 case 6:
699 printf("PSK/SHA-256");
700 break;
Joerg Mayer4361eef2013-08-29 12:07:38 +0200701 case 7:
702 printf("TDLS/TPK");
703 break;
Marcel Holtmann857d9662009-05-04 01:48:47 -0700704 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200705 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200706 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700707 break;
708 }
Ben Greearf188ab62015-03-17 15:02:33 -0700709 } else if (memcmp(data, wfa_oui, 3) == 0) {
710 switch (data[3]) {
711 case 1:
712 printf("OSEN");
713 break;
714 default:
715 printf("%.02x-%.02x-%.02x:%d",
716 data[0], data[1] ,data[2], data[3]);
717 break;
718 }
Marcel Holtmann857d9662009-05-04 01:48:47 -0700719 } else
Johannes Berg332769c2009-05-05 10:43:33 +0200720 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200721 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700722}
723
Ben Greearf188ab62015-03-17 15:02:33 -0700724static void _print_rsn_ie(const char *defcipher, const char *defauth,
725 uint8_t len, const uint8_t *data, int is_osen)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700726{
727 bool first = true;
Ben Greearf188ab62015-03-17 15:02:33 -0700728 __u16 count, capa;
Marcel Holtmann857d9662009-05-04 01:48:47 -0700729 int i;
730
Ben Greearf188ab62015-03-17 15:02:33 -0700731 if (!is_osen) {
732 __u16 version;
733 version = data[0] + (data[1] << 8);
734 tab_on_first(&first);
735 printf("\t * Version: %d\n", version);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700736
Ben Greearf188ab62015-03-17 15:02:33 -0700737 data += 2;
738 len -= 2;
739 }
Marcel Holtmann857d9662009-05-04 01:48:47 -0700740
741 if (len < 4) {
742 tab_on_first(&first);
743 printf("\t * Group cipher: %s\n", defcipher);
744 printf("\t * Pairwise ciphers: %s\n", defcipher);
745 return;
746 }
747
748 tab_on_first(&first);
749 printf("\t * Group cipher: ");
750 print_cipher(data);
751 printf("\n");
752
753 data += 4;
754 len -= 4;
755
756 if (len < 2) {
757 tab_on_first(&first);
758 printf("\t * Pairwise ciphers: %s\n", defcipher);
759 return;
760 }
761
762 count = data[0] | (data[1] << 8);
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700763 if (2 + (count * 4) > len)
764 goto invalid;
765
Marcel Holtmann857d9662009-05-04 01:48:47 -0700766 tab_on_first(&first);
767 printf("\t * Pairwise ciphers:");
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700768 for (i = 0; i < count; i++) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700769 printf(" ");
770 print_cipher(data + 2 + (i * 4));
771 }
772 printf("\n");
773
774 data += 2 + (count * 4);
775 len -= 2 + (count * 4);
776
777 if (len < 2) {
778 tab_on_first(&first);
779 printf("\t * Authentication suites: %s\n", defauth);
780 return;
781 }
782
783 count = data[0] | (data[1] << 8);
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700784 if (2 + (count * 4) > len)
785 goto invalid;
786
Marcel Holtmann857d9662009-05-04 01:48:47 -0700787 tab_on_first(&first);
788 printf("\t * Authentication suites:");
Johannes Berg83b49342009-05-04 13:35:22 +0200789 for (i = 0; i < count; i++) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700790 printf(" ");
791 print_auth(data + 2 + (i * 4));
792 }
793 printf("\n");
794
795 data += 2 + (count * 4);
796 len -= 2 + (count * 4);
797
Johannes Berg6a4f24e2009-05-04 18:57:03 +0200798 if (len >= 2) {
799 capa = data[0] | (data[1] << 8);
800 tab_on_first(&first);
Johannes Bergcadbe892009-05-05 11:33:34 +0200801 printf("\t * Capabilities:");
802 if (capa & 0x0001)
803 printf(" PreAuth");
804 if (capa & 0x0002)
805 printf(" NoPairwise");
806 switch ((capa & 0x000c) >> 2) {
807 case 0:
Joerg Mayer4361eef2013-08-29 12:07:38 +0200808 printf(" 1-PTKSA-RC");
Johannes Bergcadbe892009-05-05 11:33:34 +0200809 break;
810 case 1:
811 printf(" 2-PTKSA-RC");
812 break;
813 case 2:
814 printf(" 4-PTKSA-RC");
815 break;
816 case 3:
817 printf(" 16-PTKSA-RC");
818 break;
819 }
820 switch ((capa & 0x0030) >> 4) {
821 case 0:
Joerg Mayer4361eef2013-08-29 12:07:38 +0200822 printf(" 1-GTKSA-RC");
Johannes Bergcadbe892009-05-05 11:33:34 +0200823 break;
824 case 1:
825 printf(" 2-GTKSA-RC");
826 break;
827 case 2:
828 printf(" 4-GTKSA-RC");
829 break;
830 case 3:
831 printf(" 16-GTKSA-RC");
832 break;
833 }
834 if (capa & 0x0040)
835 printf(" MFP-required");
836 if (capa & 0x0080)
837 printf(" MFP-capable");
838 if (capa & 0x0200)
839 printf(" Peerkey-enabled");
840 if (capa & 0x0400)
841 printf(" SPP-AMSDU-capable");
842 if (capa & 0x0800)
843 printf(" SPP-AMSDU-required");
844 printf(" (0x%.4x)\n", capa);
Johannes Berg6a4f24e2009-05-04 18:57:03 +0200845 data += 2;
846 len -= 2;
847 }
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700848
Johannes Berg5ba3f522010-09-29 12:45:06 +0200849 if (len >= 2) {
850 int pmkid_count = data[0] | (data[1] << 8);
851
852 if (len >= 2 + 16 * pmkid_count) {
853 tab_on_first(&first);
854 printf("\t * %d PMKIDs\n", pmkid_count);
855 /* not printing PMKID values */
856 data += 2 + 16 * pmkid_count;
857 len -= 2 + 16 * pmkid_count;
858 } else
859 goto invalid;
860 }
861
862 if (len >= 4) {
863 tab_on_first(&first);
864 printf("\t * Group mgmt cipher suite: ");
865 print_cipher(data);
866 printf("\n");
867 data += 4;
868 len -= 4;
869 }
870
Johannes Bergcadbe892009-05-05 11:33:34 +0200871 invalid:
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700872 if (len != 0) {
873 printf("\t\t * bogus tail data (%d):", len);
874 while (len) {
875 printf(" %.2x", *data);
876 data++;
877 len--;
878 }
879 printf("\n");
880 }
Marcel Holtmann857d9662009-05-04 01:48:47 -0700881}
882
Ben Greearf188ab62015-03-17 15:02:33 -0700883static void print_rsn_ie(const char *defcipher, const char *defauth,
884 uint8_t len, const uint8_t *data)
885{
886 _print_rsn_ie(defcipher, defauth, len, data, 0);
887}
888
889static void print_osen_ie(const char *defcipher, const char *defauth,
890 uint8_t len, const uint8_t *data)
891{
892 printf("\n\t");
893 _print_rsn_ie(defcipher, defauth, len, data, 1);
894}
895
Johannes Berg83b49342009-05-04 13:35:22 +0200896static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700897{
Johannes Berg83b49342009-05-04 13:35:22 +0200898 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700899}
900
Luis R. Rodriguez0c445c22009-12-07 20:55:25 -0500901static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
902{
Luis R. Rodriguez357c1a52009-12-07 22:05:42 -0500903 printf("\n");
Johannes Berg7ddfb672009-12-08 10:11:22 +0100904 print_ht_capability(data[0] | (data[1] << 8));
905 print_ampdu_length(data[2] & 3);
Christian Lamparterc79c7462010-06-27 00:51:23 +0200906 print_ampdu_spacing((data[2] >> 2) & 7);
Johannes Berg7ddfb672009-12-08 10:11:22 +0100907 print_ht_mcs(data + 3);
Luis R. Rodriguez0c445c22009-12-07 20:55:25 -0500908}
909
Ben Greear489f0ea2013-08-29 15:23:44 -0700910static const char* ntype_11u(uint8_t t)
911{
912 switch (t) {
913 case 0: return "Private";
914 case 1: return "Private with Guest";
915 case 2: return "Chargeable Public";
916 case 3: return "Free Public";
917 case 4: return "Personal Device";
918 case 5: return "Emergency Services Only";
919 case 14: return "Test or Experimental";
920 case 15: return "Wildcard";
921 default: return "Reserved";
922 }
923}
924
925static const char* vgroup_11u(uint8_t t)
926{
927 switch (t) {
928 case 0: return "Unspecified";
929 case 1: return "Assembly";
930 case 2: return "Business";
931 case 3: return "Educational";
932 case 4: return "Factory and Industrial";
933 case 5: return "Institutional";
934 case 6: return "Mercantile";
935 case 7: return "Residential";
936 case 8: return "Storage";
937 case 9: return "Utility and Miscellaneous";
938 case 10: return "Vehicular";
939 case 11: return "Outdoor";
940 default: return "Reserved";
941 }
942}
943
944static void print_interworking(const uint8_t type, uint8_t len, const uint8_t *data)
945{
946 /* See Section 7.3.2.92 in the 802.11u spec. */
947 printf("\n");
948 if (len >= 1) {
949 uint8_t ano = data[0];
950 printf("\t\tNetwork Options: 0x%hx\n", (unsigned short)(ano));
951 printf("\t\t\tNetwork Type: %i (%s)\n",
952 (int)(ano & 0xf), ntype_11u(ano & 0xf));
953 if (ano & (1<<4))
954 printf("\t\t\tInternet\n");
955 if (ano & (1<<5))
956 printf("\t\t\tASRA\n");
957 if (ano & (1<<6))
958 printf("\t\t\tESR\n");
959 if (ano & (1<<7))
960 printf("\t\t\tUESA\n");
961 }
962 if ((len == 3) || (len == 9)) {
963 printf("\t\tVenue Group: %i (%s)\n",
964 (int)(data[1]), vgroup_11u(data[1]));
965 printf("\t\tVenue Type: %i\n", (int)(data[2]));
966 }
967 if (len == 9)
968 printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
969 data[3], data[4], data[5], data[6], data[7], data[8]);
970 else if (len == 7)
971 printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
972 data[1], data[2], data[3], data[4], data[5], data[6]);
973}
974
Ben Greear3f818f62013-08-29 15:23:45 -0700975static void print_11u_advert(const uint8_t type, uint8_t len, const uint8_t *data)
976{
977 /* See Section 7.3.2.93 in the 802.11u spec. */
978 /* TODO: This code below does not decode private protocol IDs */
979 int idx = 0;
980 printf("\n");
981 while (idx < (len - 1)) {
982 uint8_t qri = data[idx];
983 uint8_t proto_id = data[idx + 1];
984 printf("\t\tQuery Response Info: 0x%hx\n", (unsigned short)(qri));
985 printf("\t\t\tQuery Response Length Limit: %i\n",
986 (qri & 0x7f));
987 if (qri & (1<<7))
988 printf("\t\t\tPAME-BI\n");
989 switch(proto_id) {
990 case 0:
991 printf("\t\t\tANQP\n"); break;
992 case 1:
993 printf("\t\t\tMIH Information Service\n"); break;
994 case 2:
995 printf("\t\t\tMIH Command and Event Services Capability Discovery\n"); break;
996 case 3:
997 printf("\t\t\tEmergency Alert System (EAS)\n"); break;
998 case 221:
999 printf("\t\t\tVendor Specific\n"); break;
1000 default:
1001 printf("\t\t\tReserved: %i\n", proto_id); break;
1002 }
1003 idx += 2;
1004 }
1005}
1006
Ben Greearbc9e14f2013-08-29 15:23:46 -07001007static void print_11u_rcon(const uint8_t type, uint8_t len, const uint8_t *data)
1008{
1009 /* See Section 7.3.2.96 in the 802.11u spec. */
1010 int idx = 0;
1011 int ln0 = data[1] & 0xf;
1012 int ln1 = ((data[1] & 0xf0) >> 4);
1013 int ln2 = 0;
1014 printf("\n");
1015
1016 if (ln1)
1017 ln2 = len - 2 - ln0 - ln1;
1018
1019 printf("\t\tANQP OIs: %i\n", data[0]);
1020
1021 if (ln0 > 0) {
1022 printf("\t\tOI 1: ");
1023 if (2 + ln0 > len) {
1024 printf("Invalid IE length.\n");
1025 } else {
1026 for (idx = 0; idx < ln0; idx++) {
1027 printf("%02hx", data[2 + idx]);
1028 }
1029 printf("\n");
1030 }
1031 }
1032
1033 if (ln1 > 0) {
1034 printf("\t\tOI 2: ");
1035 if (2 + ln0 + ln1 > len) {
1036 printf("Invalid IE length.\n");
1037 } else {
1038 for (idx = 0; idx < ln1; idx++) {
1039 printf("%02hx", data[2 + ln0 + idx]);
1040 }
1041 printf("\n");
1042 }
1043 }
1044
1045 if (ln2 > 0) {
1046 printf("\t\tOI 3: ");
1047 if (2 + ln0 + ln1 + ln2 > len) {
1048 printf("Invalid IE length.\n");
1049 } else {
1050 for (idx = 0; idx < ln2; idx++) {
1051 printf("%02hx", data[2 + ln0 + ln1 + idx]);
1052 }
1053 printf("\n");
1054 }
1055 }
1056}
1057
Johannes Berg29f75792012-11-21 15:18:19 +01001058static const char *ht_secondary_offset[4] = {
1059 "no secondary",
1060 "above",
1061 "[reserved!]",
1062 "below",
1063};
1064
Johannes Bergbe7602f2010-09-01 19:43:54 +02001065static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data)
1066{
Johannes Bergbe7602f2010-09-01 19:43:54 +02001067 static const char *protection[4] = {
1068 "no",
1069 "nonmember",
1070 "20 MHz",
1071 "non-HT mixed",
1072 };
1073 static const char *sta_chan_width[2] = {
1074 "20 MHz",
1075 "any",
1076 };
1077
1078 printf("\n");
1079 printf("\t\t * primary channel: %d\n", data[0]);
1080 printf("\t\t * secondary channel offset: %s\n",
Johannes Berg29f75792012-11-21 15:18:19 +01001081 ht_secondary_offset[data[1] & 0x3]);
Johannes Bergbe7602f2010-09-01 19:43:54 +02001082 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
1083 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
1084 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
1085 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
1086 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
1087 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
1088 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
1089 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
1090 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
1091 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
1092 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
1093}
1094
Johannes Berg83b49342009-05-04 13:35:22 +02001095static void print_capabilities(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmann9b880b02009-05-04 01:48:44 -07001096{
Johannes Berg31d2d252009-05-05 11:44:04 +02001097 int i, base, bit;
1098 bool first = true;
Marcel Holtmann9b880b02009-05-04 01:48:44 -07001099
Johannes Berg31d2d252009-05-05 11:44:04 +02001100
1101 for (i = 0; i < len; i++) {
1102 base = i * 8;
1103
1104 for (bit = 0; bit < 8; bit++) {
1105 if (!(data[i] & (1 << bit)))
1106 continue;
1107
1108 if (!first)
1109 printf(",");
1110 else
1111 first = false;
1112
Johannes Berg70c649d2012-11-09 17:07:36 +01001113#define CAPA(bit, name) case bit: printf(" " name); break
1114
Johannes Berg31d2d252009-05-05 11:44:04 +02001115 switch (bit + base) {
Johannes Berg70c649d2012-11-09 17:07:36 +01001116 CAPA(0, "HT Information Exchange Supported");
1117 CAPA(1, "reserved (On-demand Beacon)");
1118 CAPA(2, "Extended Channel Switching");
1119 CAPA(3, "reserved (Wave Indication)");
1120 CAPA(4, "PSMP Capability");
1121 CAPA(5, "reserved (Service Interval Granularity)");
1122 CAPA(6, "S-PSMP Capability");
1123 CAPA(7, "Event");
1124 CAPA(8, "Diagnostics");
1125 CAPA(9, "Multicast Diagnostics");
1126 CAPA(10, "Location Tracking");
1127 CAPA(11, "FMS");
1128 CAPA(12, "Proxy ARP Service");
1129 CAPA(13, "Collocated Interference Reporting");
1130 CAPA(14, "Civic Location");
1131 CAPA(15, "Geospatial Location");
1132 CAPA(16, "TFS");
1133 CAPA(17, "WNM-Sleep Mode");
1134 CAPA(18, "TIM Broadcast");
1135 CAPA(19, "BSS Transition");
1136 CAPA(20, "QoS Traffic Capability");
1137 CAPA(21, "AC Station Count");
1138 CAPA(22, "Multiple BSSID");
1139 CAPA(23, "Timing Measurement");
1140 CAPA(24, "Channel Usage");
1141 CAPA(25, "SSID List");
1142 CAPA(26, "DMS");
1143 CAPA(27, "UTC TSF Offset");
1144 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
1145 CAPA(29, "TDLS Peer PSM Support");
1146 CAPA(30, "TDLS channel switching");
1147 CAPA(31, "Interworking");
1148 CAPA(32, "QoS Map");
1149 CAPA(33, "EBR");
1150 CAPA(34, "SSPN Interface");
1151 CAPA(35, "Reserved");
1152 CAPA(36, "MSGCF Capability");
1153 CAPA(37, "TDLS Support");
1154 CAPA(38, "TDLS Prohibited");
1155 CAPA(39, "TDLS Channel Switching Prohibited");
1156 CAPA(40, "Reject Unadmitted Frame");
1157 CAPA(44, "Identifier Location");
1158 CAPA(45, "U-APSD Coexistence");
1159 CAPA(46, "WNM-Notification");
1160 CAPA(47, "Reserved");
1161 CAPA(48, "UTF-8 SSID");
Johannes Berg31d2d252009-05-05 11:44:04 +02001162 default:
1163 printf(" %d", bit);
1164 break;
1165 }
Johannes Berg70c649d2012-11-09 17:07:36 +01001166#undef CAPA
Johannes Berg31d2d252009-05-05 11:44:04 +02001167 }
1168 }
1169
Marcel Holtmann9b880b02009-05-04 01:48:44 -07001170 printf("\n");
1171}
1172
Jouni Malinen575280c2010-01-06 17:53:59 +02001173static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data)
1174{
1175 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
1176 "Bitmap[0] 0x%x",
1177 data[0], data[1], data[2], data[3]);
1178 if (len - 4)
1179 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
1180 printf("\n");
1181}
1182
Joerg Mayer792172b2013-04-19 22:39:05 +02001183static void print_ibssatim(const uint8_t type, uint8_t len, const uint8_t *data)
1184{
1185 printf(" %d TUs", (data[1] << 8) + data[0]);
1186}
1187
Johannes Berg54eb1612012-11-12 13:07:18 +01001188static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
1189{
1190 printf("\n");
1191 print_vht_info(data[0] | (data[1] << 8) |
1192 (data[2] << 16) | (data[3] << 24),
1193 data + 4);
1194}
1195
Johannes Bergca159932012-11-20 17:26:14 +01001196static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data)
1197{
1198 const char *chandwidths[] = {
1199 [0] = "20 or 40 MHz",
1200 [1] = "80 MHz",
1201 [3] = "80+80 MHz",
1202 [2] = "160 MHz",
1203 };
1204
1205 printf("\n");
1206 printf("\t\t * channel width: %d (%s)\n", data[0],
1207 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
1208 printf("\t\t * center freq segment 1: %d\n", data[1]);
1209 printf("\t\t * center freq segment 2: %d\n", data[2]);
1210 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
1211}
1212
Johannes Berg29f75792012-11-21 15:18:19 +01001213static void print_obss_scan_params(const uint8_t type, uint8_t len, const uint8_t *data)
1214{
1215 printf("\n");
1216 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
1217 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
1218 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
1219 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
1220 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
1221 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
1222 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
1223 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
1224}
1225
1226static void print_secchan_offs(const uint8_t type, uint8_t len, const uint8_t *data)
1227{
1228 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
1229 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
1230 else
1231 printf(" %d\n", data[0]);
1232}
1233
1234static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data)
1235{
1236 printf("\n");
1237 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
1238 printf("\t\t * channel utilisation: %d/255\n", data[2]);
1239 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
1240}
1241
Chun-Yeow Yeoh2d86b0f2013-07-12 19:17:06 +08001242static void print_mesh_conf(const uint8_t type, uint8_t len, const uint8_t *data)
1243{
1244 printf("\n");
1245 printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
1246 printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
1247 printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
1248 printf("\t\t * Synchronization Method ID: %d\n", data[3]);
1249 printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
1250 printf("\t\t * Mesh Formation Info:\n");
1251 printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
1252 if (data[5] & 0x01)
1253 printf("\t\t\t Connected to Mesh Gate\n");
1254 if (data[5] & 0x80)
1255 printf("\t\t\t Connected to AS\n");
1256 printf("\t\t * Mesh Capability\n");
1257 if (data[6] & 0x01)
1258 printf("\t\t\t Accepting Additional Mesh Peerings\n");
1259 if (data[6] & 0x02)
1260 printf("\t\t\t MCCA Supported\n");
1261 if (data[6] & 0x04)
1262 printf("\t\t\t MCCA Enabled\n");
1263 if (data[6] & 0x08)
1264 printf("\t\t\t Forwarding\n");
1265 if (data[6] & 0x10)
1266 printf("\t\t\t MBCA Supported\n");
1267 if (data[6] & 0x20)
1268 printf("\t\t\t TBTT Adjusting\n");
1269 if (data[6] & 0x40)
1270 printf("\t\t\t Mesh Power Save Level\n");
1271}
1272
Johannes Berg83b49342009-05-04 13:35:22 +02001273struct ie_print {
1274 const char *name;
1275 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data);
1276 uint8_t minlen, maxlen;
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001277 uint8_t flags;
Johannes Berg764fe752009-02-12 10:30:32 +01001278};
1279
Johannes Berg83b49342009-05-04 13:35:22 +02001280static void print_ie(const struct ie_print *p, const uint8_t type,
1281 uint8_t len, const uint8_t *data)
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001282{
1283 int i;
1284
Johannes Berg83b49342009-05-04 13:35:22 +02001285 if (!p->print)
1286 return;
1287
1288 printf("\t%s:", p->name);
1289 if (len < p->minlen || len > p->maxlen) {
1290 if (len > 1) {
1291 printf(" <invalid: %d bytes:", len);
1292 for (i = 0; i < len; i++)
1293 printf(" %.02x", data[i]);
1294 printf(">\n");
1295 } else if (len)
1296 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
1297 else
1298 printf(" <invalid: no data>\n");
1299 return;
1300 }
1301
1302 p->print(type, len, data);
1303}
1304
1305#define PRINT_IGN { \
1306 .name = "IGNORE", \
1307 .print = NULL, \
1308 .minlen = 0, \
1309 .maxlen = 255, \
1310}
1311
1312static const struct ie_print ieprinters[] = {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001313 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
1314 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg014d5812009-08-10 15:37:20 +02001315 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
Jouni Malinen575280c2010-01-06 17:53:59 +02001316 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
Joerg Mayer792172b2013-04-19 22:39:05 +02001317 [6] = { "IBSS ATIM window", print_ibssatim, 2, 2, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001318 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +01001319 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001320 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
Joerg Mayer792172b2013-04-19 22:39:05 +02001321 [35] = { "TPC report", print_tpcreport, 2, 2, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001322 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
Johannes Berga2e61862010-09-01 19:28:22 +02001323 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
Joerg Mayer792172b2013-04-19 22:39:05 +02001324 [47] = { "ERP D4.0", print_erp, 1, 255, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +01001325 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
Johannes Bergbe7602f2010-09-01 19:43:54 +02001326 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +01001327 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
Johannes Berg54eb1612012-11-12 13:07:18 +01001328 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
Johannes Bergca159932012-11-20 17:26:14 +01001329 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001330 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
1331 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
Chun-Yeow Yeoh2d86b0f2013-07-12 19:17:06 +08001332 [113] = { "MESH Configuration", print_mesh_conf, 7, 7, BIT(PRINT_SCAN), },
Chun-Yeow Yeoh720583e2012-05-14 23:23:48 +08001333 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001334 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
Ben Greear489f0ea2013-08-29 15:23:44 -07001335 [107] = { "802.11u Interworking", print_interworking, 0, 255, BIT(PRINT_SCAN), },
Ben Greear3f818f62013-08-29 15:23:45 -07001336 [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
Ben Greearbc9e14f2013-08-29 15:23:46 -07001337 [111] = { "802.11u Roaming Consortium", print_11u_rcon, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg83b49342009-05-04 13:35:22 +02001338};
1339
1340static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data)
1341{
1342 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
1343}
1344
Ben Greearf188ab62015-03-17 15:02:33 -07001345static void print_wifi_osen(const uint8_t type, uint8_t len, const uint8_t *data)
1346{
1347 print_osen_ie("OSEN", "OSEN", len, data);
1348}
1349
Johannes Berg1cab57e2009-08-10 15:15:49 +02001350static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
1351{
1352 int i;
1353 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
1354
1355 if (len < 19)
1356 goto invalid;
1357
1358 if (data[0] != 1) {
Johannes Bergcee4fe22009-08-10 15:40:11 +02001359 printf("Parameter: not version 1: ");
Johannes Berg1cab57e2009-08-10 15:15:49 +02001360 return false;
1361 }
1362
Marcel Holtmann89ea7062009-12-19 19:49:24 -08001363 printf("\t * Parameter version 1");
Johannes Berg1cab57e2009-08-10 15:15:49 +02001364
1365 data++;
1366
1367 if (data[0] & 0x80)
Marcel Holtmann89ea7062009-12-19 19:49:24 -08001368 printf("\n\t\t * u-APSD");
Johannes Berg1cab57e2009-08-10 15:15:49 +02001369
1370 data += 2;
1371
1372 for (i = 0; i < 4; i++) {
Marcel Holtmann89ea7062009-12-19 19:49:24 -08001373 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
Yoni Divinsky87181512011-11-16 14:55:11 +02001374 if (data[0] & 0x10)
Johannes Berg1cab57e2009-08-10 15:15:49 +02001375 printf(" acm");
1376 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
1377 (1 << (data[1] >> 4)) - 1);
Johannes Berga2a4c262009-08-10 16:40:51 +02001378 printf(", AIFSN %d", data[0] & 0xf);
Johannes Berg1cab57e2009-08-10 15:15:49 +02001379 if (data[2] | data[3])
Johannes Bergcee4fe22009-08-10 15:40:11 +02001380 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
Johannes Berg1cab57e2009-08-10 15:15:49 +02001381 data += 4;
1382 }
1383
1384 printf("\n");
1385 return true;
1386
1387 invalid:
1388 printf("invalid: ");
1389 return false;
1390}
1391
Johannes Berg83b49342009-05-04 13:35:22 +02001392static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data)
1393{
1394 int i;
1395
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001396 switch (data[0]) {
1397 case 0x00:
Johannes Berg83b49342009-05-04 13:35:22 +02001398 printf(" information:");
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001399 break;
1400 case 0x01:
Johannes Berg1cab57e2009-08-10 15:15:49 +02001401 if (print_wifi_wmm_param(data + 1, len - 1))
1402 return;
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001403 break;
1404 default:
Johannes Berg83b49342009-05-04 13:35:22 +02001405 printf(" type %d:", data[0]);
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001406 break;
1407 }
1408
Johannes Berg1cab57e2009-08-10 15:15:49 +02001409 for(i = 1; i < len; i++)
1410 printf(" %.02x", data[i]);
Marcel Holtmann6ff0c932009-05-04 01:48:46 -07001411 printf("\n");
1412}
1413
Jouni Malinena6816962010-04-06 18:03:57 +03001414static const char * wifi_wps_dev_passwd_id(uint16_t id)
1415{
1416 switch (id) {
1417 case 0:
1418 return "Default (PIN)";
1419 case 1:
1420 return "User-specified";
1421 case 2:
1422 return "Machine-specified";
1423 case 3:
1424 return "Rekey";
1425 case 4:
1426 return "PushButton";
1427 case 5:
1428 return "Registrar-specified";
1429 default:
1430 return "??";
1431 }
1432}
1433
Johannes Berg83b49342009-05-04 13:35:22 +02001434static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg4673a892009-04-30 20:05:59 +02001435{
1436 bool first = true;
1437 __u16 subtype, sublen;
1438
Johannes Berg4673a892009-04-30 20:05:59 +02001439 while (len >= 4) {
1440 subtype = (data[0] << 8) + data[1];
1441 sublen = (data[2] << 8) + data[3];
1442 if (sublen > len)
1443 break;
1444
1445 switch (subtype) {
1446 case 0x104a:
1447 tab_on_first(&first);
Johannes Bergdffc6752009-05-04 13:07:16 +02001448 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
Johannes Berg4673a892009-04-30 20:05:59 +02001449 break;
1450 case 0x1011:
1451 tab_on_first(&first);
1452 printf("\t * Device name: %.*s\n", sublen, data + 4);
1453 break;
Jouni Malinena6816962010-04-06 18:03:57 +03001454 case 0x1012: {
1455 uint16_t id;
1456 tab_on_first(&first);
1457 if (sublen != 2) {
1458 printf("\t * Device Password ID: (invalid "
1459 "length %d)\n", sublen);
1460 break;
1461 }
1462 id = data[4] << 8 | data[5];
1463 printf("\t * Device Password ID: %u (%s)\n",
1464 id, wifi_wps_dev_passwd_id(id));
1465 break;
1466 }
Johannes Berg4673a892009-04-30 20:05:59 +02001467 case 0x1021:
1468 tab_on_first(&first);
1469 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
1470 break;
1471 case 0x1023:
1472 tab_on_first(&first);
1473 printf("\t * Model: %.*s\n", sublen, data + 4);
1474 break;
Jouni Malinena6816962010-04-06 18:03:57 +03001475 case 0x1024:
1476 tab_on_first(&first);
1477 printf("\t * Model Number: %.*s\n", sublen, data + 4);
1478 break;
1479 case 0x103b: {
1480 __u8 val = data[4];
1481 tab_on_first(&first);
1482 printf("\t * Response Type: %d%s\n",
1483 val, val == 3 ? " (AP)" : "");
1484 break;
1485 }
1486 case 0x103c: {
1487 __u8 val = data[4];
1488 tab_on_first(&first);
1489 printf("\t * RF Bands: 0x%x\n", val);
1490 break;
1491 }
1492 case 0x1041: {
1493 __u8 val = data[4];
1494 tab_on_first(&first);
1495 printf("\t * Selected Registrar: 0x%x\n", val);
1496 break;
1497 }
1498 case 0x1042:
1499 tab_on_first(&first);
1500 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
1501 break;
1502 case 0x1044: {
1503 __u8 val = data[4];
1504 tab_on_first(&first);
1505 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
1506 val,
1507 val == 1 ? " (Unconfigured)" : "",
1508 val == 2 ? " (Configured)" : "");
1509 break;
1510 }
Johannes Berg09fe09d2011-06-09 20:34:59 +02001511 case 0x1047:
1512 tab_on_first(&first);
1513 printf("\t * UUID: ");
1514 if (sublen != 16) {
1515 printf("(invalid, length=%d)\n", sublen);
1516 break;
1517 }
1518 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
1519 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
1520 data[4], data[5], data[6], data[7],
1521 data[8], data[9], data[10], data[11],
1522 data[12], data[13], data[14], data[15],
1523 data[16], data[17], data[18], data[19]);
1524 break;
Jouni Malinena6816962010-04-06 18:03:57 +03001525 case 0x1054: {
1526 tab_on_first(&first);
1527 if (sublen != 8) {
1528 printf("\t * Primary Device Type: (invalid "
1529 "length %d)\n", sublen);
1530 break;
1531 }
1532 printf("\t * Primary Device Type: "
1533 "%u-%02x%02x%02x%02x-%u\n",
1534 data[4] << 8 | data[5],
1535 data[6], data[7], data[8], data[9],
1536 data[10] << 8 | data[11]);
1537 break;
1538 }
Johannes Berg7ee5a862009-05-01 11:52:51 +02001539 case 0x1057: {
Johannes Bergfe31a222009-06-08 12:46:43 +02001540 __u8 val = data[4];
Johannes Berg7ee5a862009-05-01 11:52:51 +02001541 tab_on_first(&first);
Johannes Bergfe31a222009-06-08 12:46:43 +02001542 printf("\t * AP setup locked: 0x%.2x\n", val);
Johannes Berg7ee5a862009-05-01 11:52:51 +02001543 break;
1544 }
Jouni Malinena6816962010-04-06 18:03:57 +03001545 case 0x1008:
1546 case 0x1053: {
Johannes Berg4673a892009-04-30 20:05:59 +02001547 __u16 meth = (data[4] << 8) + data[5];
1548 bool comma = false;
1549 tab_on_first(&first);
Jouni Malinena6816962010-04-06 18:03:57 +03001550 printf("\t * %sConfig methods:",
1551 subtype == 0x1053 ? "Selected Registrar ": "");
Johannes Berg4673a892009-04-30 20:05:59 +02001552#define T(bit, name) do { \
1553 if (meth & (1<<bit)) { \
1554 if (comma) \
1555 printf(","); \
1556 comma = true; \
1557 printf(" " name); \
1558 } } while (0)
1559 T(0, "USB");
1560 T(1, "Ethernet");
1561 T(2, "Label");
1562 T(3, "Display");
1563 T(4, "Ext. NFC");
1564 T(5, "Int. NFC");
1565 T(6, "NFC Intf.");
1566 T(7, "PBC");
1567 T(8, "Keypad");
1568 printf("\n");
1569 break;
1570#undef T
1571 }
Johannes Berg2650d462010-03-24 23:09:52 -07001572 default: {
1573 const __u8 *subdata = data + 4;
1574 __u16 tmplen = sublen;
1575
1576 tab_on_first(&first);
1577 printf("\t * Unknown TLV (%#.4x, %d bytes):",
1578 subtype, tmplen);
1579 while (tmplen) {
1580 printf(" %.2x", *subdata);
1581 subdata++;
1582 tmplen--;
1583 }
1584 printf("\n");
Johannes Berg4673a892009-04-30 20:05:59 +02001585 break;
1586 }
Johannes Berg2650d462010-03-24 23:09:52 -07001587 }
Johannes Berg4673a892009-04-30 20:05:59 +02001588
1589 data += sublen + 4;
1590 len -= sublen + 4;
1591 }
1592
1593 if (len != 0) {
1594 printf("\t\t * bogus tail data (%d):", len);
1595 while (len) {
1596 printf(" %.2x", *data);
1597 data++;
1598 len--;
1599 }
1600 printf("\n");
1601 }
1602}
1603
Johannes Berg83b49342009-05-04 13:35:22 +02001604static const struct ie_print wifiprinters[] = {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001605 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
1606 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
1607 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg4673a892009-04-30 20:05:59 +02001608};
1609
Johannes Berg9a223742011-06-09 20:55:44 +02001610static inline void print_p2p(const uint8_t type, uint8_t len, const uint8_t *data)
1611{
1612 bool first = true;
1613 __u8 subtype;
1614 __u16 sublen;
1615
1616 while (len >= 3) {
1617 subtype = data[0];
1618 sublen = (data[2] << 8) + data[1];
1619
1620 if (sublen > len - 3)
1621 break;
1622
1623 switch (subtype) {
1624 case 0x02: /* capability */
1625 tab_on_first(&first);
1626 if (sublen < 2) {
1627 printf("\t * malformed capability\n");
1628 break;
1629 }
1630 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
1631 data[3], data[4]);
1632 break;
1633 case 0x0d: /* device info */
1634 if (sublen < 6 + 2 + 8 + 1) {
1635 printf("\t * malformed device info\n");
1636 break;
1637 }
1638 /* fall through for now */
1639 case 0x00: /* status */
1640 case 0x01: /* minor reason */
1641 case 0x03: /* device ID */
1642 case 0x04: /* GO intent */
1643 case 0x05: /* configuration timeout */
1644 case 0x06: /* listen channel */
1645 case 0x07: /* group BSSID */
1646 case 0x08: /* ext listen timing */
1647 case 0x09: /* intended interface address */
1648 case 0x0a: /* manageability */
1649 case 0x0b: /* channel list */
1650 case 0x0c: /* NoA */
1651 case 0x0e: /* group info */
1652 case 0x0f: /* group ID */
1653 case 0x10: /* interface */
1654 case 0x11: /* operating channel */
1655 case 0x12: /* invitation flags */
1656 case 0xdd: /* vendor specific */
1657 default: {
1658 const __u8 *subdata = data + 4;
1659 __u16 tmplen = sublen;
1660
1661 tab_on_first(&first);
1662 printf("\t * Unknown TLV (%#.2x, %d bytes):",
1663 subtype, tmplen);
1664 while (tmplen) {
1665 printf(" %.2x", *subdata);
1666 subdata++;
1667 tmplen--;
1668 }
1669 printf("\n");
1670 break;
1671 }
1672 }
1673
1674 data += sublen + 3;
1675 len -= sublen + 3;
1676 }
1677
1678 if (len != 0) {
1679 tab_on_first(&first);
1680 printf("\t * bogus tail data (%d):", len);
1681 while (len) {
1682 printf(" %.2x", *data);
1683 data++;
1684 len--;
1685 }
1686 printf("\n");
1687 }
1688}
1689
Ben Greear7e10c4a2013-08-29 15:23:47 -07001690static inline void print_hs20_ind(const uint8_t type, uint8_t len, const uint8_t *data)
1691{
1692 /* I can't find the spec for this...just going off what wireshark uses. */
1693 printf("\n");
1694 if (len > 0)
1695 printf("\t\tDGAF: %i\n", (int)(data[0] & 0x1));
1696 else
1697 printf("\t\tUnexpected length: %i\n", len);
1698}
1699
Johannes Berg9a223742011-06-09 20:55:44 +02001700static const struct ie_print wfa_printers[] = {
1701 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
Ben Greear7e10c4a2013-08-29 15:23:47 -07001702 [16] = { "HotSpot 2.0 Indication", print_hs20_ind, 1, 255, BIT(PRINT_SCAN), },
Ben Greearf188ab62015-03-17 15:02:33 -07001703 [18] = { "HotSpot 2.0 OSEN", print_wifi_osen, 1, 255, BIT(PRINT_SCAN), },
Johannes Berg9a223742011-06-09 20:55:44 +02001704};
1705
Johannes Berg764fe752009-02-12 10:30:32 +01001706static void print_vendor(unsigned char len, unsigned char *data,
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001707 bool unknown, enum print_ie_type ptype)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001708{
1709 int i;
1710
Johannes Bergfbf80af2009-04-30 18:49:44 +02001711 if (len < 3) {
Johannes Berg4673a892009-04-30 20:05:59 +02001712 printf("\tVendor specific: <too short> data:");
Johannes Bergfbf80af2009-04-30 18:49:44 +02001713 for(i = 0; i < len; i++)
1714 printf(" %.02x", data[i]);
1715 printf("\n");
1716 return;
1717 }
1718
Johannes Berg3bd60ef2011-06-09 20:36:59 +02001719 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001720 if (data[3] < ARRAY_SIZE(wifiprinters) &&
1721 wifiprinters[data[3]].name &&
1722 wifiprinters[data[3]].flags & BIT(ptype)) {
Johannes Berg83b49342009-05-04 13:35:22 +02001723 print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4);
1724 return;
1725 }
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001726 if (!unknown)
Johannes Berg4673a892009-04-30 20:05:59 +02001727 return;
Johannes Berg3bd60ef2011-06-09 20:36:59 +02001728 printf("\tMS/WiFi %#.2x, data:", data[3]);
Johannes Berg4673a892009-04-30 20:05:59 +02001729 for(i = 0; i < len - 4; i++)
1730 printf(" %.02x", data[i + 4]);
1731 printf("\n");
1732 return;
1733 }
1734
Johannes Berg9a223742011-06-09 20:55:44 +02001735 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
1736 if (data[3] < ARRAY_SIZE(wfa_printers) &&
1737 wfa_printers[data[3]].name &&
1738 wfa_printers[data[3]].flags & BIT(ptype)) {
1739 print_ie(&wfa_printers[data[3]], data[3], len - 4, data + 4);
1740 return;
1741 }
1742 if (!unknown)
1743 return;
1744 printf("\tWFA %#.2x, data:", data[3]);
1745 for(i = 0; i < len - 4; i++)
1746 printf(" %.02x", data[i + 4]);
1747 printf("\n");
1748 return;
1749 }
1750
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001751 if (!unknown)
Johannes Berg764fe752009-02-12 10:30:32 +01001752 return;
1753
Johannes Bergfbf80af2009-04-30 18:49:44 +02001754 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
Johannes Berg3563f4c2008-09-19 05:08:11 +02001755 data[0], data[1], data[2]);
Johannes Bergfbf80af2009-04-30 18:49:44 +02001756 for (i = 3; i < len; i++)
1757 printf(" %.2x", data[i]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001758 printf("\n");
1759}
1760
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001761void print_ies(unsigned char *ie, int ielen, bool unknown,
1762 enum print_ie_type ptype)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001763{
1764 while (ielen >= 2 && ielen >= ie[1]) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001765 if (ie[0] < ARRAY_SIZE(ieprinters) &&
1766 ieprinters[ie[0]].name &&
1767 ieprinters[ie[0]].flags & BIT(ptype)) {
Johannes Berg83b49342009-05-04 13:35:22 +02001768 print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2);
Johannes Berg764fe752009-02-12 10:30:32 +01001769 } else if (ie[0] == 221 /* vendor */) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001770 print_vendor(ie[1], ie + 2, unknown, ptype);
1771 } else if (unknown) {
Johannes Berg3563f4c2008-09-19 05:08:11 +02001772 int i;
1773
Johannes Berg8086b702009-04-30 18:57:06 +02001774 printf("\tUnknown IE (%d):", ie[0]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001775 for (i=0; i<ie[1]; i++)
Johannes Berg8086b702009-04-30 18:57:06 +02001776 printf(" %.2x", ie[2+i]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001777 printf("\n");
1778 }
1779 ielen -= ie[1] + 2;
1780 ie += ie[1] + 2;
1781 }
1782}
1783
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001784static void print_capa_dmg(__u16 capa)
1785{
1786 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1787 case WLAN_CAPABILITY_DMG_TYPE_AP:
1788 printf(" DMG_ESS");
1789 break;
1790 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1791 printf(" DMG_PCP");
1792 break;
1793 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
1794 printf(" DMG_IBSS");
1795 break;
1796 }
1797
1798 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
1799 printf(" CBAP_Only");
1800 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
1801 printf(" CBAP_Src");
1802 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
1803 printf(" Privacy");
1804 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
1805 printf(" ECPAC");
1806 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
1807 printf(" SpectrumMgmt");
1808 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
1809 printf(" RadioMeasure");
1810}
1811
1812static void print_capa_non_dmg(__u16 capa)
1813{
1814 if (capa & WLAN_CAPABILITY_ESS)
1815 printf(" ESS");
1816 if (capa & WLAN_CAPABILITY_IBSS)
1817 printf(" IBSS");
1818 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
1819 printf(" CfPollable");
1820 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
1821 printf(" CfPollReq");
1822 if (capa & WLAN_CAPABILITY_PRIVACY)
1823 printf(" Privacy");
1824 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
1825 printf(" ShortPreamble");
1826 if (capa & WLAN_CAPABILITY_PBCC)
1827 printf(" PBCC");
1828 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
1829 printf(" ChannelAgility");
1830 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
1831 printf(" SpectrumMgmt");
1832 if (capa & WLAN_CAPABILITY_QOS)
1833 printf(" QoS");
1834 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
1835 printf(" ShortSlotTime");
1836 if (capa & WLAN_CAPABILITY_APSD)
1837 printf(" APSD");
1838 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
1839 printf(" RadioMeasure");
1840 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
1841 printf(" DSSS-OFDM");
1842 if (capa & WLAN_CAPABILITY_DEL_BACK)
1843 printf(" DelayedBACK");
1844 if (capa & WLAN_CAPABILITY_IMM_BACK)
1845 printf(" ImmediateBACK");
1846}
1847
Johannes Berg3563f4c2008-09-19 05:08:11 +02001848static int print_bss_handler(struct nl_msg *msg, void *arg)
1849{
1850 struct nlattr *tb[NL80211_ATTR_MAX + 1];
1851 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1852 struct nlattr *bss[NL80211_BSS_MAX + 1];
1853 char mac_addr[20], dev[20];
1854 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1855 [NL80211_BSS_TSF] = { .type = NLA_U64 },
1856 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1857 [NL80211_BSS_BSSID] = { },
1858 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
1859 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
1860 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
Johannes Bergf2e17e12009-01-07 22:05:49 +01001861 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
1862 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
Johannes Berga56117a2009-07-10 18:31:59 +02001863 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
Holger Schurigc04a78d2009-09-24 11:20:47 +02001864 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
Jouni Malinen575280c2010-01-06 17:53:59 +02001865 [NL80211_BSS_BEACON_IES] = { },
Johannes Berg3563f4c2008-09-19 05:08:11 +02001866 };
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001867 struct scan_params *params = arg;
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001868 int show = params->show_both_ie_sets ? 2 : 1;
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001869 bool is_dmg = false;
Johannes Berg3563f4c2008-09-19 05:08:11 +02001870
1871 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1872 genlmsg_attrlen(gnlh, 0), NULL);
1873
1874 if (!tb[NL80211_ATTR_BSS]) {
Johannes Berg5fe70c02009-11-11 15:18:43 +01001875 fprintf(stderr, "bss info missing!\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001876 return NL_SKIP;
1877 }
1878 if (nla_parse_nested(bss, NL80211_BSS_MAX,
1879 tb[NL80211_ATTR_BSS],
1880 bss_policy)) {
Johannes Berg5fe70c02009-11-11 15:18:43 +01001881 fprintf(stderr, "failed to parse nested attributes!\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001882 return NL_SKIP;
1883 }
1884
1885 if (!bss[NL80211_BSS_BSSID])
1886 return NL_SKIP;
1887
1888 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
Johannes Berg00889fb2013-04-19 00:48:16 +02001889 printf("BSS %s", mac_addr);
1890 if (tb[NL80211_ATTR_IFINDEX]) {
1891 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
1892 printf("(on %s)", dev);
1893 }
Johannes Berga56117a2009-07-10 18:31:59 +02001894
1895 if (bss[NL80211_BSS_STATUS]) {
1896 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
1897 case NL80211_BSS_STATUS_AUTHENTICATED:
1898 printf(" -- authenticated");
1899 break;
1900 case NL80211_BSS_STATUS_ASSOCIATED:
1901 printf(" -- associated");
1902 break;
1903 case NL80211_BSS_STATUS_IBSS_JOINED:
1904 printf(" -- joined");
1905 break;
1906 default:
1907 printf(" -- unknown status: %d",
1908 nla_get_u32(bss[NL80211_BSS_STATUS]));
1909 break;
1910 }
1911 }
1912 printf("\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001913
Johannes Berge7109a82009-02-10 21:14:45 +01001914 if (bss[NL80211_BSS_TSF]) {
1915 unsigned long long tsf;
1916 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
1917 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
1918 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
1919 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
1920 }
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001921 if (bss[NL80211_BSS_FREQUENCY]) {
1922 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
1923 printf("\tfreq: %d\n", freq);
1924 if (freq > 45000)
1925 is_dmg = true;
1926 }
Johannes Berg3563f4c2008-09-19 05:08:11 +02001927 if (bss[NL80211_BSS_BEACON_INTERVAL])
Joerg Mayer792172b2013-04-19 22:39:05 +02001928 printf("\tbeacon interval: %d TUs\n",
Johannes Berg3563f4c2008-09-19 05:08:11 +02001929 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
Marcel Holtmann92a04ec2009-05-04 01:48:45 -07001930 if (bss[NL80211_BSS_CAPABILITY]) {
1931 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
1932 printf("\tcapability:");
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001933 if (is_dmg)
1934 print_capa_dmg(capa);
1935 else
1936 print_capa_non_dmg(capa);
Marcel Holtmann92a04ec2009-05-04 01:48:45 -07001937 printf(" (0x%.4x)\n", capa);
1938 }
Johannes Bergf2e17e12009-01-07 22:05:49 +01001939 if (bss[NL80211_BSS_SIGNAL_MBM]) {
1940 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
1941 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
1942 }
1943 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
1944 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
1945 printf("\tsignal: %d/100\n", s);
1946 }
Holger Schurigc04a78d2009-09-24 11:20:47 +02001947 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
1948 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
1949 printf("\tlast seen: %d ms ago\n", age);
1950 }
Johannes Bergc5514492011-12-07 09:08:40 +01001951
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001952 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
Jouni Malinen575280c2010-01-06 17:53:59 +02001953 if (bss[NL80211_BSS_BEACON_IES])
1954 printf("\tInformation elements from Probe Response "
1955 "frame:\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001956 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
Johannes Berg764fe752009-02-12 10:30:32 +01001957 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001958 params->unknown, params->type);
Jouni Malinen575280c2010-01-06 17:53:59 +02001959 }
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001960 if (bss[NL80211_BSS_BEACON_IES] && show--) {
Jouni Malinen575280c2010-01-06 17:53:59 +02001961 printf("\tInformation elements from Beacon frame:\n");
1962 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
1963 nla_len(bss[NL80211_BSS_BEACON_IES]),
1964 params->unknown, params->type);
1965 }
Johannes Berg3563f4c2008-09-19 05:08:11 +02001966
1967 return NL_SKIP;
1968}
1969
Johannes Berg764fe752009-02-12 10:30:32 +01001970static struct scan_params scan_params;
Johannes Berg3563f4c2008-09-19 05:08:11 +02001971
Johannes Berg7c37a242009-04-08 13:13:28 +02001972static int handle_scan_dump(struct nl80211_state *state,
1973 struct nl_cb *cb,
Johannes Berg3563f4c2008-09-19 05:08:11 +02001974 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +02001975 int argc, char **argv,
1976 enum id_input id)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001977{
Johannes Berg764fe752009-02-12 10:30:32 +01001978 if (argc > 1)
1979 return 1;
1980
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001981 memset(&scan_params, 0, sizeof(scan_params));
1982
Johannes Berg764fe752009-02-12 10:30:32 +01001983 if (argc == 1 && !strcmp(argv[0], "-u"))
1984 scan_params.unknown = true;
Jouni Malinen575280c2010-01-06 17:53:59 +02001985 else if (argc == 1 && !strcmp(argv[0], "-b"))
1986 scan_params.show_both_ie_sets = true;
Johannes Berg764fe752009-02-12 10:30:32 +01001987
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001988 scan_params.type = PRINT_SCAN;
1989
Johannes Berg764fe752009-02-12 10:30:32 +01001990 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler,
1991 &scan_params);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001992 return 0;
1993}
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001994
1995static int handle_scan_combined(struct nl80211_state *state,
1996 struct nl_cb *cb,
1997 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +02001998 int argc, char **argv,
1999 enum id_input id)
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002000{
Johannes Berg559a1712009-05-04 14:06:33 +02002001 char **trig_argv;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002002 static char *dump_argv[] = {
2003 NULL,
2004 "scan",
2005 "dump",
Marcel Holtmann92649ea2009-05-02 13:07:24 -07002006 NULL,
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002007 };
2008 static const __u32 cmds[] = {
2009 NL80211_CMD_NEW_SCAN_RESULTS,
2010 NL80211_CMD_SCAN_ABORTED,
2011 };
Johannes Berg559a1712009-05-04 14:06:33 +02002012 int trig_argc, dump_argc, err;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002013
Johannes Berg559a1712009-05-04 14:06:33 +02002014 if (argc >= 3 && !strcmp(argv[2], "-u")) {
2015 dump_argc = 4;
2016 dump_argv[3] = "-u";
Jouni Malinen575280c2010-01-06 17:53:59 +02002017 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
2018 dump_argc = 4;
2019 dump_argv[3] = "-b";
Johannes Berg559a1712009-05-04 14:06:33 +02002020 } else
2021 dump_argc = 3;
2022
2023 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
2024 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
2025 if (!trig_argv)
2026 return -ENOMEM;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002027 trig_argv[0] = argv[0];
Johannes Berg559a1712009-05-04 14:06:33 +02002028 trig_argv[1] = "scan";
2029 trig_argv[2] = "trigger";
2030 int i;
2031 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
2032 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
Johannes Berg75f42042012-07-19 08:36:05 +02002033 err = handle_cmd(state, id, trig_argc, trig_argv);
Johannes Berg559a1712009-05-04 14:06:33 +02002034 free(trig_argv);
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002035 if (err)
2036 return err;
2037
Johannes Berg61725db2009-04-23 15:57:38 +02002038 /*
2039 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
2040 *
2041 * This code has a bug, which requires creating a separate
2042 * nl80211 socket to fix:
2043 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
2044 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
2045 * before (!) we listen to it, because we only start listening
2046 * after we send our scan request.
2047 *
2048 * Doing it the other way around has a race condition as well,
2049 * if you first open the events socket you may get a notification
2050 * for a previous scan.
2051 *
2052 * The only proper way to fix this would be to listen to events
2053 * before sending the command, and for the kernel to send the
2054 * scan request along with the event, so that you can match up
2055 * whether the scan you requested was finished or aborted (this
2056 * may result in processing a scan that another application
2057 * requested, but that doesn't seem to be a problem).
2058 *
2059 * Alas, the kernel doesn't do that (yet).
2060 */
2061
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002062 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
2063 NL80211_CMD_SCAN_ABORTED) {
2064 printf("scan aborted!\n");
2065 return 0;
2066 }
2067
2068 dump_argv[0] = argv[0];
Johannes Berg75f42042012-07-19 08:36:05 +02002069 return handle_cmd(state, id, dump_argc, dump_argv);
Johannes Berga5fe4ef2009-04-08 14:11:47 +02002070}
Johannes Berg5085aa22014-11-19 13:44:43 +01002071TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]", 0, 0,
Johannes Berg6ca98d22009-05-05 15:00:56 +02002072 CIB_NETDEV, handle_scan_combined,
2073 "Scan on the given frequencies and probe for the given SSIDs\n"
2074 "(or wildcard if not given) unless passive scanning is requested.\n"
Johannes Berg64797a72010-03-24 23:38:25 -07002075 "If -u is specified print unknown data in the scan results.\n"
2076 "Specified (vendor) IEs must be well-formed.");
Johannes Berg4698bfc2009-08-24 12:53:34 +02002077COMMAND(scan, dump, "[-u]",
2078 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
2079 "Dump the current scan results. If -u is specified, print unknown\n"
2080 "data in scan results.");
Johannes Berg5085aa22014-11-19 13:44:43 +01002081COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
Johannes Berg4698bfc2009-08-24 12:53:34 +02002082 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
2083 "Trigger a scan on the given frequencies with probing for the given\n"
2084 "SSIDs (or wildcard if not given) unless passive scanning is requested.");
Luciano Coelho3fce58a2015-03-17 16:11:48 +02002085
2086
2087static int handle_start_sched_scan(struct nl80211_state *state,
2088 struct nl_cb *cb, struct nl_msg *msg,
2089 int argc, char **argv, enum id_input id)
2090{
2091 return parse_sched_scan(msg, &argc, &argv);
2092}
2093
2094static int handle_stop_sched_scan(struct nl80211_state *state, struct nl_cb *cb,
2095 struct nl_msg *msg, int argc, char **argv,
2096 enum id_input id)
2097{
2098 if (argc != 0)
2099 return 1;
2100
2101 return 0;
2102}
2103
2104COMMAND(scan, sched_start,
2105 SCHED_SCAN_OPTIONS,
2106 NL80211_CMD_START_SCHED_SCAN, 0, CIB_NETDEV, handle_start_sched_scan,
2107 "Start a scheduled scan at the specified interval on the given frequencies\n"
2108 "with probing for the given SSIDs (or wildcard if not given) unless passive\n"
2109 "scanning is requested. If matches are specified, only matching results\n"
2110 "will be returned.");
2111COMMAND(scan, sched_stop, "",
2112 NL80211_CMD_STOP_SCHED_SCAN, 0, CIB_NETDEV, handle_stop_sched_scan,
2113 "Stop an ongoing scheduled scan.");