]> git.ipfire.org Git - thirdparty/iw.git/blame - scan.c
add support for showing extended capabilities IE
[thirdparty/iw.git] / scan.c
CommitLineData
3563f4c5
JB
1#include <net/if.h>
2#include <errno.h>
3#include <string.h>
4#include <ctype.h>
764fe753 5#include <stdbool.h>
3563f4c5
JB
6
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
764fe753
JB
16struct scan_params {
17 bool unknown;
18};
19
7c37a24d
JB
20static int handle_scan(struct nl80211_state *state,
21 struct nl_cb *cb,
3563f4c5
JB
22 struct nl_msg *msg,
23 int argc, char **argv)
24{
25 struct nl_msg *ssids = NULL;
26 int err = -ENOBUFS;
27
28 ssids = nlmsg_alloc();
29 if (!ssids)
30 return -ENOMEM;
31 NLA_PUT(ssids, 1, 0, "");
32 nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
33
34 err = 0;
35 nla_put_failure:
36 nlmsg_free(ssids);
37 return err;
38}
39COMMAND(scan, trigger, NULL,
40 NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan);
41
42typedef void (*printfn)(unsigned char type, unsigned char len, unsigned char *data);
43
44static void print_ssid(unsigned char type, unsigned char len, unsigned char *data)
45{
46 int i;
47 printf("\tSSID: ");
48 for (i=0; i<len; i++) {
49 if (isprint(data[i]))
50 printf("%c", data[i]);
51 else
52 printf("\\x%.2x", data[i]);
53 }
54 printf("\n");
55}
56
57static void print_supprates(unsigned char type, unsigned char len, unsigned char *data)
58{
59 int i;
60
61 if (type == 1)
62 printf("\tSupported rates: ");
63 else
64 printf("\tExtended supported rates: ");
65
66 for (i=0; i<len; i++) {
67 int r = data[i] & 0x7f;
68 printf("%d.%d%s ", r/2, 5*(r&1), data[i] & 0x80 ? "*":"");
69 }
70 printf("\n");
71}
72
73static void print_ds(unsigned char type, unsigned char len, unsigned char *data)
74{
75 printf("\tDS Parameter set: channel %d\n", data[0]);
76}
77
78static void print_ign(unsigned char type, unsigned char len, unsigned char *data)
79{
80 /* ignore for now, not too useful */
81}
82
b7e8fa37
MH
83static void print_country(unsigned char type, unsigned char len, unsigned char *data)
84{
85 int i;
86
87 printf("\tCountry: %.*s", 2, data);
88 switch (data[2]) {
89 case 'I':
90 printf(" (indoor)");
91 break;
92 case 'O':
93 printf(" (outdoor)");
94 break;
95 }
96 printf(", data:");
97 for(i=0; i<len-3; i++)
98 printf(" %.02x", data[i + 3]);
99 printf("\n");
100}
101
fc4d1484
MH
102static void print_erp(unsigned char type, unsigned char len, unsigned char *data)
103{
104 if (data[0] == 0x00)
105 return;
106
107 printf("\tERP:");
108 if (data[0] & 0x01)
109 printf(" NonERP_Present");
110 if (data[0] & 0x02)
111 printf(" Use_Protection");
112 if (data[0] & 0x04)
113 printf(" Barker_Preamble_Mode");
114 printf("\n");
115}
116
9b880b00
MH
117static void print_capabilities(unsigned char type, unsigned char len, unsigned char *data)
118{
119 int i;
120
121 printf("\tExtended capabilties:");
122 for(i=0; i<len; i++)
123 printf(" %.02x", data[i]);
124 printf("\n");
125}
126
764fe753
JB
127static const printfn ieprinters[] = {
128 [0] = print_ssid,
129 [1] = print_supprates,
130 [3] = print_ds,
131 [5] = print_ign,
b7e8fa37 132 [7] = print_country,
fc4d1484 133 [42] = print_erp,
764fe753 134 [50] = print_supprates,
9b880b00 135 [127] = print_capabilities,
764fe753
JB
136};
137
4673a894
JB
138static void tab_on_first(bool *first)
139{
140 if (!*first)
141 printf("\t");
142 else
143 *first = false;
144}
145
146static void print_wifi_wps(unsigned char type, unsigned char len, unsigned char *data)
147{
148 bool first = true;
149 __u16 subtype, sublen;
150
151 printf("\tWPS:");
152
153 while (len >= 4) {
154 subtype = (data[0] << 8) + data[1];
155 sublen = (data[2] << 8) + data[3];
156 if (sublen > len)
157 break;
158
159 switch (subtype) {
160 case 0x104a:
161 tab_on_first(&first);
162 printf("\t * Version: %#.2x\n", data[4]);
163 break;
164 case 0x1011:
165 tab_on_first(&first);
166 printf("\t * Device name: %.*s\n", sublen, data + 4);
167 break;
168 case 0x1021:
169 tab_on_first(&first);
170 printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
171 break;
172 case 0x1023:
173 tab_on_first(&first);
174 printf("\t * Model: %.*s\n", sublen, data + 4);
175 break;
7ee5a865
JB
176 case 0x1057: {
177 __u16 val = (data[4] << 8) | data[5];
178 tab_on_first(&first);
179 printf("\t * AP setup locked: 0x%.4x\n", val);
180 break;
181 }
4673a894
JB
182 case 0x1008: {
183 __u16 meth = (data[4] << 8) + data[5];
184 bool comma = false;
185 tab_on_first(&first);
186 printf("\t * Config methods:");
187#define T(bit, name) do { \
188 if (meth & (1<<bit)) { \
189 if (comma) \
190 printf(","); \
191 comma = true; \
192 printf(" " name); \
193 } } while (0)
194 T(0, "USB");
195 T(1, "Ethernet");
196 T(2, "Label");
197 T(3, "Display");
198 T(4, "Ext. NFC");
199 T(5, "Int. NFC");
200 T(6, "NFC Intf.");
201 T(7, "PBC");
202 T(8, "Keypad");
203 printf("\n");
204 break;
205#undef T
206 }
207 default:
208 break;
209 }
210
211 data += sublen + 4;
212 len -= sublen + 4;
213 }
214
215 if (len != 0) {
216 printf("\t\t * bogus tail data (%d):", len);
217 while (len) {
218 printf(" %.2x", *data);
219 data++;
220 len--;
221 }
222 printf("\n");
223 }
224}
225
226static const printfn wifiprinters[] = {
227 [4] = print_wifi_wps,
228};
229
764fe753
JB
230static void print_vendor(unsigned char len, unsigned char *data,
231 struct scan_params *params)
3563f4c5
JB
232{
233 int i;
234
fbf80af5 235 if (len < 3) {
4673a894 236 printf("\tVendor specific: <too short> data:");
fbf80af5
JB
237 for(i = 0; i < len; i++)
238 printf(" %.02x", data[i]);
239 printf("\n");
240 return;
241 }
242
4673a894
JB
243 if (len >= 4 && data[0] == 0x00 && data[1] == 0x50 && data[2] == 0xF2) {
244 if (data[3] < ARRAY_SIZE(wifiprinters) && wifiprinters[data[3]])
245 return wifiprinters[data[3]](data[3], len - 4, data + 4);
246 if (!params->unknown)
247 return;
248 printf("\tWiFi OUI %#.2x data:", data[3]);
249 for(i = 0; i < len - 4; i++)
250 printf(" %.02x", data[i + 4]);
251 printf("\n");
252 return;
253 }
254
764fe753
JB
255 if (!params->unknown)
256 return;
257
fbf80af5 258 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
3563f4c5 259 data[0], data[1], data[2]);
fbf80af5
JB
260 for (i = 3; i < len; i++)
261 printf(" %.2x", data[i]);
3563f4c5
JB
262 printf("\n");
263}
264
764fe753 265static void print_ies(unsigned char *ie, int ielen, struct scan_params *params)
3563f4c5
JB
266{
267 while (ielen >= 2 && ielen >= ie[1]) {
97ebbaf5 268 if (ie[0] < ARRAY_SIZE(ieprinters) && ieprinters[ie[0]]) {
3563f4c5 269 ieprinters[ie[0]](ie[0], ie[1], ie + 2);
764fe753
JB
270 } else if (ie[0] == 221 /* vendor */) {
271 print_vendor(ie[1], ie + 2, params);
272 } else if (params->unknown) {
3563f4c5
JB
273 int i;
274
8086b700 275 printf("\tUnknown IE (%d):", ie[0]);
3563f4c5 276 for (i=0; i<ie[1]; i++)
8086b700 277 printf(" %.2x", ie[2+i]);
3563f4c5
JB
278 printf("\n");
279 }
280 ielen -= ie[1] + 2;
281 ie += ie[1] + 2;
282 }
283}
284
285static int print_bss_handler(struct nl_msg *msg, void *arg)
286{
287 struct nlattr *tb[NL80211_ATTR_MAX + 1];
288 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
289 struct nlattr *bss[NL80211_BSS_MAX + 1];
290 char mac_addr[20], dev[20];
291 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
292 [NL80211_BSS_TSF] = { .type = NLA_U64 },
293 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
294 [NL80211_BSS_BSSID] = { },
295 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
296 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
297 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
f2e17e1f
JB
298 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
299 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
3563f4c5
JB
300 };
301
302 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
303 genlmsg_attrlen(gnlh, 0), NULL);
304
305 if (!tb[NL80211_ATTR_BSS]) {
306 fprintf(stderr, "bss info missing!");
307 return NL_SKIP;
308 }
309 if (nla_parse_nested(bss, NL80211_BSS_MAX,
310 tb[NL80211_ATTR_BSS],
311 bss_policy)) {
312 fprintf(stderr, "failed to parse nested attributes!");
313 return NL_SKIP;
314 }
315
316 if (!bss[NL80211_BSS_BSSID])
317 return NL_SKIP;
318
319 mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
320 if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
321 printf("BSS %s (on %s)\n", mac_addr, dev);
322
e7109a8a
JB
323 if (bss[NL80211_BSS_TSF]) {
324 unsigned long long tsf;
325 tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
326 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
327 tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
328 (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
329 }
3563f4c5
JB
330 if (bss[NL80211_BSS_FREQUENCY])
331 printf("\tfreq: %d\n",
332 nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
333 if (bss[NL80211_BSS_BEACON_INTERVAL])
334 printf("\tbeacon interval: %d\n",
335 nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
336 if (bss[NL80211_BSS_CAPABILITY])
337 printf("\tcapability: 0x%.4x\n",
338 nla_get_u16(bss[NL80211_BSS_CAPABILITY]));
f2e17e1f
JB
339 if (bss[NL80211_BSS_SIGNAL_MBM]) {
340 int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
341 printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
342 }
343 if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
344 unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
345 printf("\tsignal: %d/100\n", s);
346 }
3563f4c5
JB
347 if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
348 print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
764fe753
JB
349 nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
350 arg);
3563f4c5
JB
351
352 return NL_SKIP;
353}
354
764fe753 355static struct scan_params scan_params;
3563f4c5 356
7c37a24d
JB
357static int handle_scan_dump(struct nl80211_state *state,
358 struct nl_cb *cb,
3563f4c5
JB
359 struct nl_msg *msg,
360 int argc, char **argv)
361{
764fe753
JB
362 if (argc > 1)
363 return 1;
364
365 scan_params.unknown = false;
366 if (argc == 1 && !strcmp(argv[0], "-u"))
367 scan_params.unknown = true;
368
369 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler,
370 &scan_params);
3563f4c5
JB
371 return 0;
372}
764fe753 373COMMAND(scan, dump, "[-u]",
3563f4c5 374 NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump);
a5fe4ef2
JB
375
376static int handle_scan_combined(struct nl80211_state *state,
377 struct nl_cb *cb,
378 struct nl_msg *msg,
379 int argc, char **argv)
380{
381 static char *trig_argv[] = {
382 NULL,
383 "scan",
384 "trigger",
385 };
386 static char *dump_argv[] = {
387 NULL,
388 "scan",
389 "dump",
92649eab 390 NULL,
a5fe4ef2
JB
391 };
392 static const __u32 cmds[] = {
393 NL80211_CMD_NEW_SCAN_RESULTS,
394 NL80211_CMD_SCAN_ABORTED,
395 };
92649eab 396 int dump_argc, err;
a5fe4ef2
JB
397
398 trig_argv[0] = argv[0];
399 err = handle_cmd(state, II_NETDEV, ARRAY_SIZE(trig_argv), trig_argv);
400 if (err)
401 return err;
402
61725dbe
JB
403 /*
404 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
405 *
406 * This code has a bug, which requires creating a separate
407 * nl80211 socket to fix:
408 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
409 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
410 * before (!) we listen to it, because we only start listening
411 * after we send our scan request.
412 *
413 * Doing it the other way around has a race condition as well,
414 * if you first open the events socket you may get a notification
415 * for a previous scan.
416 *
417 * The only proper way to fix this would be to listen to events
418 * before sending the command, and for the kernel to send the
419 * scan request along with the event, so that you can match up
420 * whether the scan you requested was finished or aborted (this
421 * may result in processing a scan that another application
422 * requested, but that doesn't seem to be a problem).
423 *
424 * Alas, the kernel doesn't do that (yet).
425 */
426
a5fe4ef2
JB
427 if (listen_events(state, ARRAY_SIZE(cmds), cmds) ==
428 NL80211_CMD_SCAN_ABORTED) {
429 printf("scan aborted!\n");
430 return 0;
431 }
432
92649eab
MH
433 if (argc == 3 && !strcmp(argv[2], "-u")) {
434 dump_argc = 4;
435 dump_argv[3] = "-u";
436 } else
437 dump_argc = 3;
438
a5fe4ef2 439 dump_argv[0] = argv[0];
92649eab 440 return handle_cmd(state, II_NETDEV, dump_argc, dump_argv);
a5fe4ef2 441}
92649eab 442TOPLEVEL(scan, "[-u]", 0, 0, CIB_NETDEV, handle_scan_combined);