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 };
35 enum print_ie_type type
;
38 static int handle_scan(struct nl80211_state
*state
,
41 int argc
, char **argv
)
43 struct nl_msg
*ssids
= NULL
, *freqs
= NULL
;
54 bool passive
= false, have_ssids
= false, have_freqs
= false;
56 ssids
= nlmsg_alloc();
60 freqs
= nlmsg_alloc();
66 for (i
= 0; i
< argc
; i
++) {
67 if (parse
== NONE
&& strcmp(argv
[i
], "freq") == 0) {
71 } else if (parse
< SSID
&& strcmp(argv
[i
], "ssid") == 0) {
75 } else if (parse
< SSID
&& strcmp(argv
[i
], "passive") == 0) {
86 freq
= strtoul(argv
[i
], &eptr
, 10);
87 if (eptr
!= argv
[i
] + strlen(argv
[i
]))
89 NLA_PUT_U32(freqs
, i
, freq
);
92 NLA_PUT(ssids
, i
, strlen(argv
[i
]), argv
[i
]);
98 NLA_PUT(ssids
, 1, 0, "");
100 nla_put_nested(msg
, NL80211_ATTR_SCAN_SSIDS
, ssids
);
103 nla_put_nested(msg
, NL80211_ATTR_SCAN_FREQUENCIES
, freqs
);
111 COMMAND(scan
, trigger
, "[freq <freq>*] [ssid <ssid>*|passive]",
112 NL80211_CMD_TRIGGER_SCAN
, 0, CIB_NETDEV
, handle_scan
,
113 "Trigger a scan on the given frequencies with probing for the given\n"
114 "SSIDs (or wildcard if not given) unless passive scanning is requested.");
116 static void tab_on_first(bool *first
)
124 static void print_ssid(const uint8_t type
, uint8_t len
, const uint8_t *data
)
127 print_ssid_escaped(len
, data
);
131 static void print_supprates(const uint8_t type
, uint8_t len
, const uint8_t *data
)
137 for (i
= 0; i
< len
; i
++) {
138 int r
= data
[i
] & 0x7f;
139 printf("%d.%d%s ", r
/2, 5*(r
&1), data
[i
] & 0x80 ? "*":"");
144 static void print_ds(const uint8_t type
, uint8_t len
, const uint8_t *data
)
146 printf(" channel %d\n", data
[0]);
149 static void print_country(const uint8_t type
, uint8_t len
, const uint8_t *data
)
153 printf(" %.*s", 2, data
);
159 printf(" (outdoor)");
162 printf(" (in/outdoor)");
165 printf(" (invalid environment)");
169 for(i
=0; i
<len
-3; i
++)
170 printf(" %.02x", data
[i
+ 3]);
174 static void print_powerconstraint(const uint8_t type
, uint8_t len
, const uint8_t *data
)
176 printf(" %d dB\n", data
[0]);
179 static void print_erp(const uint8_t type
, uint8_t len
, const uint8_t *data
)
182 printf(" <no flags>");
184 printf(" NonERP_Present");
186 printf(" Use_Protection");
188 printf(" Barker_Preamble_Mode");
192 static void print_cipher(const uint8_t *data
)
194 if (memcmp(data
, wifi_oui
, 3) == 0) {
197 printf("Use group cipher suite");
212 printf("%.02x-%.02x-%.02x:%d",
213 data
[0], data
[1] ,data
[2], data
[3]);
216 } else if (memcmp(data
, ieee80211_oui
, 3) == 0) {
219 printf("Use group cipher suite");
234 printf("AES-128-CMAC");
237 printf("%.02x-%.02x-%.02x:%d",
238 data
[0], data
[1] ,data
[2], data
[3]);
242 printf("%.02x-%.02x-%.02x:%d",
243 data
[0], data
[1] ,data
[2], data
[3]);
246 static void print_auth(const uint8_t *data
)
248 if (memcmp(data
, wifi_oui
, 3) == 0) {
251 printf("IEEE 802.1X");
257 printf("%.02x-%.02x-%.02x:%d",
258 data
[0], data
[1] ,data
[2], data
[3]);
261 } else if (memcmp(data
, ieee80211_oui
, 3) == 0) {
264 printf("IEEE 802.1X");
270 printf("FT/IEEE 802.1X");
276 printf("IEEE 802.1X/SHA-256");
279 printf("PSK/SHA-256");
282 printf("%.02x-%.02x-%.02x:%d",
283 data
[0], data
[1] ,data
[2], data
[3]);
287 printf("%.02x-%.02x-%.02x:%d",
288 data
[0], data
[1] ,data
[2], data
[3]);
291 static void print_rsn_ie(const char *defcipher
, const char *defauth
,
292 uint8_t len
, const uint8_t *data
)
295 __u16 version
, count
, capa
;
298 version
= data
[0] + (data
[1] << 8);
299 tab_on_first(&first
);
300 printf("\t * Version: %d\n", version
);
306 tab_on_first(&first
);
307 printf("\t * Group cipher: %s\n", defcipher
);
308 printf("\t * Pairwise ciphers: %s\n", defcipher
);
312 tab_on_first(&first
);
313 printf("\t * Group cipher: ");
321 tab_on_first(&first
);
322 printf("\t * Pairwise ciphers: %s\n", defcipher
);
326 count
= data
[0] | (data
[1] << 8);
327 if (2 + (count
* 4) > len
)
330 tab_on_first(&first
);
331 printf("\t * Pairwise ciphers:");
332 for (i
= 0; i
< count
; i
++) {
334 print_cipher(data
+ 2 + (i
* 4));
338 data
+= 2 + (count
* 4);
339 len
-= 2 + (count
* 4);
342 tab_on_first(&first
);
343 printf("\t * Authentication suites: %s\n", defauth
);
347 count
= data
[0] | (data
[1] << 8);
348 if (2 + (count
* 4) > len
)
351 tab_on_first(&first
);
352 printf("\t * Authentication suites:");
353 for (i
= 0; i
< count
; i
++) {
355 print_auth(data
+ 2 + (i
* 4));
359 data
+= 2 + (count
* 4);
360 len
-= 2 + (count
* 4);
363 capa
= data
[0] | (data
[1] << 8);
364 tab_on_first(&first
);
365 printf("\t * Capabilities:");
369 printf(" NoPairwise");
370 switch ((capa
& 0x000c) >> 2) {
374 printf(" 2-PTKSA-RC");
377 printf(" 4-PTKSA-RC");
380 printf(" 16-PTKSA-RC");
383 switch ((capa
& 0x0030) >> 4) {
387 printf(" 2-GTKSA-RC");
390 printf(" 4-GTKSA-RC");
393 printf(" 16-GTKSA-RC");
397 printf(" MFP-required");
399 printf(" MFP-capable");
401 printf(" Peerkey-enabled");
403 printf(" SPP-AMSDU-capable");
405 printf(" SPP-AMSDU-required");
406 printf(" (0x%.4x)\n", capa
);
413 printf("\t\t * bogus tail data (%d):", len
);
415 printf(" %.2x", *data
);
423 static void print_rsn(const uint8_t type
, uint8_t len
, const uint8_t *data
)
425 print_rsn_ie("CCMP", "IEEE 802.1X", len
, data
);
428 static void print_capabilities(const uint8_t type
, uint8_t len
, const uint8_t *data
)
434 for (i
= 0; i
< len
; i
++) {
437 for (bit
= 0; bit
< 8; bit
++) {
438 if (!(data
[i
] & (1 << bit
)))
446 switch (bit
+ base
) {
448 printf(" HT Information Exchange Supported");
451 printf(" On-demand Beacon");
454 printf(" Extended Channel Switching");
457 printf(" Wave Indication");
460 printf(" PSMP Capability");
463 printf(" Service Interval Granularity");
466 printf(" S-PSMP Capability");
480 void (*print
)(const uint8_t type
, uint8_t len
, const uint8_t *data
);
481 uint8_t minlen
, maxlen
;
485 static void print_ie(const struct ie_print
*p
, const uint8_t type
,
486 uint8_t len
, const uint8_t *data
)
493 printf("\t%s:", p
->name
);
494 if (len
< p
->minlen
|| len
> p
->maxlen
) {
496 printf(" <invalid: %d bytes:", len
);
497 for (i
= 0; i
< len
; i
++)
498 printf(" %.02x", data
[i
]);
501 printf(" <invalid: 1 byte: %.02x>\n", data
[0]);
503 printf(" <invalid: no data>\n");
507 p
->print(type
, len
, data
);
510 #define PRINT_IGN { \
517 static const struct ie_print ieprinters
[] = {
518 [0] = { "SSID", print_ssid
, 0, 32, BIT(PRINT_SCAN
) | BIT(PRINT_LINK
), },
519 [1] = { "Supported rates", print_supprates
, 0, 255, BIT(PRINT_SCAN
), },
520 [3] = { "DS Paramater set", print_ds
, 1, 1, BIT(PRINT_SCAN
), },
522 [7] = { "Country", print_country
, 3, 255, BIT(PRINT_SCAN
), },
523 [32] = { "Power constraint", print_powerconstraint
, 1, 1, BIT(PRINT_SCAN
), },
524 [42] = { "ERP", print_erp
, 1, 255, BIT(PRINT_SCAN
), },
525 [48] = { "RSN", print_rsn
, 2, 255, BIT(PRINT_SCAN
), },
526 [50] = { "Extended supported rates", print_supprates
, 0, 255, BIT(PRINT_SCAN
), },
527 [127] = { "Extended capabilities", print_capabilities
, 0, 255, BIT(PRINT_SCAN
), },
530 static void print_wifi_wpa(const uint8_t type
, uint8_t len
, const uint8_t *data
)
532 print_rsn_ie("TKIP", "IEEE 802.1X", len
, data
);
535 static bool print_wifi_wmm_param(const uint8_t *data
, uint8_t len
)
538 static const char *aci_tbl
[] = { "BE", "BK", "VI", "VO" };
544 printf("not version 1: ");
548 printf("\t* Version 1");
553 printf("\n\t\t* u-APSD");
557 for (i
= 0; i
< 4; i
++) {
558 printf("\n\t\t* %s:", aci_tbl
[(data
[0] >> 5) & 3]);
561 printf(" CW %d-%d", (1 << (data
[1] & 0xf)) - 1,
562 (1 << (data
[1] >> 4)) - 1);
563 if (data
[2] | data
[3])
564 printf(", TXOP %d", (data
[2] + (data
[3] << 8)) * 32);
576 static void print_wifi_wmm(const uint8_t type
, uint8_t len
, const uint8_t *data
)
582 printf(" information:");
585 if (print_wifi_wmm_param(data
+ 1, len
- 1))
589 printf(" type %d:", data
[0]);
593 for(i
= 1; i
< len
; i
++)
594 printf(" %.02x", data
[i
]);
598 static void print_wifi_wps(const uint8_t type
, uint8_t len
, const uint8_t *data
)
601 __u16 subtype
, sublen
;
604 subtype
= (data
[0] << 8) + data
[1];
605 sublen
= (data
[2] << 8) + data
[3];
611 tab_on_first(&first
);
612 printf("\t * Version: %d.%d\n", data
[4] >> 4, data
[4] & 0xF);
615 tab_on_first(&first
);
616 printf("\t * Device name: %.*s\n", sublen
, data
+ 4);
619 tab_on_first(&first
);
620 printf("\t * Manufacturer: %.*s\n", sublen
, data
+ 4);
623 tab_on_first(&first
);
624 printf("\t * Model: %.*s\n", sublen
, data
+ 4);
628 tab_on_first(&first
);
629 printf("\t * AP setup locked: 0x%.2x\n", val
);
633 __u16 meth
= (data
[4] << 8) + data
[5];
635 tab_on_first(&first
);
636 printf("\t * Config methods:");
637 #define T(bit, name) do { \
638 if (meth & (1<<bit)) { \
666 printf("\t\t * bogus tail data (%d):", len
);
668 printf(" %.2x", *data
);
676 static const struct ie_print wifiprinters
[] = {
677 [1] = { "WPA", print_wifi_wpa
, 2, 255, BIT(PRINT_SCAN
), },
678 [2] = { "WMM", print_wifi_wmm
, 1, 255, BIT(PRINT_SCAN
), },
679 [4] = { "WPS", print_wifi_wps
, 0, 255, BIT(PRINT_SCAN
), },
682 static void print_vendor(unsigned char len
, unsigned char *data
,
683 bool unknown
, enum print_ie_type ptype
)
688 printf("\tVendor specific: <too short> data:");
689 for(i
= 0; i
< len
; i
++)
690 printf(" %.02x", data
[i
]);
695 if (len
>= 4 && memcmp(data
, wifi_oui
, 3) == 0) {
696 if (data
[3] < ARRAY_SIZE(wifiprinters
) &&
697 wifiprinters
[data
[3]].name
&&
698 wifiprinters
[data
[3]].flags
& BIT(ptype
)) {
699 print_ie(&wifiprinters
[data
[3]], data
[3], len
- 4, data
+ 4);
704 printf("\tWiFi OUI %#.2x, data:", data
[3]);
705 for(i
= 0; i
< len
- 4; i
++)
706 printf(" %.02x", data
[i
+ 4]);
714 printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:",
715 data
[0], data
[1], data
[2]);
716 for (i
= 3; i
< len
; i
++)
717 printf(" %.2x", data
[i
]);
721 void print_ies(unsigned char *ie
, int ielen
, bool unknown
,
722 enum print_ie_type ptype
)
724 while (ielen
>= 2 && ielen
>= ie
[1]) {
725 if (ie
[0] < ARRAY_SIZE(ieprinters
) &&
726 ieprinters
[ie
[0]].name
&&
727 ieprinters
[ie
[0]].flags
& BIT(ptype
)) {
728 print_ie(&ieprinters
[ie
[0]], ie
[0], ie
[1], ie
+ 2);
729 } else if (ie
[0] == 221 /* vendor */) {
730 print_vendor(ie
[1], ie
+ 2, unknown
, ptype
);
731 } else if (unknown
) {
734 printf("\tUnknown IE (%d):", ie
[0]);
735 for (i
=0; i
<ie
[1]; i
++)
736 printf(" %.2x", ie
[2+i
]);
744 static int print_bss_handler(struct nl_msg
*msg
, void *arg
)
746 struct nlattr
*tb
[NL80211_ATTR_MAX
+ 1];
747 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
748 struct nlattr
*bss
[NL80211_BSS_MAX
+ 1];
749 char mac_addr
[20], dev
[20];
750 static struct nla_policy bss_policy
[NL80211_BSS_MAX
+ 1] = {
751 [NL80211_BSS_TSF
] = { .type
= NLA_U64
},
752 [NL80211_BSS_FREQUENCY
] = { .type
= NLA_U32
},
753 [NL80211_BSS_BSSID
] = { },
754 [NL80211_BSS_BEACON_INTERVAL
] = { .type
= NLA_U16
},
755 [NL80211_BSS_CAPABILITY
] = { .type
= NLA_U16
},
756 [NL80211_BSS_INFORMATION_ELEMENTS
] = { },
757 [NL80211_BSS_SIGNAL_MBM
] = { .type
= NLA_U32
},
758 [NL80211_BSS_SIGNAL_UNSPEC
] = { .type
= NLA_U8
},
759 [NL80211_BSS_STATUS
] = { .type
= NLA_U32
},
761 struct scan_params
*params
= arg
;
763 nla_parse(tb
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
764 genlmsg_attrlen(gnlh
, 0), NULL
);
766 if (!tb
[NL80211_ATTR_BSS
]) {
767 fprintf(stderr
, "bss info missing!");
770 if (nla_parse_nested(bss
, NL80211_BSS_MAX
,
771 tb
[NL80211_ATTR_BSS
],
773 fprintf(stderr
, "failed to parse nested attributes!");
777 if (!bss
[NL80211_BSS_BSSID
])
780 mac_addr_n2a(mac_addr
, nla_data(bss
[NL80211_BSS_BSSID
]));
781 if_indextoname(nla_get_u32(tb
[NL80211_ATTR_IFINDEX
]), dev
);
782 printf("BSS %s (on %s)", mac_addr
, dev
);
784 if (bss
[NL80211_BSS_STATUS
]) {
785 switch (nla_get_u32(bss
[NL80211_BSS_STATUS
])) {
786 case NL80211_BSS_STATUS_AUTHENTICATED
:
787 printf(" -- authenticated");
789 case NL80211_BSS_STATUS_ASSOCIATED
:
790 printf(" -- associated");
792 case NL80211_BSS_STATUS_IBSS_JOINED
:
793 printf(" -- joined");
796 printf(" -- unknown status: %d",
797 nla_get_u32(bss
[NL80211_BSS_STATUS
]));
803 if (bss
[NL80211_BSS_TSF
]) {
804 unsigned long long tsf
;
805 tsf
= (unsigned long long)nla_get_u64(bss
[NL80211_BSS_TSF
]);
806 printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
807 tsf
, tsf
/1000/1000/60/60/24, (tsf
/1000/1000/60/60) % 24,
808 (tsf
/1000/1000/60) % 60, (tsf
/1000/1000) % 60);
810 if (bss
[NL80211_BSS_FREQUENCY
])
811 printf("\tfreq: %d\n",
812 nla_get_u32(bss
[NL80211_BSS_FREQUENCY
]));
813 if (bss
[NL80211_BSS_BEACON_INTERVAL
])
814 printf("\tbeacon interval: %d\n",
815 nla_get_u16(bss
[NL80211_BSS_BEACON_INTERVAL
]));
816 if (bss
[NL80211_BSS_CAPABILITY
]) {
817 __u16 capa
= nla_get_u16(bss
[NL80211_BSS_CAPABILITY
]);
818 printf("\tcapability:");
819 if (capa
& WLAN_CAPABILITY_ESS
)
821 if (capa
& WLAN_CAPABILITY_IBSS
)
823 if (capa
& WLAN_CAPABILITY_PRIVACY
)
825 if (capa
& WLAN_CAPABILITY_SHORT_PREAMBLE
)
826 printf(" ShortPreamble");
827 if (capa
& WLAN_CAPABILITY_PBCC
)
829 if (capa
& WLAN_CAPABILITY_CHANNEL_AGILITY
)
830 printf(" ChannelAgility");
831 if (capa
& WLAN_CAPABILITY_SPECTRUM_MGMT
)
832 printf(" SpectrumMgmt");
833 if (capa
& WLAN_CAPABILITY_QOS
)
835 if (capa
& WLAN_CAPABILITY_SHORT_SLOT_TIME
)
836 printf(" ShortSlotTime");
837 if (capa
& WLAN_CAPABILITY_APSD
)
839 if (capa
& WLAN_CAPABILITY_DSSS_OFDM
)
840 printf(" DSSS-OFDM");
841 printf(" (0x%.4x)\n", capa
);
843 if (bss
[NL80211_BSS_SIGNAL_MBM
]) {
844 int s
= nla_get_u32(bss
[NL80211_BSS_SIGNAL_MBM
]);
845 printf("\tsignal: %d.%.2d dBm\n", s
/100, s
%100);
847 if (bss
[NL80211_BSS_SIGNAL_UNSPEC
]) {
848 unsigned char s
= nla_get_u8(bss
[NL80211_BSS_SIGNAL_UNSPEC
]);
849 printf("\tsignal: %d/100\n", s
);
851 if (bss
[NL80211_BSS_INFORMATION_ELEMENTS
])
852 print_ies(nla_data(bss
[NL80211_BSS_INFORMATION_ELEMENTS
]),
853 nla_len(bss
[NL80211_BSS_INFORMATION_ELEMENTS
]),
854 params
->unknown
, params
->type
);
859 static struct scan_params scan_params
;
861 static int handle_scan_dump(struct nl80211_state
*state
,
864 int argc
, char **argv
)
869 scan_params
.unknown
= false;
870 if (argc
== 1 && !strcmp(argv
[0], "-u"))
871 scan_params
.unknown
= true;
873 scan_params
.type
= PRINT_SCAN
;
875 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, print_bss_handler
,
879 COMMAND(scan
, dump
, "[-u]",
880 NL80211_CMD_GET_SCAN
, NLM_F_DUMP
, CIB_NETDEV
, handle_scan_dump
,
881 "Dump the current scan results. If -u is specified, print unknown\n"
882 "data in scan results.");
884 static int handle_scan_combined(struct nl80211_state
*state
,
887 int argc
, char **argv
)
890 static char *dump_argv
[] = {
896 static const __u32 cmds
[] = {
897 NL80211_CMD_NEW_SCAN_RESULTS
,
898 NL80211_CMD_SCAN_ABORTED
,
900 int trig_argc
, dump_argc
, err
;
902 if (argc
>= 3 && !strcmp(argv
[2], "-u")) {
908 trig_argc
= 3 + (argc
- 2) + (3 - dump_argc
);
909 trig_argv
= calloc(trig_argc
, sizeof(*trig_argv
));
912 trig_argv
[0] = argv
[0];
913 trig_argv
[1] = "scan";
914 trig_argv
[2] = "trigger";
916 for (i
= 0; i
< argc
- 2 - (dump_argc
- 3); i
++)
917 trig_argv
[i
+ 3] = argv
[i
+ 2 + (dump_argc
- 3)];
918 err
= handle_cmd(state
, II_NETDEV
, trig_argc
, trig_argv
);
924 * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
926 * This code has a bug, which requires creating a separate
927 * nl80211 socket to fix:
928 * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or
929 * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel
930 * before (!) we listen to it, because we only start listening
931 * after we send our scan request.
933 * Doing it the other way around has a race condition as well,
934 * if you first open the events socket you may get a notification
935 * for a previous scan.
937 * The only proper way to fix this would be to listen to events
938 * before sending the command, and for the kernel to send the
939 * scan request along with the event, so that you can match up
940 * whether the scan you requested was finished or aborted (this
941 * may result in processing a scan that another application
942 * requested, but that doesn't seem to be a problem).
944 * Alas, the kernel doesn't do that (yet).
947 if (listen_events(state
, ARRAY_SIZE(cmds
), cmds
) ==
948 NL80211_CMD_SCAN_ABORTED
) {
949 printf("scan aborted!\n");
953 dump_argv
[0] = argv
[0];
954 return handle_cmd(state
, II_NETDEV
, dump_argc
, dump_argv
);
956 TOPLEVEL(scan
, "[-u] [freq <freq>*] [ssid <ssid>*|passive]", 0, 0,
957 CIB_NETDEV
, handle_scan_combined
,
958 "Scan on the given frequencies and probe for the given SSIDs\n"
959 "(or wildcard if not given) unless passive scanning is requested.\n"
960 "If -u is specified print unknown data in the scan results.");