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>
16 #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)
28 #define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
30 static unsigned char wifi_oui
[3] = { 0x00, 0x50, 0xf2 };
31 static unsigned char ieee80211_oui
[3] = { 0x00, 0x0f, 0xac };
37 static int handle_scan(struct nl80211_state
*state
,
40 int argc
, char **argv
)
42 struct nl_msg
*ssids
= NULL
;
45 ssids
= nlmsg_alloc();
48 NLA_PUT(ssids
, 1, 0, "");
49 nla_put_nested(msg
, NL80211_ATTR_SCAN_SSIDS
, ssids
);
56 COMMAND(scan
, trigger
, NULL
,
57 NL80211_CMD_TRIGGER_SCAN
, 0, CIB_NETDEV
, handle_scan
);
59 typedef void (*printfn
)(unsigned char type
, unsigned char len
, unsigned char *data
);
61 static void tab_on_first(bool *first
)
69 static void print_ssid(unsigned char type
, unsigned char len
, unsigned char *data
)
73 for (i
=0; i
<len
; i
++) {
75 printf("%c", data
[i
]);
77 printf("\\x%.2x", data
[i
]);
82 static void print_supprates(unsigned char type
, unsigned char len
, unsigned char *data
)
87 printf("\tSupported rates: ");
89 printf("\tExtended supported rates: ");
91 for (i
=0; i
<len
; i
++) {
92 int r
= data
[i
] & 0x7f;
93 printf("%d.%d%s ", r
/2, 5*(r
&1), data
[i
] & 0x80 ? "*":"");
98 static void print_ds(unsigned char type
, unsigned char len
, unsigned char *data
)
100 printf("\tDS Parameter set: channel %d\n", data
[0]);
103 static void print_ign(unsigned char type
, unsigned char len
, unsigned char *data
)
105 /* ignore for now, not too useful */
108 static void print_country(unsigned char type
, unsigned char len
, unsigned char *data
)
112 printf("\tCountry: %.*s", 2, data
);
118 printf(" (outdoor)");
122 for(i
=0; i
<len
-3; i
++)
123 printf(" %.02x", data
[i
+ 3]);
127 static void print_erp(unsigned char type
, unsigned char len
, unsigned char *data
)
134 printf(" NonERP_Present");
136 printf(" Use_Protection");
138 printf(" Barker_Preamble_Mode");
142 static void print_cipher(unsigned char *data
)
144 if (memcmp(data
, wifi_oui
, 3) == 0) {
147 printf("Use group cipher suite");
162 printf("Unknown (%.02x-%.02x-%.02x:%d)",
163 data
[0], data
[1] ,data
[2], data
[3]);
166 } else if (memcmp(data
, ieee80211_oui
, 3) == 0) {
169 printf("Use group cipher suite");
184 printf("AES-128-CMAC");
187 printf("Unknown (%.02x-%.02x-%.02x:%d)",
188 data
[0], data
[1] ,data
[2], data
[3]);
192 printf("Unknown (%.02x-%.02x-%.02x:%d)",
193 data
[0], data
[1] ,data
[2], data
[3]);
196 static void print_auth(unsigned char *data
)
198 if (memcmp(data
, wifi_oui
, 3) == 0) {
201 printf("IEEE 802.1X");
207 printf("Unknown (%.02x-%.02x-%.02x:%d)",
208 data
[0], data
[1] ,data
[2], data
[3]);
211 } else if (memcmp(data
, ieee80211_oui
, 3) == 0) {
214 printf("IEEE 802.1X");
220 printf("Unknown (%.02x-%.02x-%.02x:%d)",
221 data
[0], data
[1] ,data
[2], data
[3]);
225 printf("Unknown (%.02x-%.02x-%.02x:%d)",
226 data
[0], data
[1] ,data
[2], data
[3]);
229 static void print_rsn_ie(const char *ie
,
230 const char *defcipher
, const char *defauth
,
231 unsigned char len
, unsigned char *data
)
234 __u16 version
, count
, capa
;
240 printf(" <too short> data:");
241 for(i
= 0; i
< len
; i
++)
242 printf(" %.02x", data
[i
]);
247 version
= data
[0] + (data
[1] << 8);
248 tab_on_first(&first
);
249 printf("\t * Version: %d\n", version
);
255 tab_on_first(&first
);
256 printf("\t * Group cipher: %s\n", defcipher
);
257 printf("\t * Pairwise ciphers: %s\n", defcipher
);
261 tab_on_first(&first
);
262 printf("\t * Group cipher: ");
270 tab_on_first(&first
);
271 printf("\t * Pairwise ciphers: %s\n", defcipher
);
275 count
= data
[0] | (data
[1] << 8);
276 tab_on_first(&first
);
277 printf("\t * Pairwise ciphers:");
278 for (i
=0; i
<count
; i
++) {
280 print_cipher(data
+ 2 + (i
* 4));
284 data
+= 2 + (count
* 4);
285 len
-= 2 + (count
* 4);
288 tab_on_first(&first
);
289 printf("\t * Authentication suites: %s\n", defauth
);
293 count
= data
[0] | (data
[1] << 8);
294 tab_on_first(&first
);
295 printf("\t * Authentication suites:");
296 for (i
=0; i
<count
; i
++) {
298 print_auth(data
+ 2 + (i
* 4));
302 data
+= 2 + (count
* 4);
303 len
-= 2 + (count
* 4);
308 capa
= data
[0] | (data
[1] << 8);
309 tab_on_first(&first
);
310 printf("\t * Capabilities: 0x%.4x\n", capa
);
313 static void print_rsn(unsigned char type
, unsigned char len
, unsigned char *data
)
315 print_rsn_ie("RSN", "CCMP", "IEEE 802.1X", len
, data
);
318 static void print_capabilities(unsigned char type
, unsigned char len
, unsigned char *data
)
322 printf("\tExtended capabilties:");
324 printf(" %.02x", data
[i
]);
328 static const printfn ieprinters
[] = {
330 [1] = print_supprates
,
336 [50] = print_supprates
,
337 [127] = print_capabilities
,
340 static void print_wifi_wpa(unsigned char type
, unsigned char len
, unsigned char *data
)
342 print_rsn_ie("WPA", "TKIP", "IEEE 802.1X", len
, data
);
345 static void print_wifi_wmm(unsigned char type
, unsigned char len
, unsigned char *data
)
352 printf("information:");
355 printf("parameter:");
358 printf("type %d:", data
[0]);
362 for(i
=0; i
<len
-1; i
++)
363 printf(" %.02x", data
[i
+ 1]);
367 static void print_wifi_wps(unsigned char type
, unsigned char len
, unsigned char *data
)
370 __u16 subtype
, sublen
;
375 subtype
= (data
[0] << 8) + data
[1];
376 sublen
= (data
[2] << 8) + data
[3];
382 tab_on_first(&first
);
383 printf("\t * Version: %#.2x\n", data
[4]);
386 tab_on_first(&first
);
387 printf("\t * Device name: %.*s\n", sublen
, data
+ 4);
390 tab_on_first(&first
);
391 printf("\t * Manufacturer: %.*s\n", sublen
, data
+ 4);
394 tab_on_first(&first
);
395 printf("\t * Model: %.*s\n", sublen
, data
+ 4);
398 __u16 val
= (data
[4] << 8) | data
[5];
399 tab_on_first(&first
);
400 printf("\t * AP setup locked: 0x%.4x\n", val
);
404 __u16 meth
= (data
[4] << 8) + data
[5];
406 tab_on_first(&first
);
407 printf("\t * Config methods:");
408 #define T(bit, name) do { \
409 if (meth & (1<<bit)) { \
437 printf("\t\t * bogus tail data (%d):", len
);
439 printf(" %.2x", *data
);
447 static const printfn wifiprinters
[] = {
448 [1] = print_wifi_wpa
,
449 [2] = print_wifi_wmm
,
450 [4] = print_wifi_wps
,
453 static void print_vendor(unsigned char len
, unsigned char *data
,
454 struct scan_params
*params
)
459 printf("\tVendor specific: <too short> data:");
460 for(i
= 0; i
< len
; i
++)
461 printf(" %.02x", data
[i
]);
466 if (len
>= 4 && memcmp(data
, wifi_oui
, 3) == 0) {
467 if (data
[3] < ARRAY_SIZE(wifiprinters
) && wifiprinters
[data
[3]])
468 return wifiprinters
[data
[3]](data
[3], len
- 4, data
+ 4);
469 if (!params
->unknown
)
471 printf("\tWiFi OUI %#.2x, data:", data
[3]);
472 for(i
= 0; i
< len
- 4; i
++)
473 printf(" %.02x", data
[i
+ 4]);
478 if (!params
->unknown
)
481 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
482 data
[0], data
[1], data
[2]);
483 for (i
= 3; i
< len
; i
++)
484 printf(" %.2x", data
[i
]);
488 static void print_ies(unsigned char *ie
, int ielen
, struct scan_params
*params
)
490 while (ielen
>= 2 && ielen
>= ie
[1]) {
491 if (ie
[0] < ARRAY_SIZE(ieprinters
) && ieprinters
[ie
[0]]) {
492 ieprinters
[ie
[0]](ie
[0], ie
[1], ie
+ 2);
493 } else if (ie
[0] == 221 /* vendor */) {
494 print_vendor(ie
[1], ie
+ 2, params
);
495 } else if (params
->unknown
) {
498 printf("\tUnknown IE (%d):", ie
[0]);
499 for (i
=0; i
<ie
[1]; i
++)
500 printf(" %.2x", ie
[2+i
]);
508 static int print_bss_handler(struct nl_msg
*msg
, void *arg
)
510 struct nlattr
*tb
[NL80211_ATTR_MAX
+ 1];
511 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
512 struct nlattr
*bss
[NL80211_BSS_MAX
+ 1];
513 char mac_addr
[20], dev
[20];
514 static struct nla_policy bss_policy
[NL80211_BSS_MAX
+ 1] = {
515 [NL80211_BSS_TSF
] = { .type
= NLA_U64
},
516 [NL80211_BSS_FREQUENCY
] = { .type
= NLA_U32
},
517 [NL80211_BSS_BSSID
] = { },
518 [NL80211_BSS_BEACON_INTERVAL
] = { .type
= NLA_U16
},
519 [NL80211_BSS_CAPABILITY
] = { .type
= NLA_U16
},
520 [NL80211_BSS_INFORMATION_ELEMENTS
] = { },
521 [NL80211_BSS_SIGNAL_MBM
] = { .type
= NLA_U32
},
522 [NL80211_BSS_SIGNAL_UNSPEC
] = { .type
= NLA_U8
},
525 nla_parse(tb
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
526 genlmsg_attrlen(gnlh
, 0), NULL
);
528 if (!tb
[NL80211_ATTR_BSS
]) {
529 fprintf(stderr
, "bss info missing!");
532 if (nla_parse_nested(bss
, NL80211_BSS_MAX
,
533 tb
[NL80211_ATTR_BSS
],
535 fprintf(stderr
, "failed to parse nested attributes!");
539 if (!bss
[NL80211_BSS_BSSID
])
542 mac_addr_n2a(mac_addr
, nla_data(bss
[NL80211_BSS_BSSID
]));
543 if_indextoname(nla_get_u32(tb
[NL80211_ATTR_IFINDEX
]), dev
);
544 printf("BSS %s (on %s)\n", mac_addr
, dev
);
546 if (bss
[NL80211_BSS_TSF
]) {
547 unsigned long long tsf
;
548 tsf
= (unsigned long long)nla_get_u64(bss
[NL80211_BSS_TSF
]);
549 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
550 tsf
, tsf
/1000/1000/60/60/24, (tsf
/1000/1000/60/60) % 24,
551 (tsf
/1000/1000/60) % 60, (tsf
/1000/1000) % 60);
553 if (bss
[NL80211_BSS_FREQUENCY
])
554 printf("\tfreq: %d\n",
555 nla_get_u32(bss
[NL80211_BSS_FREQUENCY
]));
556 if (bss
[NL80211_BSS_BEACON_INTERVAL
])
557 printf("\tbeacon interval: %d\n",
558 nla_get_u16(bss
[NL80211_BSS_BEACON_INTERVAL
]));
559 if (bss
[NL80211_BSS_CAPABILITY
]) {
560 __u16 capa
= nla_get_u16(bss
[NL80211_BSS_CAPABILITY
]);
561 printf("\tcapability:");
562 if (capa
& WLAN_CAPABILITY_ESS
)
564 if (capa
& WLAN_CAPABILITY_IBSS
)
566 if (capa
& WLAN_CAPABILITY_PRIVACY
)
568 if (capa
& WLAN_CAPABILITY_SHORT_PREAMBLE
)
569 printf(" ShortPreamble");
570 if (capa
& WLAN_CAPABILITY_PBCC
)
572 if (capa
& WLAN_CAPABILITY_CHANNEL_AGILITY
)
573 printf(" ChannelAgility");
574 if (capa
& WLAN_CAPABILITY_SPECTRUM_MGMT
)
575 printf(" SpectrumMgmt");
576 if (capa
& WLAN_CAPABILITY_QOS
)
578 if (capa
& WLAN_CAPABILITY_SHORT_SLOT_TIME
)
579 printf(" ShortSlotTime");
580 if (capa
& WLAN_CAPABILITY_APSD
)
582 if (capa
& WLAN_CAPABILITY_DSSS_OFDM
)
583 printf(" DSSS-OFDM");
584 printf(" (0x%.4x)\n", capa
);
586 if (bss
[NL80211_BSS_SIGNAL_MBM
]) {
587 int s
= nla_get_u32(bss
[NL80211_BSS_SIGNAL_MBM
]);
588 printf("\tsignal: %d.%.2d dBm\n", s
/100, s
%100);
590 if (bss
[NL80211_BSS_SIGNAL_UNSPEC
]) {
591 unsigned char s
= nla_get_u8(bss
[NL80211_BSS_SIGNAL_UNSPEC
]);
592 printf("\tsignal: %d/100\n", s
);
594 if (bss
[NL80211_BSS_INFORMATION_ELEMENTS
])
595 print_ies(nla_data(bss
[NL80211_BSS_INFORMATION_ELEMENTS
]),
596 nla_len(bss
[NL80211_BSS_INFORMATION_ELEMENTS
]),
602 static struct scan_params scan_params
;
604 static int handle_scan_dump(struct nl80211_state
*state
,
607 int argc
, char **argv
)
612 scan_params
.unknown
= false;
613 if (argc
== 1 && !strcmp(argv
[0], "-u"))
614 scan_params
.unknown
= true;
616 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, print_bss_handler
,
620 COMMAND(scan
, dump
, "[-u]",
621 NL80211_CMD_GET_SCAN
, NLM_F_DUMP
, CIB_NETDEV
, handle_scan_dump
);
623 static int handle_scan_combined(struct nl80211_state
*state
,
626 int argc
, char **argv
)
628 static char *trig_argv
[] = {
633 static char *dump_argv
[] = {
639 static const __u32 cmds
[] = {
640 NL80211_CMD_NEW_SCAN_RESULTS
,
641 NL80211_CMD_SCAN_ABORTED
,
645 trig_argv
[0] = argv
[0];
646 err
= handle_cmd(state
, II_NETDEV
, ARRAY_SIZE(trig_argv
), trig_argv
);
651 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
653 * This code has a bug, which requires creating a separate
654 * nl80211 socket to fix:
655 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
656 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
657 * before (!) we listen to it, because we only start listening
658 * after we send our scan request.
660 * Doing it the other way around has a race condition as well,
661 * if you first open the events socket you may get a notification
662 * for a previous scan.
664 * The only proper way to fix this would be to listen to events
665 * before sending the command, and for the kernel to send the
666 * scan request along with the event, so that you can match up
667 * whether the scan you requested was finished or aborted (this
668 * may result in processing a scan that another application
669 * requested, but that doesn't seem to be a problem).
671 * Alas, the kernel doesn't do that (yet).
674 if (listen_events(state
, ARRAY_SIZE(cmds
), cmds
) ==
675 NL80211_CMD_SCAN_ABORTED
) {
676 printf("scan aborted!\n");
680 if (argc
== 3 && !strcmp(argv
[2], "-u")) {
686 dump_argv
[0] = argv
[0];
687 return handle_cmd(state
, II_NETDEV
, dump_argc
, dump_argv
);
689 TOPLEVEL(scan
, "[-u]", 0, 0, CIB_NETDEV
, handle_scan_combined
);