blob: 2188166e8bde82819e7150266d9211a462b1de26 [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 Berg7c37a242009-04-08 13:13:28 +020073static int handle_scan(struct nl80211_state *state,
74 struct nl_cb *cb,
Johannes Berg3563f4c2008-09-19 05:08:11 +020075 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +020076 int argc, char **argv,
77 enum id_input id)
Johannes Berg3563f4c2008-09-19 05:08:11 +020078{
Johannes Berg559a1712009-05-04 14:06:33 +020079 struct nl_msg *ssids = NULL, *freqs = NULL;
80 char *eptr;
Johannes Berg3563f4c2008-09-19 05:08:11 +020081 int err = -ENOBUFS;
Johannes Berg559a1712009-05-04 14:06:33 +020082 int i;
83 enum {
84 NONE,
85 FREQ,
Johannes Berg64797a72010-03-24 23:38:25 -070086 IES,
Johannes Berg559a1712009-05-04 14:06:33 +020087 SSID,
88 DONE,
89 } parse = NONE;
90 int freq;
91 bool passive = false, have_ssids = false, have_freqs = false;
Johannes Berg64797a72010-03-24 23:38:25 -070092 size_t tmp;
93 unsigned char *ies;
Sam Lefflerfe862232012-10-17 12:20:04 -070094 int flags = 0;
Johannes Berg3563f4c2008-09-19 05:08:11 +020095
96 ssids = nlmsg_alloc();
97 if (!ssids)
98 return -ENOMEM;
Johannes Berg559a1712009-05-04 14:06:33 +020099
100 freqs = nlmsg_alloc();
101 if (!freqs) {
102 nlmsg_free(ssids);
103 return -ENOMEM;
104 }
105
106 for (i = 0; i < argc; i++) {
Johannes Berg559a1712009-05-04 14:06:33 +0200107 switch (parse) {
108 case NONE:
Johannes Berg64797a72010-03-24 23:38:25 -0700109 if (strcmp(argv[i], "freq") == 0) {
110 parse = FREQ;
111 have_freqs = true;
112 break;
113 } else if (strcmp(argv[i], "ies") == 0) {
114 parse = IES;
115 break;
Sam Lefflerfe862232012-10-17 12:20:04 -0700116 } else if (strcmp(argv[i], "lowpri") == 0) {
117 parse = NONE;
118 flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
119 break;
120 } else if (strcmp(argv[i], "flush") == 0) {
121 parse = NONE;
122 flags |= NL80211_SCAN_FLAG_FLUSH;
123 break;
Antonio Quartulliced94d52012-10-26 16:41:48 +0200124 } else if (strcmp(argv[i], "ap-force") == 0) {
125 parse = NONE;
126 flags |= NL80211_SCAN_FLAG_AP;
127 break;
Johannes Berg64797a72010-03-24 23:38:25 -0700128 } else if (strcmp(argv[i], "ssid") == 0) {
129 parse = SSID;
130 have_ssids = true;
131 break;
132 } else if (strcmp(argv[i], "passive") == 0) {
133 parse = DONE;
134 passive = true;
135 break;
136 }
Johannes Berg559a1712009-05-04 14:06:33 +0200137 case DONE:
138 return 1;
139 case FREQ:
140 freq = strtoul(argv[i], &eptr, 10);
Johannes Bergcc12e892011-03-02 14:45:32 +0100141 if (eptr != argv[i] + strlen(argv[i])) {
142 /* failed to parse as number -- maybe a tag? */
143 i--;
144 parse = NONE;
145 continue;
146 }
Johannes Berg559a1712009-05-04 14:06:33 +0200147 NLA_PUT_U32(freqs, i, freq);
Johannes Berg64797a72010-03-24 23:38:25 -0700148 break;
149 case IES:
150 ies = parse_hex(argv[i], &tmp);
151 if (!ies)
152 goto nla_put_failure;
153 NLA_PUT(msg, NL80211_ATTR_IE, tmp, ies);
154 free(ies);
155 parse = NONE;
Johannes Berg559a1712009-05-04 14:06:33 +0200156 break;
157 case SSID:
158 NLA_PUT(ssids, i, strlen(argv[i]), argv[i]);
159 break;
160 }
161 }
162
163 if (!have_ssids)
164 NLA_PUT(ssids, 1, 0, "");
165 if (!passive)
166 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
167
168 if (have_freqs)
169 nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
Sam Lefflerfe862232012-10-17 12:20:04 -0700170 if (flags)
171 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200172
173 err = 0;
174 nla_put_failure:
175 nlmsg_free(ssids);
Johannes Berg559a1712009-05-04 14:06:33 +0200176 nlmsg_free(freqs);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200177 return err;
178}
Johannes Berg3563f4c2008-09-19 05:08:11 +0200179
Marcel Holtmann857d9662009-05-04 01:48:47 -0700180static void tab_on_first(bool *first)
181{
182 if (!*first)
183 printf("\t");
184 else
185 *first = false;
186}
187
Johannes Berg83b49342009-05-04 13:35:22 +0200188static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200189{
Johannes Berg83b49342009-05-04 13:35:22 +0200190 printf(" ");
Johannes Berg748f8482009-05-24 16:48:17 +0200191 print_ssid_escaped(len, data);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200192 printf("\n");
193}
194
Johannes Bergca159932012-11-20 17:26:14 +0100195#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200196#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
197
Johannes Berg83b49342009-05-04 13:35:22 +0200198static void print_supprates(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200199{
200 int i;
201
Johannes Berg83b49342009-05-04 13:35:22 +0200202 printf(" ");
Johannes Berg3563f4c2008-09-19 05:08:11 +0200203
Johannes Berg83b49342009-05-04 13:35:22 +0200204 for (i = 0; i < len; i++) {
Johannes Berg3563f4c2008-09-19 05:08:11 +0200205 int r = data[i] & 0x7f;
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200206
Johannes Bergca159932012-11-20 17:26:14 +0100207 if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
208 printf("VHT");
209 else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
Christian Lamparter1fd19c32011-10-11 13:15:25 +0200210 printf("HT");
211 else
212 printf("%d.%d", r/2, 5*(r&1));
213
214 printf("%s ", data[i] & 0x80 ? "*" : "");
Johannes Berg3563f4c2008-09-19 05:08:11 +0200215 }
216 printf("\n");
217}
218
Johannes Berg83b49342009-05-04 13:35:22 +0200219static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg3563f4c2008-09-19 05:08:11 +0200220{
Johannes Berg83b49342009-05-04 13:35:22 +0200221 printf(" channel %d\n", data[0]);
Johannes Berg3563f4c2008-09-19 05:08:11 +0200222}
223
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500224static const char *country_env_str(char environment)
225{
226 switch (environment) {
227 case 'I':
228 return "Indoor only";
229 case 'O':
230 return "Outdoor only";
231 case ' ':
232 return "Indoor/Outdoor";
233 default:
234 return "bogus";
235 }
236}
237
Johannes Berg83b49342009-05-04 13:35:22 +0200238static void print_country(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700239{
Johannes Berg83b49342009-05-04 13:35:22 +0200240 printf(" %.*s", 2, data);
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500241
242 printf("\tEnvironment: %s\n", country_env_str(data[2]));
243
244 data += 3;
245 len -= 3;
246
247 if (len < 3) {
248 printf("\t\tNo country IE triplets present\n");
249 return;
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700250 }
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500251
252 while (len >= 3) {
253 int end_channel;
Johannes Berg72725712010-09-22 11:32:54 +0200254 union ieee80211_country_ie_triplet *triplet = (void *) data;
Luis R. Rodriguez2b690f02010-02-19 13:55:58 -0500255
256 if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
257 printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
258 triplet->ext.reg_extension_id,
259 triplet->ext.reg_class,
260 triplet->ext.coverage_class,
261 triplet->ext.coverage_class * 450);
262
263 data += 3;
264 len -= 3;
265 continue;
266 }
267
268 /* 2 GHz */
269 if (triplet->chans.first_channel <= 14)
270 end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
271 else
272 end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
273
Luis R. Rodrigueza9859d32010-10-18 11:37:56 -0700274 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 -0500275
276 data += 3;
277 len -= 3;
278 }
279
280 return;
Marcel Holtmannb7e8fa32009-05-04 01:48:43 -0700281}
282
Marcel Holtmannd1563a12009-05-04 09:06:07 -0700283static void print_powerconstraint(const uint8_t type, uint8_t len, const uint8_t *data)
284{
285 printf(" %d dB\n", data[0]);
286}
287
Johannes Berg83b49342009-05-04 13:35:22 +0200288static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmannfc4d1482009-05-04 01:48:42 -0700289{
290 if (data[0] == 0x00)
Johannes Berg83b49342009-05-04 13:35:22 +0200291 printf(" <no flags>");
Marcel Holtmannfc4d1482009-05-04 01:48:42 -0700292 if (data[0] & 0x01)
293 printf(" NonERP_Present");
294 if (data[0] & 0x02)
295 printf(" Use_Protection");
296 if (data[0] & 0x04)
297 printf(" Barker_Preamble_Mode");
298 printf("\n");
299}
300
Johannes Berg83b49342009-05-04 13:35:22 +0200301static void print_cipher(const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700302{
Johannes Berg3bd60ef2011-06-09 20:36:59 +0200303 if (memcmp(data, ms_oui, 3) == 0) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700304 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200305 case 0:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700306 printf("Use group cipher suite");
307 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200308 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700309 printf("WEP-40");
310 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200311 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700312 printf("TKIP");
313 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200314 case 4:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700315 printf("CCMP");
316 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200317 case 5:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700318 printf("WEP-104");
319 break;
320 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200321 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200322 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700323 break;
324 }
325 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
326 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200327 case 0:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700328 printf("Use group cipher suite");
329 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200330 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700331 printf("WEP-40");
332 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200333 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700334 printf("TKIP");
335 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200336 case 4:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700337 printf("CCMP");
338 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200339 case 5:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700340 printf("WEP-104");
341 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200342 case 6:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700343 printf("AES-128-CMAC");
344 break;
Vladimir Kondratieva8b3da92012-07-05 14:36:20 +0300345 case 8:
346 printf("GCMP");
347 break;
Marcel Holtmann857d9662009-05-04 01:48:47 -0700348 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200349 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200350 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700351 break;
352 }
353 } else
Johannes Berg332769c2009-05-05 10:43:33 +0200354 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200355 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700356}
357
Johannes Berg83b49342009-05-04 13:35:22 +0200358static void print_auth(const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700359{
Johannes Berg3bd60ef2011-06-09 20:36:59 +0200360 if (memcmp(data, ms_oui, 3) == 0) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700361 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200362 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700363 printf("IEEE 802.1X");
364 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200365 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700366 printf("PSK");
367 break;
368 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200369 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200370 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700371 break;
372 }
373 } else if (memcmp(data, ieee80211_oui, 3) == 0) {
374 switch (data[3]) {
Johannes Berg510e0e22009-05-05 10:45:30 +0200375 case 1:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700376 printf("IEEE 802.1X");
377 break;
Johannes Berg510e0e22009-05-05 10:45:30 +0200378 case 2:
Marcel Holtmann857d9662009-05-04 01:48:47 -0700379 printf("PSK");
380 break;
Johannes Berg0fe1c412009-05-05 11:21:13 +0200381 case 3:
382 printf("FT/IEEE 802.1X");
383 break;
384 case 4:
385 printf("FT/PSK");
386 break;
387 case 5:
388 printf("IEEE 802.1X/SHA-256");
389 break;
390 case 6:
391 printf("PSK/SHA-256");
392 break;
Marcel Holtmann857d9662009-05-04 01:48:47 -0700393 default:
Johannes Berg332769c2009-05-05 10:43:33 +0200394 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200395 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700396 break;
397 }
398 } else
Johannes Berg332769c2009-05-05 10:43:33 +0200399 printf("%.02x-%.02x-%.02x:%d",
Johannes Berg5594fd22009-05-04 13:06:07 +0200400 data[0], data[1] ,data[2], data[3]);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700401}
402
Johannes Berg83b49342009-05-04 13:35:22 +0200403static void print_rsn_ie(const char *defcipher, const char *defauth,
404 uint8_t len, const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700405{
406 bool first = true;
407 __u16 version, count, capa;
408 int i;
409
Marcel Holtmann857d9662009-05-04 01:48:47 -0700410 version = data[0] + (data[1] << 8);
411 tab_on_first(&first);
412 printf("\t * Version: %d\n", version);
413
414 data += 2;
415 len -= 2;
416
417 if (len < 4) {
418 tab_on_first(&first);
419 printf("\t * Group cipher: %s\n", defcipher);
420 printf("\t * Pairwise ciphers: %s\n", defcipher);
421 return;
422 }
423
424 tab_on_first(&first);
425 printf("\t * Group cipher: ");
426 print_cipher(data);
427 printf("\n");
428
429 data += 4;
430 len -= 4;
431
432 if (len < 2) {
433 tab_on_first(&first);
434 printf("\t * Pairwise ciphers: %s\n", defcipher);
435 return;
436 }
437
438 count = data[0] | (data[1] << 8);
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700439 if (2 + (count * 4) > len)
440 goto invalid;
441
Marcel Holtmann857d9662009-05-04 01:48:47 -0700442 tab_on_first(&first);
443 printf("\t * Pairwise ciphers:");
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700444 for (i = 0; i < count; i++) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700445 printf(" ");
446 print_cipher(data + 2 + (i * 4));
447 }
448 printf("\n");
449
450 data += 2 + (count * 4);
451 len -= 2 + (count * 4);
452
453 if (len < 2) {
454 tab_on_first(&first);
455 printf("\t * Authentication suites: %s\n", defauth);
456 return;
457 }
458
459 count = data[0] | (data[1] << 8);
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700460 if (2 + (count * 4) > len)
461 goto invalid;
462
Marcel Holtmann857d9662009-05-04 01:48:47 -0700463 tab_on_first(&first);
464 printf("\t * Authentication suites:");
Johannes Berg83b49342009-05-04 13:35:22 +0200465 for (i = 0; i < count; i++) {
Marcel Holtmann857d9662009-05-04 01:48:47 -0700466 printf(" ");
467 print_auth(data + 2 + (i * 4));
468 }
469 printf("\n");
470
471 data += 2 + (count * 4);
472 len -= 2 + (count * 4);
473
Johannes Berg6a4f24e2009-05-04 18:57:03 +0200474 if (len >= 2) {
475 capa = data[0] | (data[1] << 8);
476 tab_on_first(&first);
Johannes Bergcadbe892009-05-05 11:33:34 +0200477 printf("\t * Capabilities:");
478 if (capa & 0x0001)
479 printf(" PreAuth");
480 if (capa & 0x0002)
481 printf(" NoPairwise");
482 switch ((capa & 0x000c) >> 2) {
483 case 0:
484 break;
485 case 1:
486 printf(" 2-PTKSA-RC");
487 break;
488 case 2:
489 printf(" 4-PTKSA-RC");
490 break;
491 case 3:
492 printf(" 16-PTKSA-RC");
493 break;
494 }
495 switch ((capa & 0x0030) >> 4) {
496 case 0:
497 break;
498 case 1:
499 printf(" 2-GTKSA-RC");
500 break;
501 case 2:
502 printf(" 4-GTKSA-RC");
503 break;
504 case 3:
505 printf(" 16-GTKSA-RC");
506 break;
507 }
508 if (capa & 0x0040)
509 printf(" MFP-required");
510 if (capa & 0x0080)
511 printf(" MFP-capable");
512 if (capa & 0x0200)
513 printf(" Peerkey-enabled");
514 if (capa & 0x0400)
515 printf(" SPP-AMSDU-capable");
516 if (capa & 0x0800)
517 printf(" SPP-AMSDU-required");
518 printf(" (0x%.4x)\n", capa);
Johannes Berg6a4f24e2009-05-04 18:57:03 +0200519 data += 2;
520 len -= 2;
521 }
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700522
Johannes Berg5ba3f522010-09-29 12:45:06 +0200523 if (len >= 2) {
524 int pmkid_count = data[0] | (data[1] << 8);
525
526 if (len >= 2 + 16 * pmkid_count) {
527 tab_on_first(&first);
528 printf("\t * %d PMKIDs\n", pmkid_count);
529 /* not printing PMKID values */
530 data += 2 + 16 * pmkid_count;
531 len -= 2 + 16 * pmkid_count;
532 } else
533 goto invalid;
534 }
535
536 if (len >= 4) {
537 tab_on_first(&first);
538 printf("\t * Group mgmt cipher suite: ");
539 print_cipher(data);
540 printf("\n");
541 data += 4;
542 len -= 4;
543 }
544
Johannes Bergcadbe892009-05-05 11:33:34 +0200545 invalid:
Marcel Holtmann31d477f2009-05-04 09:49:09 -0700546 if (len != 0) {
547 printf("\t\t * bogus tail data (%d):", len);
548 while (len) {
549 printf(" %.2x", *data);
550 data++;
551 len--;
552 }
553 printf("\n");
554 }
Marcel Holtmann857d9662009-05-04 01:48:47 -0700555}
556
Johannes Berg83b49342009-05-04 13:35:22 +0200557static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmann857d9662009-05-04 01:48:47 -0700558{
Johannes Berg83b49342009-05-04 13:35:22 +0200559 print_rsn_ie("CCMP", "IEEE 802.1X", len, data);
Marcel Holtmann857d9662009-05-04 01:48:47 -0700560}
561
Luis R. Rodriguez0c445c22009-12-07 20:55:25 -0500562static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
563{
Luis R. Rodriguez357c1a52009-12-07 22:05:42 -0500564 printf("\n");
Johannes Berg7ddfb672009-12-08 10:11:22 +0100565 print_ht_capability(data[0] | (data[1] << 8));
566 print_ampdu_length(data[2] & 3);
Christian Lamparterc79c7462010-06-27 00:51:23 +0200567 print_ampdu_spacing((data[2] >> 2) & 7);
Johannes Berg7ddfb672009-12-08 10:11:22 +0100568 print_ht_mcs(data + 3);
Luis R. Rodriguez0c445c22009-12-07 20:55:25 -0500569}
570
Johannes Berg29f75792012-11-21 15:18:19 +0100571static const char *ht_secondary_offset[4] = {
572 "no secondary",
573 "above",
574 "[reserved!]",
575 "below",
576};
577
Johannes Bergbe7602f2010-09-01 19:43:54 +0200578static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data)
579{
Johannes Bergbe7602f2010-09-01 19:43:54 +0200580 static const char *protection[4] = {
581 "no",
582 "nonmember",
583 "20 MHz",
584 "non-HT mixed",
585 };
586 static const char *sta_chan_width[2] = {
587 "20 MHz",
588 "any",
589 };
590
591 printf("\n");
592 printf("\t\t * primary channel: %d\n", data[0]);
593 printf("\t\t * secondary channel offset: %s\n",
Johannes Berg29f75792012-11-21 15:18:19 +0100594 ht_secondary_offset[data[1] & 0x3]);
Johannes Bergbe7602f2010-09-01 19:43:54 +0200595 printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
596 printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
597 printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
598 printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
599 printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
600 printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
601 printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
602 printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
603 printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
604 printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
605 printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
606}
607
Johannes Berg83b49342009-05-04 13:35:22 +0200608static void print_capabilities(const uint8_t type, uint8_t len, const uint8_t *data)
Marcel Holtmann9b880b02009-05-04 01:48:44 -0700609{
Johannes Berg31d2d252009-05-05 11:44:04 +0200610 int i, base, bit;
611 bool first = true;
Marcel Holtmann9b880b02009-05-04 01:48:44 -0700612
Johannes Berg31d2d252009-05-05 11:44:04 +0200613
614 for (i = 0; i < len; i++) {
615 base = i * 8;
616
617 for (bit = 0; bit < 8; bit++) {
618 if (!(data[i] & (1 << bit)))
619 continue;
620
621 if (!first)
622 printf(",");
623 else
624 first = false;
625
Johannes Berg70c649d2012-11-09 17:07:36 +0100626#define CAPA(bit, name) case bit: printf(" " name); break
627
Johannes Berg31d2d252009-05-05 11:44:04 +0200628 switch (bit + base) {
Johannes Berg70c649d2012-11-09 17:07:36 +0100629 CAPA(0, "HT Information Exchange Supported");
630 CAPA(1, "reserved (On-demand Beacon)");
631 CAPA(2, "Extended Channel Switching");
632 CAPA(3, "reserved (Wave Indication)");
633 CAPA(4, "PSMP Capability");
634 CAPA(5, "reserved (Service Interval Granularity)");
635 CAPA(6, "S-PSMP Capability");
636 CAPA(7, "Event");
637 CAPA(8, "Diagnostics");
638 CAPA(9, "Multicast Diagnostics");
639 CAPA(10, "Location Tracking");
640 CAPA(11, "FMS");
641 CAPA(12, "Proxy ARP Service");
642 CAPA(13, "Collocated Interference Reporting");
643 CAPA(14, "Civic Location");
644 CAPA(15, "Geospatial Location");
645 CAPA(16, "TFS");
646 CAPA(17, "WNM-Sleep Mode");
647 CAPA(18, "TIM Broadcast");
648 CAPA(19, "BSS Transition");
649 CAPA(20, "QoS Traffic Capability");
650 CAPA(21, "AC Station Count");
651 CAPA(22, "Multiple BSSID");
652 CAPA(23, "Timing Measurement");
653 CAPA(24, "Channel Usage");
654 CAPA(25, "SSID List");
655 CAPA(26, "DMS");
656 CAPA(27, "UTC TSF Offset");
657 CAPA(28, "TDLS Peer U-APSD Buffer STA Support");
658 CAPA(29, "TDLS Peer PSM Support");
659 CAPA(30, "TDLS channel switching");
660 CAPA(31, "Interworking");
661 CAPA(32, "QoS Map");
662 CAPA(33, "EBR");
663 CAPA(34, "SSPN Interface");
664 CAPA(35, "Reserved");
665 CAPA(36, "MSGCF Capability");
666 CAPA(37, "TDLS Support");
667 CAPA(38, "TDLS Prohibited");
668 CAPA(39, "TDLS Channel Switching Prohibited");
669 CAPA(40, "Reject Unadmitted Frame");
670 CAPA(44, "Identifier Location");
671 CAPA(45, "U-APSD Coexistence");
672 CAPA(46, "WNM-Notification");
673 CAPA(47, "Reserved");
674 CAPA(48, "UTF-8 SSID");
Johannes Berg31d2d252009-05-05 11:44:04 +0200675 default:
676 printf(" %d", bit);
677 break;
678 }
Johannes Berg70c649d2012-11-09 17:07:36 +0100679#undef CAPA
Johannes Berg31d2d252009-05-05 11:44:04 +0200680 }
681 }
682
Marcel Holtmann9b880b02009-05-04 01:48:44 -0700683 printf("\n");
684}
685
Jouni Malinen575280c2010-01-06 17:53:59 +0200686static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data)
687{
688 printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x "
689 "Bitmap[0] 0x%x",
690 data[0], data[1], data[2], data[3]);
691 if (len - 4)
692 printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s");
693 printf("\n");
694}
695
Johannes Berg54eb1612012-11-12 13:07:18 +0100696static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data)
697{
698 printf("\n");
699 print_vht_info(data[0] | (data[1] << 8) |
700 (data[2] << 16) | (data[3] << 24),
701 data + 4);
702}
703
Johannes Bergca159932012-11-20 17:26:14 +0100704static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data)
705{
706 const char *chandwidths[] = {
707 [0] = "20 or 40 MHz",
708 [1] = "80 MHz",
709 [3] = "80+80 MHz",
710 [2] = "160 MHz",
711 };
712
713 printf("\n");
714 printf("\t\t * channel width: %d (%s)\n", data[0],
715 data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
716 printf("\t\t * center freq segment 1: %d\n", data[1]);
717 printf("\t\t * center freq segment 2: %d\n", data[2]);
718 printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
719}
720
Johannes Berg29f75792012-11-21 15:18:19 +0100721static void print_obss_scan_params(const uint8_t type, uint8_t len, const uint8_t *data)
722{
723 printf("\n");
724 printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
725 printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
726 printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
727 printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
728 printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
729 printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
730 printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
731 ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
732}
733
734static void print_secchan_offs(const uint8_t type, uint8_t len, const uint8_t *data)
735{
736 if (data[0] < ARRAY_SIZE(ht_secondary_offset))
737 printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
738 else
739 printf(" %d\n", data[0]);
740}
741
742static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data)
743{
744 printf("\n");
745 printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
746 printf("\t\t * channel utilisation: %d/255\n", data[2]);
747 printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
748}
749
Johannes Berg83b49342009-05-04 13:35:22 +0200750struct ie_print {
751 const char *name;
752 void (*print)(const uint8_t type, uint8_t len, const uint8_t *data);
753 uint8_t minlen, maxlen;
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200754 uint8_t flags;
Johannes Berg764fe752009-02-12 10:30:32 +0100755};
756
Johannes Berg83b49342009-05-04 13:35:22 +0200757static void print_ie(const struct ie_print *p, const uint8_t type,
758 uint8_t len, const uint8_t *data)
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700759{
760 int i;
761
Johannes Berg83b49342009-05-04 13:35:22 +0200762 if (!p->print)
763 return;
764
765 printf("\t%s:", p->name);
766 if (len < p->minlen || len > p->maxlen) {
767 if (len > 1) {
768 printf(" <invalid: %d bytes:", len);
769 for (i = 0; i < len; i++)
770 printf(" %.02x", data[i]);
771 printf(">\n");
772 } else if (len)
773 printf(" <invalid: 1 byte: %.02x>\n", data[0]);
774 else
775 printf(" <invalid: no data>\n");
776 return;
777 }
778
779 p->print(type, len, data);
780}
781
782#define PRINT_IGN { \
783 .name = "IGNORE", \
784 .print = NULL, \
785 .minlen = 0, \
786 .maxlen = 255, \
787}
788
789static const struct ie_print ieprinters[] = {
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200790 [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
791 [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg014d5812009-08-10 15:37:20 +0200792 [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
Jouni Malinen575280c2010-01-06 17:53:59 +0200793 [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200794 [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +0100795 [11] = { "BSS Load", print_bss_load, 5, 5, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200796 [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), },
797 [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
Johannes Berga2e61862010-09-01 19:28:22 +0200798 [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +0100799 [74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
Johannes Bergbe7602f2010-09-01 19:43:54 +0200800 [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), },
Johannes Berg29f75792012-11-21 15:18:19 +0100801 [62] = { "Secondary Channel Offset", print_secchan_offs, 1, 1, BIT(PRINT_SCAN), },
Johannes Berg54eb1612012-11-12 13:07:18 +0100802 [191] = { "VHT capabilities", print_vht_capa, 12, 255, BIT(PRINT_SCAN), },
Johannes Bergca159932012-11-20 17:26:14 +0100803 [192] = { "VHT operation", print_vht_oper, 5, 255, BIT(PRINT_SCAN), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200804 [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), },
805 [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
Chun-Yeow Yeoh720583e2012-05-14 23:23:48 +0800806 [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
Johannes Bergfebeb0c2009-07-25 17:31:08 +0200807 [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg83b49342009-05-04 13:35:22 +0200808};
809
810static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data)
811{
812 print_rsn_ie("TKIP", "IEEE 802.1X", len, data);
813}
814
Johannes Berg1cab57e2009-08-10 15:15:49 +0200815static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
816{
817 int i;
818 static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };
819
820 if (len < 19)
821 goto invalid;
822
823 if (data[0] != 1) {
Johannes Bergcee4fe22009-08-10 15:40:11 +0200824 printf("Parameter: not version 1: ");
Johannes Berg1cab57e2009-08-10 15:15:49 +0200825 return false;
826 }
827
Marcel Holtmann89ea7062009-12-19 19:49:24 -0800828 printf("\t * Parameter version 1");
Johannes Berg1cab57e2009-08-10 15:15:49 +0200829
830 data++;
831
832 if (data[0] & 0x80)
Marcel Holtmann89ea7062009-12-19 19:49:24 -0800833 printf("\n\t\t * u-APSD");
Johannes Berg1cab57e2009-08-10 15:15:49 +0200834
835 data += 2;
836
837 for (i = 0; i < 4; i++) {
Marcel Holtmann89ea7062009-12-19 19:49:24 -0800838 printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
Yoni Divinsky87181512011-11-16 14:55:11 +0200839 if (data[0] & 0x10)
Johannes Berg1cab57e2009-08-10 15:15:49 +0200840 printf(" acm");
841 printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
842 (1 << (data[1] >> 4)) - 1);
Johannes Berga2a4c262009-08-10 16:40:51 +0200843 printf(", AIFSN %d", data[0] & 0xf);
Johannes Berg1cab57e2009-08-10 15:15:49 +0200844 if (data[2] | data[3])
Johannes Bergcee4fe22009-08-10 15:40:11 +0200845 printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
Johannes Berg1cab57e2009-08-10 15:15:49 +0200846 data += 4;
847 }
848
849 printf("\n");
850 return true;
851
852 invalid:
853 printf("invalid: ");
854 return false;
855}
856
Johannes Berg83b49342009-05-04 13:35:22 +0200857static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data)
858{
859 int i;
860
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700861 switch (data[0]) {
862 case 0x00:
Johannes Berg83b49342009-05-04 13:35:22 +0200863 printf(" information:");
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700864 break;
865 case 0x01:
Johannes Berg1cab57e2009-08-10 15:15:49 +0200866 if (print_wifi_wmm_param(data + 1, len - 1))
867 return;
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700868 break;
869 default:
Johannes Berg83b49342009-05-04 13:35:22 +0200870 printf(" type %d:", data[0]);
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700871 break;
872 }
873
Johannes Berg1cab57e2009-08-10 15:15:49 +0200874 for(i = 1; i < len; i++)
875 printf(" %.02x", data[i]);
Marcel Holtmann6ff0c932009-05-04 01:48:46 -0700876 printf("\n");
877}
878
Jouni Malinena6816962010-04-06 18:03:57 +0300879static const char * wifi_wps_dev_passwd_id(uint16_t id)
880{
881 switch (id) {
882 case 0:
883 return "Default (PIN)";
884 case 1:
885 return "User-specified";
886 case 2:
887 return "Machine-specified";
888 case 3:
889 return "Rekey";
890 case 4:
891 return "PushButton";
892 case 5:
893 return "Registrar-specified";
894 default:
895 return "??";
896 }
897}
898
Johannes Berg83b49342009-05-04 13:35:22 +0200899static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data)
Johannes Berg4673a892009-04-30 20:05:59 +0200900{
901 bool first = true;
902 __u16 subtype, sublen;
903
Johannes Berg4673a892009-04-30 20:05:59 +0200904 while (len >= 4) {
905 subtype = (data[0] << 8) + data[1];
906 sublen = (data[2] << 8) + data[3];
907 if (sublen > len)
908 break;
909
910 switch (subtype) {
911 case 0x104a:
912 tab_on_first(&first);
Johannes Bergdffc6752009-05-04 13:07:16 +0200913 printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
Johannes Berg4673a892009-04-30 20:05:59 +0200914 break;
915 case 0x1011:
916 tab_on_first(&first);
917 printf("\t * Device name: %.*s\n", sublen, data + 4);
918 break;
Jouni Malinena6816962010-04-06 18:03:57 +0300919 case 0x1012: {
920 uint16_t id;
921 tab_on_first(&first);
922 if (sublen != 2) {
923 printf("\t * Device Password ID: (invalid "
924 "length %d)\n", sublen);
925 break;
926 }
927 id = data[4] << 8 | data[5];
928 printf("\t * Device Password ID: %u (%s)\n",
929 id, wifi_wps_dev_passwd_id(id));
930 break;
931 }
Johannes Berg4673a892009-04-30 20:05:59 +0200932 case 0x1021:
933 tab_on_first(&first);
934 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
935 break;
936 case 0x1023:
937 tab_on_first(&first);
938 printf("\t * Model: %.*s\n", sublen, data + 4);
939 break;
Jouni Malinena6816962010-04-06 18:03:57 +0300940 case 0x1024:
941 tab_on_first(&first);
942 printf("\t * Model Number: %.*s\n", sublen, data + 4);
943 break;
944 case 0x103b: {
945 __u8 val = data[4];
946 tab_on_first(&first);
947 printf("\t * Response Type: %d%s\n",
948 val, val == 3 ? " (AP)" : "");
949 break;
950 }
951 case 0x103c: {
952 __u8 val = data[4];
953 tab_on_first(&first);
954 printf("\t * RF Bands: 0x%x\n", val);
955 break;
956 }
957 case 0x1041: {
958 __u8 val = data[4];
959 tab_on_first(&first);
960 printf("\t * Selected Registrar: 0x%x\n", val);
961 break;
962 }
963 case 0x1042:
964 tab_on_first(&first);
965 printf("\t * Serial Number: %.*s\n", sublen, data + 4);
966 break;
967 case 0x1044: {
968 __u8 val = data[4];
969 tab_on_first(&first);
970 printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
971 val,
972 val == 1 ? " (Unconfigured)" : "",
973 val == 2 ? " (Configured)" : "");
974 break;
975 }
Johannes Berg09fe09d2011-06-09 20:34:59 +0200976 case 0x1047:
977 tab_on_first(&first);
978 printf("\t * UUID: ");
979 if (sublen != 16) {
980 printf("(invalid, length=%d)\n", sublen);
981 break;
982 }
983 printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
984 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
985 data[4], data[5], data[6], data[7],
986 data[8], data[9], data[10], data[11],
987 data[12], data[13], data[14], data[15],
988 data[16], data[17], data[18], data[19]);
989 break;
Jouni Malinena6816962010-04-06 18:03:57 +0300990 case 0x1054: {
991 tab_on_first(&first);
992 if (sublen != 8) {
993 printf("\t * Primary Device Type: (invalid "
994 "length %d)\n", sublen);
995 break;
996 }
997 printf("\t * Primary Device Type: "
998 "%u-%02x%02x%02x%02x-%u\n",
999 data[4] << 8 | data[5],
1000 data[6], data[7], data[8], data[9],
1001 data[10] << 8 | data[11]);
1002 break;
1003 }
Johannes Berg7ee5a862009-05-01 11:52:51 +02001004 case 0x1057: {
Johannes Bergfe31a222009-06-08 12:46:43 +02001005 __u8 val = data[4];
Johannes Berg7ee5a862009-05-01 11:52:51 +02001006 tab_on_first(&first);
Johannes Bergfe31a222009-06-08 12:46:43 +02001007 printf("\t * AP setup locked: 0x%.2x\n", val);
Johannes Berg7ee5a862009-05-01 11:52:51 +02001008 break;
1009 }
Jouni Malinena6816962010-04-06 18:03:57 +03001010 case 0x1008:
1011 case 0x1053: {
Johannes Berg4673a892009-04-30 20:05:59 +02001012 __u16 meth = (data[4] << 8) + data[5];
1013 bool comma = false;
1014 tab_on_first(&first);
Jouni Malinena6816962010-04-06 18:03:57 +03001015 printf("\t * %sConfig methods:",
1016 subtype == 0x1053 ? "Selected Registrar ": "");
Johannes Berg4673a892009-04-30 20:05:59 +02001017#define T(bit, name) do { \
1018 if (meth & (1<<bit)) { \
1019 if (comma) \
1020 printf(","); \
1021 comma = true; \
1022 printf(" " name); \
1023 } } while (0)
1024 T(0, "USB");
1025 T(1, "Ethernet");
1026 T(2, "Label");
1027 T(3, "Display");
1028 T(4, "Ext. NFC");
1029 T(5, "Int. NFC");
1030 T(6, "NFC Intf.");
1031 T(7, "PBC");
1032 T(8, "Keypad");
1033 printf("\n");
1034 break;
1035#undef T
1036 }
Johannes Berg2650d462010-03-24 23:09:52 -07001037 default: {
1038 const __u8 *subdata = data + 4;
1039 __u16 tmplen = sublen;
1040
1041 tab_on_first(&first);
1042 printf("\t * Unknown TLV (%#.4x, %d bytes):",
1043 subtype, tmplen);
1044 while (tmplen) {
1045 printf(" %.2x", *subdata);
1046 subdata++;
1047 tmplen--;
1048 }
1049 printf("\n");
Johannes Berg4673a892009-04-30 20:05:59 +02001050 break;
1051 }
Johannes Berg2650d462010-03-24 23:09:52 -07001052 }
Johannes Berg4673a892009-04-30 20:05:59 +02001053
1054 data += sublen + 4;
1055 len -= sublen + 4;
1056 }
1057
1058 if (len != 0) {
1059 printf("\t\t * bogus tail data (%d):", len);
1060 while (len) {
1061 printf(" %.2x", *data);
1062 data++;
1063 len--;
1064 }
1065 printf("\n");
1066 }
1067}
1068
Johannes Berg83b49342009-05-04 13:35:22 +02001069static const struct ie_print wifiprinters[] = {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001070 [1] = { "WPA", print_wifi_wpa, 2, 255, BIT(PRINT_SCAN), },
1071 [2] = { "WMM", print_wifi_wmm, 1, 255, BIT(PRINT_SCAN), },
1072 [4] = { "WPS", print_wifi_wps, 0, 255, BIT(PRINT_SCAN), },
Johannes Berg4673a892009-04-30 20:05:59 +02001073};
1074
Johannes Berg9a223742011-06-09 20:55:44 +02001075static inline void print_p2p(const uint8_t type, uint8_t len, const uint8_t *data)
1076{
1077 bool first = true;
1078 __u8 subtype;
1079 __u16 sublen;
1080
1081 while (len >= 3) {
1082 subtype = data[0];
1083 sublen = (data[2] << 8) + data[1];
1084
1085 if (sublen > len - 3)
1086 break;
1087
1088 switch (subtype) {
1089 case 0x02: /* capability */
1090 tab_on_first(&first);
1091 if (sublen < 2) {
1092 printf("\t * malformed capability\n");
1093 break;
1094 }
1095 printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n",
1096 data[3], data[4]);
1097 break;
1098 case 0x0d: /* device info */
1099 if (sublen < 6 + 2 + 8 + 1) {
1100 printf("\t * malformed device info\n");
1101 break;
1102 }
1103 /* fall through for now */
1104 case 0x00: /* status */
1105 case 0x01: /* minor reason */
1106 case 0x03: /* device ID */
1107 case 0x04: /* GO intent */
1108 case 0x05: /* configuration timeout */
1109 case 0x06: /* listen channel */
1110 case 0x07: /* group BSSID */
1111 case 0x08: /* ext listen timing */
1112 case 0x09: /* intended interface address */
1113 case 0x0a: /* manageability */
1114 case 0x0b: /* channel list */
1115 case 0x0c: /* NoA */
1116 case 0x0e: /* group info */
1117 case 0x0f: /* group ID */
1118 case 0x10: /* interface */
1119 case 0x11: /* operating channel */
1120 case 0x12: /* invitation flags */
1121 case 0xdd: /* vendor specific */
1122 default: {
1123 const __u8 *subdata = data + 4;
1124 __u16 tmplen = sublen;
1125
1126 tab_on_first(&first);
1127 printf("\t * Unknown TLV (%#.2x, %d bytes):",
1128 subtype, tmplen);
1129 while (tmplen) {
1130 printf(" %.2x", *subdata);
1131 subdata++;
1132 tmplen--;
1133 }
1134 printf("\n");
1135 break;
1136 }
1137 }
1138
1139 data += sublen + 3;
1140 len -= sublen + 3;
1141 }
1142
1143 if (len != 0) {
1144 tab_on_first(&first);
1145 printf("\t * bogus tail data (%d):", len);
1146 while (len) {
1147 printf(" %.2x", *data);
1148 data++;
1149 len--;
1150 }
1151 printf("\n");
1152 }
1153}
1154
1155static const struct ie_print wfa_printers[] = {
1156 [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), },
1157};
1158
Johannes Berg764fe752009-02-12 10:30:32 +01001159static void print_vendor(unsigned char len, unsigned char *data,
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001160 bool unknown, enum print_ie_type ptype)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001161{
1162 int i;
1163
Johannes Bergfbf80af2009-04-30 18:49:44 +02001164 if (len < 3) {
Johannes Berg4673a892009-04-30 20:05:59 +02001165 printf("\tVendor specific: <too short> data:");
Johannes Bergfbf80af2009-04-30 18:49:44 +02001166 for(i = 0; i < len; i++)
1167 printf(" %.02x", data[i]);
1168 printf("\n");
1169 return;
1170 }
1171
Johannes Berg3bd60ef2011-06-09 20:36:59 +02001172 if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001173 if (data[3] < ARRAY_SIZE(wifiprinters) &&
1174 wifiprinters[data[3]].name &&
1175 wifiprinters[data[3]].flags & BIT(ptype)) {
Johannes Berg83b49342009-05-04 13:35:22 +02001176 print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4);
1177 return;
1178 }
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001179 if (!unknown)
Johannes Berg4673a892009-04-30 20:05:59 +02001180 return;
Johannes Berg3bd60ef2011-06-09 20:36:59 +02001181 printf("\tMS/WiFi %#.2x, data:", data[3]);
Johannes Berg4673a892009-04-30 20:05:59 +02001182 for(i = 0; i < len - 4; i++)
1183 printf(" %.02x", data[i + 4]);
1184 printf("\n");
1185 return;
1186 }
1187
Johannes Berg9a223742011-06-09 20:55:44 +02001188 if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
1189 if (data[3] < ARRAY_SIZE(wfa_printers) &&
1190 wfa_printers[data[3]].name &&
1191 wfa_printers[data[3]].flags & BIT(ptype)) {
1192 print_ie(&wfa_printers[data[3]], data[3], len - 4, data + 4);
1193 return;
1194 }
1195 if (!unknown)
1196 return;
1197 printf("\tWFA %#.2x, data:", data[3]);
1198 for(i = 0; i < len - 4; i++)
1199 printf(" %.02x", data[i + 4]);
1200 printf("\n");
1201 return;
1202 }
1203
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001204 if (!unknown)
Johannes Berg764fe752009-02-12 10:30:32 +01001205 return;
1206
Johannes Bergfbf80af2009-04-30 18:49:44 +02001207 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
Johannes Berg3563f4c2008-09-19 05:08:11 +02001208 data[0], data[1], data[2]);
Johannes Bergfbf80af2009-04-30 18:49:44 +02001209 for (i = 3; i < len; i++)
1210 printf(" %.2x", data[i]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001211 printf("\n");
1212}
1213
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001214void print_ies(unsigned char *ie, int ielen, bool unknown,
1215 enum print_ie_type ptype)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001216{
1217 while (ielen >= 2 && ielen >= ie[1]) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001218 if (ie[0] < ARRAY_SIZE(ieprinters) &&
1219 ieprinters[ie[0]].name &&
1220 ieprinters[ie[0]].flags & BIT(ptype)) {
Johannes Berg83b49342009-05-04 13:35:22 +02001221 print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2);
Johannes Berg764fe752009-02-12 10:30:32 +01001222 } else if (ie[0] == 221 /* vendor */) {
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001223 print_vendor(ie[1], ie + 2, unknown, ptype);
1224 } else if (unknown) {
Johannes Berg3563f4c2008-09-19 05:08:11 +02001225 int i;
1226
Johannes Berg8086b702009-04-30 18:57:06 +02001227 printf("\tUnknown IE (%d):", ie[0]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001228 for (i=0; i<ie[1]; i++)
Johannes Berg8086b702009-04-30 18:57:06 +02001229 printf(" %.2x", ie[2+i]);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001230 printf("\n");
1231 }
1232 ielen -= ie[1] + 2;
1233 ie += ie[1] + 2;
1234 }
1235}
1236
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001237static void print_capa_dmg(__u16 capa)
1238{
1239 switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1240 case WLAN_CAPABILITY_DMG_TYPE_AP:
1241 printf(" DMG_ESS");
1242 break;
1243 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1244 printf(" DMG_PCP");
1245 break;
1246 case WLAN_CAPABILITY_DMG_TYPE_IBSS:
1247 printf(" DMG_IBSS");
1248 break;
1249 }
1250
1251 if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
1252 printf(" CBAP_Only");
1253 if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
1254 printf(" CBAP_Src");
1255 if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
1256 printf(" Privacy");
1257 if (capa & WLAN_CAPABILITY_DMG_ECPAC)
1258 printf(" ECPAC");
1259 if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
1260 printf(" SpectrumMgmt");
1261 if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
1262 printf(" RadioMeasure");
1263}
1264
1265static void print_capa_non_dmg(__u16 capa)
1266{
1267 if (capa & WLAN_CAPABILITY_ESS)
1268 printf(" ESS");
1269 if (capa & WLAN_CAPABILITY_IBSS)
1270 printf(" IBSS");
1271 if (capa & WLAN_CAPABILITY_CF_POLLABLE)
1272 printf(" CfPollable");
1273 if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
1274 printf(" CfPollReq");
1275 if (capa & WLAN_CAPABILITY_PRIVACY)
1276 printf(" Privacy");
1277 if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
1278 printf(" ShortPreamble");
1279 if (capa & WLAN_CAPABILITY_PBCC)
1280 printf(" PBCC");
1281 if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
1282 printf(" ChannelAgility");
1283 if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
1284 printf(" SpectrumMgmt");
1285 if (capa & WLAN_CAPABILITY_QOS)
1286 printf(" QoS");
1287 if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
1288 printf(" ShortSlotTime");
1289 if (capa & WLAN_CAPABILITY_APSD)
1290 printf(" APSD");
1291 if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
1292 printf(" RadioMeasure");
1293 if (capa & WLAN_CAPABILITY_DSSS_OFDM)
1294 printf(" DSSS-OFDM");
1295 if (capa & WLAN_CAPABILITY_DEL_BACK)
1296 printf(" DelayedBACK");
1297 if (capa & WLAN_CAPABILITY_IMM_BACK)
1298 printf(" ImmediateBACK");
1299}
1300
Johannes Berg3563f4c2008-09-19 05:08:11 +02001301static int print_bss_handler(struct nl_msg *msg, void *arg)
1302{
1303 struct nlattr *tb[NL80211_ATTR_MAX + 1];
1304 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1305 struct nlattr *bss[NL80211_BSS_MAX + 1];
1306 char mac_addr[20], dev[20];
1307 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1308 [NL80211_BSS_TSF] = { .type = NLA_U64 },
1309 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1310 [NL80211_BSS_BSSID] = { },
1311 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
1312 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
1313 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
Johannes Bergf2e17e12009-01-07 22:05:49 +01001314 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
1315 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
Johannes Berga56117a2009-07-10 18:31:59 +02001316 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
Holger Schurigc04a78d2009-09-24 11:20:47 +02001317 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
Jouni Malinen575280c2010-01-06 17:53:59 +02001318 [NL80211_BSS_BEACON_IES] = { },
Johannes Berg3563f4c2008-09-19 05:08:11 +02001319 };
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001320 struct scan_params *params = arg;
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001321 int show = params->show_both_ie_sets ? 2 : 1;
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001322 bool is_dmg = false;
Johannes Berg3563f4c2008-09-19 05:08:11 +02001323
1324 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1325 genlmsg_attrlen(gnlh, 0), NULL);
1326
1327 if (!tb[NL80211_ATTR_BSS]) {
Johannes Berg5fe70c02009-11-11 15:18:43 +01001328 fprintf(stderr, "bss info missing!\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001329 return NL_SKIP;
1330 }
1331 if (nla_parse_nested(bss, NL80211_BSS_MAX,
1332 tb[NL80211_ATTR_BSS],
1333 bss_policy)) {
Johannes Berg5fe70c02009-11-11 15:18:43 +01001334 fprintf(stderr, "failed to parse nested attributes!\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001335 return NL_SKIP;
1336 }
1337
1338 if (!bss[NL80211_BSS_BSSID])
1339 return NL_SKIP;
1340
1341 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
1342 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
Johannes Berga56117a2009-07-10 18:31:59 +02001343 printf("BSS %s (on %s)", mac_addr, dev);
1344
1345 if (bss[NL80211_BSS_STATUS]) {
1346 switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
1347 case NL80211_BSS_STATUS_AUTHENTICATED:
1348 printf(" -- authenticated");
1349 break;
1350 case NL80211_BSS_STATUS_ASSOCIATED:
1351 printf(" -- associated");
1352 break;
1353 case NL80211_BSS_STATUS_IBSS_JOINED:
1354 printf(" -- joined");
1355 break;
1356 default:
1357 printf(" -- unknown status: %d",
1358 nla_get_u32(bss[NL80211_BSS_STATUS]));
1359 break;
1360 }
1361 }
1362 printf("\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001363
Johannes Berge7109a82009-02-10 21:14:45 +01001364 if (bss[NL80211_BSS_TSF]) {
1365 unsigned long long tsf;
1366 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
1367 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
1368 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
1369 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
1370 }
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001371 if (bss[NL80211_BSS_FREQUENCY]) {
1372 int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
1373 printf("\tfreq: %d\n", freq);
1374 if (freq > 45000)
1375 is_dmg = true;
1376 }
Johannes Berg3563f4c2008-09-19 05:08:11 +02001377 if (bss[NL80211_BSS_BEACON_INTERVAL])
1378 printf("\tbeacon interval: %d\n",
1379 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
Marcel Holtmann92a04ec2009-05-04 01:48:45 -07001380 if (bss[NL80211_BSS_CAPABILITY]) {
1381 __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
1382 printf("\tcapability:");
Vladimir Kondratiev2e8b82c2012-12-17 13:31:36 +02001383 if (is_dmg)
1384 print_capa_dmg(capa);
1385 else
1386 print_capa_non_dmg(capa);
Marcel Holtmann92a04ec2009-05-04 01:48:45 -07001387 printf(" (0x%.4x)\n", capa);
1388 }
Johannes Bergf2e17e12009-01-07 22:05:49 +01001389 if (bss[NL80211_BSS_SIGNAL_MBM]) {
1390 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
1391 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
1392 }
1393 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
1394 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
1395 printf("\tsignal: %d/100\n", s);
1396 }
Holger Schurigc04a78d2009-09-24 11:20:47 +02001397 if (bss[NL80211_BSS_SEEN_MS_AGO]) {
1398 int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
1399 printf("\tlast seen: %d ms ago\n", age);
1400 }
Johannes Bergc5514492011-12-07 09:08:40 +01001401
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001402 if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
Jouni Malinen575280c2010-01-06 17:53:59 +02001403 if (bss[NL80211_BSS_BEACON_IES])
1404 printf("\tInformation elements from Probe Response "
1405 "frame:\n");
Johannes Berg3563f4c2008-09-19 05:08:11 +02001406 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
Johannes Berg764fe752009-02-12 10:30:32 +01001407 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001408 params->unknown, params->type);
Jouni Malinen575280c2010-01-06 17:53:59 +02001409 }
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001410 if (bss[NL80211_BSS_BEACON_IES] && show--) {
Jouni Malinen575280c2010-01-06 17:53:59 +02001411 printf("\tInformation elements from Beacon frame:\n");
1412 print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
1413 nla_len(bss[NL80211_BSS_BEACON_IES]),
1414 params->unknown, params->type);
1415 }
Johannes Berg3563f4c2008-09-19 05:08:11 +02001416
1417 return NL_SKIP;
1418}
1419
Johannes Berg764fe752009-02-12 10:30:32 +01001420static struct scan_params scan_params;
Johannes Berg3563f4c2008-09-19 05:08:11 +02001421
Johannes Berg7c37a242009-04-08 13:13:28 +02001422static int handle_scan_dump(struct nl80211_state *state,
1423 struct nl_cb *cb,
Johannes Berg3563f4c2008-09-19 05:08:11 +02001424 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +02001425 int argc, char **argv,
1426 enum id_input id)
Johannes Berg3563f4c2008-09-19 05:08:11 +02001427{
Johannes Berg764fe752009-02-12 10:30:32 +01001428 if (argc > 1)
1429 return 1;
1430
Johannes Berg1c5bcd92010-02-06 15:16:29 +01001431 memset(&scan_params, 0, sizeof(scan_params));
1432
Johannes Berg764fe752009-02-12 10:30:32 +01001433 if (argc == 1 && !strcmp(argv[0], "-u"))
1434 scan_params.unknown = true;
Jouni Malinen575280c2010-01-06 17:53:59 +02001435 else if (argc == 1 && !strcmp(argv[0], "-b"))
1436 scan_params.show_both_ie_sets = true;
Johannes Berg764fe752009-02-12 10:30:32 +01001437
Johannes Bergfebeb0c2009-07-25 17:31:08 +02001438 scan_params.type = PRINT_SCAN;
1439
Johannes Berg764fe752009-02-12 10:30:32 +01001440 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler,
1441 &scan_params);
Johannes Berg3563f4c2008-09-19 05:08:11 +02001442 return 0;
1443}
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001444
1445static int handle_scan_combined(struct nl80211_state *state,
1446 struct nl_cb *cb,
1447 struct nl_msg *msg,
Johannes Berg05514f92012-07-19 11:50:50 +02001448 int argc, char **argv,
1449 enum id_input id)
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001450{
Johannes Berg559a1712009-05-04 14:06:33 +02001451 char **trig_argv;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001452 static char *dump_argv[] = {
1453 NULL,
1454 "scan",
1455 "dump",
Marcel Holtmann92649ea2009-05-02 13:07:24 -07001456 NULL,
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001457 };
1458 static const __u32 cmds[] = {
1459 NL80211_CMD_NEW_SCAN_RESULTS,
1460 NL80211_CMD_SCAN_ABORTED,
1461 };
Johannes Berg559a1712009-05-04 14:06:33 +02001462 int trig_argc, dump_argc, err;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001463
Johannes Berg559a1712009-05-04 14:06:33 +02001464 if (argc >= 3 && !strcmp(argv[2], "-u")) {
1465 dump_argc = 4;
1466 dump_argv[3] = "-u";
Jouni Malinen575280c2010-01-06 17:53:59 +02001467 } else if (argc >= 3 && !strcmp(argv[2], "-b")) {
1468 dump_argc = 4;
1469 dump_argv[3] = "-b";
Johannes Berg559a1712009-05-04 14:06:33 +02001470 } else
1471 dump_argc = 3;
1472
1473 trig_argc = 3 + (argc - 2) + (3 - dump_argc);
1474 trig_argv = calloc(trig_argc, sizeof(*trig_argv));
1475 if (!trig_argv)
1476 return -ENOMEM;
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001477 trig_argv[0] = argv[0];
Johannes Berg559a1712009-05-04 14:06:33 +02001478 trig_argv[1] = "scan";
1479 trig_argv[2] = "trigger";
1480 int i;
1481 for (i = 0; i < argc - 2 - (dump_argc - 3); i++)
1482 trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)];
Johannes Berg75f42042012-07-19 08:36:05 +02001483 err = handle_cmd(state, id, trig_argc, trig_argv);
Johannes Berg559a1712009-05-04 14:06:33 +02001484 free(trig_argv);
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001485 if (err)
1486 return err;
1487
Johannes Berg61725db2009-04-23 15:57:38 +02001488 /*
1489 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
1490 *
1491 * This code has a bug, which requires creating a separate
1492 * nl80211 socket to fix:
1493 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
1494 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
1495 * before (!) we listen to it, because we only start listening
1496 * after we send our scan request.
1497 *
1498 * Doing it the other way around has a race condition as well,
1499 * if you first open the events socket you may get a notification
1500 * for a previous scan.
1501 *
1502 * The only proper way to fix this would be to listen to events
1503 * before sending the command, and for the kernel to send the
1504 * scan request along with the event, so that you can match up
1505 * whether the scan you requested was finished or aborted (this
1506 * may result in processing a scan that another application
1507 * requested, but that doesn't seem to be a problem).
1508 *
1509 * Alas, the kernel doesn't do that (yet).
1510 */
1511
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001512 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
1513 NL80211_CMD_SCAN_ABORTED) {
1514 printf("scan aborted!\n");
1515 return 0;
1516 }
1517
1518 dump_argv[0] = argv[0];
Johannes Berg75f42042012-07-19 08:36:05 +02001519 return handle_cmd(state, id, dump_argc, dump_argv);
Johannes Berga5fe4ef2009-04-08 14:11:47 +02001520}
Antonio Quartulliced94d52012-10-26 16:41:48 +02001521TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]", 0, 0,
Johannes Berg6ca98d22009-05-05 15:00:56 +02001522 CIB_NETDEV, handle_scan_combined,
1523 "Scan on the given frequencies and probe for the given SSIDs\n"
1524 "(or wildcard if not given) unless passive scanning is requested.\n"
Johannes Berg64797a72010-03-24 23:38:25 -07001525 "If -u is specified print unknown data in the scan results.\n"
1526 "Specified (vendor) IEs must be well-formed.");
Johannes Berg4698bfc2009-08-24 12:53:34 +02001527COMMAND(scan, dump, "[-u]",
1528 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
1529 "Dump the current scan results. If -u is specified, print unknown\n"
1530 "data in scan results.");
Antonio Quartulliced94d52012-10-26 16:41:48 +02001531COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]",
Johannes Berg4698bfc2009-08-24 12:53:34 +02001532 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
1533 "Trigger a scan on the given frequencies with probing for the given\n"
1534 "SSIDs (or wildcard if not given) unless passive scanning is requested.");