X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scan.c;h=bb84a6776d055ca392e0650e31733820be572e19;hb=d8880179da0a04fcbe6759497be5a3513582cb9f;hp=218d853727a523f0e43b85462d8bac7e61cc4603;hpb=9068e4da4129c08a09a0ce421bd7e24f644ebb0b;p=thirdparty%2Fiw.git diff --git a/scan.c b/scan.c index 218d853..bb84a67 100644 --- a/scan.c +++ b/scan.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -12,99 +13,1096 @@ #include "nl80211.h" #include "iw.h" -static int handle_scan(struct nl_cb *cb, +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) +#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_QOS (1<<9) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_APSD (1<<11) +#define WLAN_CAPABILITY_DSSS_OFDM (1<<13) + +static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 }; +static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac }; +static unsigned char wfa_oui[3] = { 0x50, 0x6f, 0x9a }; + +struct scan_params { + bool unknown; + enum print_ie_type type; + bool show_both_ie_sets; +}; + +#define IEEE80211_COUNTRY_EXTENSION_ID 201 + +union ieee80211_country_ie_triplet { + struct { + __u8 first_channel; + __u8 num_channels; + __s8 max_power; + } __attribute__ ((packed)) chans; + struct { + __u8 reg_extension_id; + __u8 reg_class; + __u8 coverage_class; + } __attribute__ ((packed)) ext; +} __attribute__ ((packed)); + +static int handle_scan(struct nl80211_state *state, + struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { - struct nl_msg *ssids = NULL; + struct nl_msg *ssids = NULL, *freqs = NULL; + char *eptr; int err = -ENOBUFS; + int i; + enum { + NONE, + FREQ, + IES, + SSID, + DONE, + } parse = NONE; + int freq; + bool passive = false, have_ssids = false, have_freqs = false; + size_t tmp; + unsigned char *ies; ssids = nlmsg_alloc(); if (!ssids) return -ENOMEM; - NLA_PUT(ssids, 1, 0, ""); - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + freqs = nlmsg_alloc(); + if (!freqs) { + nlmsg_free(ssids); + return -ENOMEM; + } + + for (i = 0; i < argc; i++) { + switch (parse) { + case NONE: + if (strcmp(argv[i], "freq") == 0) { + parse = FREQ; + have_freqs = true; + break; + } else if (strcmp(argv[i], "ies") == 0) { + parse = IES; + break; + } else if (strcmp(argv[i], "ssid") == 0) { + parse = SSID; + have_ssids = true; + break; + } else if (strcmp(argv[i], "passive") == 0) { + parse = DONE; + passive = true; + break; + } + case DONE: + return 1; + case FREQ: + freq = strtoul(argv[i], &eptr, 10); + if (eptr != argv[i] + strlen(argv[i])) { + /* failed to parse as number -- maybe a tag? */ + i--; + parse = NONE; + continue; + } + NLA_PUT_U32(freqs, i, freq); + break; + case IES: + ies = parse_hex(argv[i], &tmp); + if (!ies) + goto nla_put_failure; + NLA_PUT(msg, NL80211_ATTR_IE, tmp, ies); + free(ies); + parse = NONE; + break; + case SSID: + NLA_PUT(ssids, i, strlen(argv[i]), argv[i]); + break; + } + } + + if (!have_ssids) + NLA_PUT(ssids, 1, 0, ""); + if (!passive) + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + if (have_freqs) + nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); err = 0; nla_put_failure: nlmsg_free(ssids); + nlmsg_free(freqs); return err; } -COMMAND(scan, trigger, NULL, - NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan); -typedef void (*printfn)(unsigned char type, unsigned char len, unsigned char *data); +static void tab_on_first(bool *first) +{ + if (!*first) + printf("\t"); + else + *first = false; +} + +static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data) +{ + printf(" "); + print_ssid_escaped(len, data); + printf("\n"); +} + +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 -static void print_ssid(unsigned char type, unsigned char len, unsigned char *data) +static void print_supprates(const uint8_t type, uint8_t len, const uint8_t *data) { int i; - printf("\tSSID: "); - for (i=0; i= 3) { + int end_channel; + union ieee80211_country_ie_triplet *triplet = (void *) data; + + if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) { + printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n", + triplet->ext.reg_extension_id, + triplet->ext.reg_class, + triplet->ext.coverage_class, + triplet->ext.coverage_class * 450); + + data += 3; + len -= 3; + continue; + } + + /* 2 GHz */ + if (triplet->chans.first_channel <= 14) + end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1); + else + end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1)); + + printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power); + + data += 3; + len -= 3; + } + + return; +} + +static void print_powerconstraint(const uint8_t type, uint8_t len, const uint8_t *data) +{ + printf(" %d dB\n", data[0]); +} + +static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data) +{ + if (data[0] == 0x00) + printf(" "); + if (data[0] & 0x01) + printf(" NonERP_Present"); + if (data[0] & 0x02) + printf(" Use_Protection"); + if (data[0] & 0x04) + printf(" Barker_Preamble_Mode"); + printf("\n"); +} + +static void print_cipher(const uint8_t *data) +{ + if (memcmp(data, ms_oui, 3) == 0) { + switch (data[3]) { + case 0: + printf("Use group cipher suite"); + break; + case 1: + printf("WEP-40"); + break; + case 2: + printf("TKIP"); + break; + case 4: + printf("CCMP"); + break; + case 5: + printf("WEP-104"); + break; + default: + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); + break; + } + } else if (memcmp(data, ieee80211_oui, 3) == 0) { + switch (data[3]) { + case 0: + printf("Use group cipher suite"); + break; + case 1: + printf("WEP-40"); + break; + case 2: + printf("TKIP"); + break; + case 4: + printf("CCMP"); + break; + case 5: + printf("WEP-104"); + break; + case 6: + printf("AES-128-CMAC"); + break; + case 8: + printf("GCMP"); + break; + default: + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); + break; + } + } else + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); +} + +static void print_auth(const uint8_t *data) +{ + if (memcmp(data, ms_oui, 3) == 0) { + switch (data[3]) { + case 1: + printf("IEEE 802.1X"); + break; + case 2: + printf("PSK"); + break; + default: + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); + break; + } + } else if (memcmp(data, ieee80211_oui, 3) == 0) { + switch (data[3]) { + case 1: + printf("IEEE 802.1X"); + break; + case 2: + printf("PSK"); + break; + case 3: + printf("FT/IEEE 802.1X"); + break; + case 4: + printf("FT/PSK"); + break; + case 5: + printf("IEEE 802.1X/SHA-256"); + break; + case 6: + printf("PSK/SHA-256"); + break; + default: + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); + break; + } + } else + printf("%.02x-%.02x-%.02x:%d", + data[0], data[1] ,data[2], data[3]); +} + +static void print_rsn_ie(const char *defcipher, const char *defauth, + uint8_t len, const uint8_t *data) { + bool first = true; + __u16 version, count, capa; int i; - if (type == 1) - printf("\tSupported rates: "); - else - printf("\tExtended supported rates: "); + version = data[0] + (data[1] << 8); + tab_on_first(&first); + printf("\t * Version: %d\n", version); - for (i=0; i len) + goto invalid; + + tab_on_first(&first); + printf("\t * Pairwise ciphers:"); + for (i = 0; i < count; i++) { + printf(" "); + print_cipher(data + 2 + (i * 4)); + } + printf("\n"); + + data += 2 + (count * 4); + len -= 2 + (count * 4); + + if (len < 2) { + tab_on_first(&first); + printf("\t * Authentication suites: %s\n", defauth); + return; + } + + count = data[0] | (data[1] << 8); + if (2 + (count * 4) > len) + goto invalid; + + tab_on_first(&first); + printf("\t * Authentication suites:"); + for (i = 0; i < count; i++) { + printf(" "); + print_auth(data + 2 + (i * 4)); + } + printf("\n"); + + data += 2 + (count * 4); + len -= 2 + (count * 4); + + if (len >= 2) { + capa = data[0] | (data[1] << 8); + tab_on_first(&first); + printf("\t * Capabilities:"); + if (capa & 0x0001) + printf(" PreAuth"); + if (capa & 0x0002) + printf(" NoPairwise"); + switch ((capa & 0x000c) >> 2) { + case 0: + break; + case 1: + printf(" 2-PTKSA-RC"); + break; + case 2: + printf(" 4-PTKSA-RC"); + break; + case 3: + printf(" 16-PTKSA-RC"); + break; + } + switch ((capa & 0x0030) >> 4) { + case 0: + break; + case 1: + printf(" 2-GTKSA-RC"); + break; + case 2: + printf(" 4-GTKSA-RC"); + break; + case 3: + printf(" 16-GTKSA-RC"); + break; + } + if (capa & 0x0040) + printf(" MFP-required"); + if (capa & 0x0080) + printf(" MFP-capable"); + if (capa & 0x0200) + printf(" Peerkey-enabled"); + if (capa & 0x0400) + printf(" SPP-AMSDU-capable"); + if (capa & 0x0800) + printf(" SPP-AMSDU-required"); + printf(" (0x%.4x)\n", capa); + data += 2; + len -= 2; + } + + if (len >= 2) { + int pmkid_count = data[0] | (data[1] << 8); + + if (len >= 2 + 16 * pmkid_count) { + tab_on_first(&first); + printf("\t * %d PMKIDs\n", pmkid_count); + /* not printing PMKID values */ + data += 2 + 16 * pmkid_count; + len -= 2 + 16 * pmkid_count; + } else + goto invalid; + } + + if (len >= 4) { + tab_on_first(&first); + printf("\t * Group mgmt cipher suite: "); + print_cipher(data); + printf("\n"); + data += 4; + len -= 4; + } + + invalid: + if (len != 0) { + printf("\t\t * bogus tail data (%d):", len); + while (len) { + printf(" %.2x", *data); + data++; + len--; + } + printf("\n"); + } +} + +static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data) +{ + print_rsn_ie("CCMP", "IEEE 802.1X", len, data); +} + +static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data) +{ + printf("\n"); + print_ht_capability(data[0] | (data[1] << 8)); + print_ampdu_length(data[2] & 3); + print_ampdu_spacing((data[2] >> 2) & 7); + print_ht_mcs(data + 3); +} + +static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data) +{ + static const char *offset[4] = { + "no secondary", + "above", + "[reserved!]", + "below", + }; + static const char *protection[4] = { + "no", + "nonmember", + "20 MHz", + "non-HT mixed", + }; + static const char *sta_chan_width[2] = { + "20 MHz", + "any", + }; + + printf("\n"); + printf("\t\t * primary channel: %d\n", data[0]); + printf("\t\t * secondary channel offset: %s\n", + offset[data[1] & 0x3]); + printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]); + printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3); + printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]); + printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2); + printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4); + printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6); + printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7); + printf("\t\t * STBC beacon: %d\n", data[5] & 0x1); + printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1); + printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2); + printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3); +} + +static void print_capabilities(const uint8_t type, uint8_t len, const uint8_t *data) +{ + int i, base, bit; + bool first = true; + + + for (i = 0; i < len; i++) { + base = i * 8; + + for (bit = 0; bit < 8; bit++) { + if (!(data[i] & (1 << bit))) + continue; + + if (!first) + printf(","); + else + first = false; + + switch (bit + base) { + case 0: + printf(" HT Information Exchange Supported"); + break; + case 1: + printf(" On-demand Beacon"); + break; + case 2: + printf(" Extended Channel Switching"); + break; + case 3: + printf(" Wave Indication"); + break; + case 4: + printf(" PSMP Capability"); + break; + case 5: + printf(" Service Interval Granularity"); + break; + case 6: + printf(" S-PSMP Capability"); + break; + default: + printf(" %d", bit); + break; + } + } } + + printf("\n"); +} + +static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data) +{ + printf(" DTIM Count %u DTIM Period %u Bitmap Control 0x%x " + "Bitmap[0] 0x%x", + data[0], data[1], data[2], data[3]); + if (len - 4) + printf(" (+ %u octet%s)", len - 4, len - 4 == 1 ? "" : "s"); printf("\n"); } -static void print_ds(unsigned char type, unsigned char len, unsigned char *data) +struct ie_print { + const char *name; + void (*print)(const uint8_t type, uint8_t len, const uint8_t *data); + uint8_t minlen, maxlen; + uint8_t flags; +}; + +static void print_ie(const struct ie_print *p, const uint8_t type, + uint8_t len, const uint8_t *data) { - printf("\tDS Parameter set: channel %d\n", data[0]); + int i; + + if (!p->print) + return; + + printf("\t%s:", p->name); + if (len < p->minlen || len > p->maxlen) { + if (len > 1) { + printf(" \n"); + } else if (len) + printf(" \n", data[0]); + else + printf(" \n"); + return; + } + + p->print(type, len, data); +} + +#define PRINT_IGN { \ + .name = "IGNORE", \ + .print = NULL, \ + .minlen = 0, \ + .maxlen = 255, \ } -static void print_ign(unsigned char type, unsigned char len, unsigned char *data) +static const struct ie_print ieprinters[] = { + [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), }, + [1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), }, + [3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), }, + [5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), }, + [7] = { "Country", print_country, 3, 255, BIT(PRINT_SCAN), }, + [32] = { "Power constraint", print_powerconstraint, 1, 1, BIT(PRINT_SCAN), }, + [42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), }, + [45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), }, + [61] = { "HT operation", print_ht_op, 22, 22, BIT(PRINT_SCAN), }, + [48] = { "RSN", print_rsn, 2, 255, BIT(PRINT_SCAN), }, + [50] = { "Extended supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), }, + [114] = { "MESH ID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), }, + [127] = { "Extended capabilities", print_capabilities, 0, 255, BIT(PRINT_SCAN), }, +}; + +static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data) { - /* ignore for now, not too useful */ + print_rsn_ie("TKIP", "IEEE 802.1X", len, data); } -static void print_vendor(unsigned char type, unsigned char len, unsigned char *data) +static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len) { int i; + static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" }; + + if (len < 19) + goto invalid; + + if (data[0] != 1) { + printf("Parameter: not version 1: "); + return false; + } + + printf("\t * Parameter version 1"); + + data++; + + if (data[0] & 0x80) + printf("\n\t\t * u-APSD"); + + data += 2; + + for (i = 0; i < 4; i++) { + printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]); + if (data[0] & 0x10) + printf(" acm"); + printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1, + (1 << (data[1] >> 4)) - 1); + printf(", AIFSN %d", data[0] & 0xf); + if (data[2] | data[3]) + printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32); + data += 4; + } - printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data: ", - data[0], data[1], data[2]); - for (i=3; i= 4) { + subtype = (data[0] << 8) + data[1]; + sublen = (data[2] << 8) + data[3]; + if (sublen > len) + break; + + switch (subtype) { + case 0x104a: + tab_on_first(&first); + printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF); + break; + case 0x1011: + tab_on_first(&first); + printf("\t * Device name: %.*s\n", sublen, data + 4); + break; + case 0x1012: { + uint16_t id; + tab_on_first(&first); + if (sublen != 2) { + printf("\t * Device Password ID: (invalid " + "length %d)\n", sublen); + break; + } + id = data[4] << 8 | data[5]; + printf("\t * Device Password ID: %u (%s)\n", + id, wifi_wps_dev_passwd_id(id)); + break; + } + case 0x1021: + tab_on_first(&first); + printf("\t * Manufacturer: %.*s\n", sublen, data + 4); + break; + case 0x1023: + tab_on_first(&first); + printf("\t * Model: %.*s\n", sublen, data + 4); + break; + case 0x1024: + tab_on_first(&first); + printf("\t * Model Number: %.*s\n", sublen, data + 4); + break; + case 0x103b: { + __u8 val = data[4]; + tab_on_first(&first); + printf("\t * Response Type: %d%s\n", + val, val == 3 ? " (AP)" : ""); + break; + } + case 0x103c: { + __u8 val = data[4]; + tab_on_first(&first); + printf("\t * RF Bands: 0x%x\n", val); + break; + } + case 0x1041: { + __u8 val = data[4]; + tab_on_first(&first); + printf("\t * Selected Registrar: 0x%x\n", val); + break; + } + case 0x1042: + tab_on_first(&first); + printf("\t * Serial Number: %.*s\n", sublen, data + 4); + break; + case 0x1044: { + __u8 val = data[4]; + tab_on_first(&first); + printf("\t * Wi-Fi Protected Setup State: %d%s%s\n", + val, + val == 1 ? " (Unconfigured)" : "", + val == 2 ? " (Configured)" : ""); + break; + } + case 0x1047: + tab_on_first(&first); + printf("\t * UUID: "); + if (sublen != 16) { + printf("(invalid, length=%d)\n", sublen); + break; + } + printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x\n", + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15], + data[16], data[17], data[18], data[19]); + break; + case 0x1054: { + tab_on_first(&first); + if (sublen != 8) { + printf("\t * Primary Device Type: (invalid " + "length %d)\n", sublen); + break; + } + printf("\t * Primary Device Type: " + "%u-%02x%02x%02x%02x-%u\n", + data[4] << 8 | data[5], + data[6], data[7], data[8], data[9], + data[10] << 8 | data[11]); + break; + } + case 0x1057: { + __u8 val = data[4]; + tab_on_first(&first); + printf("\t * AP setup locked: 0x%.2x\n", val); + break; + } + case 0x1008: + case 0x1053: { + __u16 meth = (data[4] << 8) + data[5]; + bool comma = false; + tab_on_first(&first); + printf("\t * %sConfig methods:", + subtype == 0x1053 ? "Selected Registrar ": ""); +#define T(bit, name) do { \ + if (meth & (1<= 3) { + subtype = data[0]; + sublen = (data[2] << 8) + data[1]; + + if (sublen > len - 3) + break; + + switch (subtype) { + case 0x02: /* capability */ + tab_on_first(&first); + if (sublen < 2) { + printf("\t * malformed capability\n"); + break; + } + printf("\t * Group capa: 0x%.2x, Device capa: 0x%.2x\n", + data[3], data[4]); + break; + case 0x0d: /* device info */ + if (sublen < 6 + 2 + 8 + 1) { + printf("\t * malformed device info\n"); + break; + } + /* fall through for now */ + case 0x00: /* status */ + case 0x01: /* minor reason */ + case 0x03: /* device ID */ + case 0x04: /* GO intent */ + case 0x05: /* configuration timeout */ + case 0x06: /* listen channel */ + case 0x07: /* group BSSID */ + case 0x08: /* ext listen timing */ + case 0x09: /* intended interface address */ + case 0x0a: /* manageability */ + case 0x0b: /* channel list */ + case 0x0c: /* NoA */ + case 0x0e: /* group info */ + case 0x0f: /* group ID */ + case 0x10: /* interface */ + case 0x11: /* operating channel */ + case 0x12: /* invitation flags */ + case 0xdd: /* vendor specific */ + default: { + const __u8 *subdata = data + 4; + __u16 tmplen = sublen; + + tab_on_first(&first); + printf("\t * Unknown TLV (%#.2x, %d bytes):", + subtype, tmplen); + while (tmplen) { + printf(" %.2x", *subdata); + subdata++; + tmplen--; + } + printf("\n"); + break; + } + } + + data += sublen + 3; + len -= sublen + 3; + } + + if (len != 0) { + tab_on_first(&first); + printf("\t * bogus tail data (%d):", len); + while (len) { + printf(" %.2x", *data); + data++; + len--; + } + printf("\n"); + } +} + +static const struct ie_print wfa_printers[] = { + [9] = { "P2P", print_p2p, 2, 255, BIT(PRINT_SCAN), }, +}; + +static void print_vendor(unsigned char len, unsigned char *data, + bool unknown, enum print_ie_type ptype) +{ + int i; + + if (len < 3) { + printf("\tVendor specific: data:"); + for(i = 0; i < len; i++) + printf(" %.02x", data[i]); + printf("\n"); + return; + } + + if (len >= 4 && memcmp(data, ms_oui, 3) == 0) { + if (data[3] < ARRAY_SIZE(wifiprinters) && + wifiprinters[data[3]].name && + wifiprinters[data[3]].flags & BIT(ptype)) { + print_ie(&wifiprinters[data[3]], data[3], len - 4, data + 4); + return; + } + if (!unknown) + return; + printf("\tMS/WiFi %#.2x, data:", data[3]); + for(i = 0; i < len - 4; i++) + printf(" %.02x", data[i + 4]); + printf("\n"); + return; + } + + if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) { + if (data[3] < ARRAY_SIZE(wfa_printers) && + wfa_printers[data[3]].name && + wfa_printers[data[3]].flags & BIT(ptype)) { + print_ie(&wfa_printers[data[3]], data[3], len - 4, data + 4); + return; + } + if (!unknown) + return; + printf("\tWFA %#.2x, data:", data[3]); + for(i = 0; i < len - 4; i++) + printf(" %.02x", data[i + 4]); + printf("\n"); + return; + } + + if (!unknown) + return; + + printf("\tVendor specific: OUI %.2x:%.2x:%.2x, data:", + data[0], data[1], data[2]); + for (i = 3; i < len; i++) + printf(" %.2x", data[i]); + printf("\n"); +} + +void print_ies(unsigned char *ie, int ielen, bool unknown, + enum print_ie_type ptype) { while (ielen >= 2 && ielen >= ie[1]) { - if (ie[0] < ARRAY_SIZE(ieprinters) && ieprinters[ie[0]]) { - ieprinters[ie[0]](ie[0], ie[1], ie + 2); - } else { + if (ie[0] < ARRAY_SIZE(ieprinters) && + ieprinters[ie[0]].name && + ieprinters[ie[0]].flags & BIT(ptype)) { + print_ie(&ieprinters[ie[0]], ie[0], ie[1], ie + 2); + } else if (ie[0] == 221 /* vendor */) { + print_vendor(ie[1], ie + 2, unknown, ptype); + } else if (unknown) { int i; - printf("\tUnknown IE (%d): ", ie[0]); + printf("\tUnknown IE (%d):", ie[0]); for (i=0; ishow_both_ie_sets ? 2 : 1; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_BSS]) { - fprintf(stderr, "bss info missing!"); + fprintf(stderr, "bss info missing!\n"); return NL_SKIP; } if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) { - fprintf(stderr, "failed to parse nested attributes!"); + fprintf(stderr, "failed to parse nested attributes!\n"); return NL_SKIP; } @@ -146,35 +1151,210 @@ static int print_bss_handler(struct nl_msg *msg, void *arg) mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID])); if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev); - printf("BSS %s (on %s)\n", mac_addr, dev); + printf("BSS %s (on %s)", mac_addr, dev); + + if (bss[NL80211_BSS_STATUS]) { + switch (nla_get_u32(bss[NL80211_BSS_STATUS])) { + case NL80211_BSS_STATUS_AUTHENTICATED: + printf(" -- authenticated"); + break; + case NL80211_BSS_STATUS_ASSOCIATED: + printf(" -- associated"); + break; + case NL80211_BSS_STATUS_IBSS_JOINED: + printf(" -- joined"); + break; + default: + printf(" -- unknown status: %d", + nla_get_u32(bss[NL80211_BSS_STATUS])); + break; + } + } + printf("\n"); - if (bss[NL80211_BSS_TSF]) - printf("\tTSF: %llu usec\n", - (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF])); + if (bss[NL80211_BSS_TSF]) { + unsigned long long tsf; + tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]); + printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n", + tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24, + (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60); + } if (bss[NL80211_BSS_FREQUENCY]) printf("\tfreq: %d\n", nla_get_u32(bss[NL80211_BSS_FREQUENCY])); if (bss[NL80211_BSS_BEACON_INTERVAL]) printf("\tbeacon interval: %d\n", nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL])); - if (bss[NL80211_BSS_CAPABILITY]) - printf("\tcapability: 0x%.4x\n", - nla_get_u16(bss[NL80211_BSS_CAPABILITY])); - if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) + if (bss[NL80211_BSS_CAPABILITY]) { + __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + printf("\tcapability:"); + if (capa & WLAN_CAPABILITY_ESS) + printf(" ESS"); + if (capa & WLAN_CAPABILITY_IBSS) + printf(" IBSS"); + if (capa & WLAN_CAPABILITY_PRIVACY) + printf(" Privacy"); + if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE) + printf(" ShortPreamble"); + if (capa & WLAN_CAPABILITY_PBCC) + printf(" PBCC"); + if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY) + printf(" ChannelAgility"); + if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT) + printf(" SpectrumMgmt"); + if (capa & WLAN_CAPABILITY_QOS) + printf(" QoS"); + if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME) + printf(" ShortSlotTime"); + if (capa & WLAN_CAPABILITY_APSD) + printf(" APSD"); + if (capa & WLAN_CAPABILITY_DSSS_OFDM) + printf(" DSSS-OFDM"); + printf(" (0x%.4x)\n", capa); + } + if (bss[NL80211_BSS_SIGNAL_MBM]) { + int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + printf("\tsignal: %d.%.2d dBm\n", s/100, s%100); + } + if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { + unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + printf("\tsignal: %d/100\n", s); + } + if (bss[NL80211_BSS_SEEN_MS_AGO]) { + int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); + printf("\tlast seen: %d ms ago\n", age); + } + + if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) { + if (bss[NL80211_BSS_BEACON_IES]) + printf("\tInformation elements from Probe Response " + "frame:\n"); print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), - nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS])); + nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), + params->unknown, params->type); + } + if (bss[NL80211_BSS_BEACON_IES] && show--) { + printf("\tInformation elements from Beacon frame:\n"); + print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]), + nla_len(bss[NL80211_BSS_BEACON_IES]), + params->unknown, params->type); + } return NL_SKIP; } +static struct scan_params scan_params; - -static int handle_scan_dump(struct nl_cb *cb, +static int handle_scan_dump(struct nl80211_state *state, + struct nl_cb *cb, struct nl_msg *msg, - int argc, char **argv) + int argc, char **argv, + enum id_input id) { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler, NULL); + if (argc > 1) + return 1; + + memset(&scan_params, 0, sizeof(scan_params)); + + if (argc == 1 && !strcmp(argv[0], "-u")) + scan_params.unknown = true; + else if (argc == 1 && !strcmp(argv[0], "-b")) + scan_params.show_both_ie_sets = true; + + scan_params.type = PRINT_SCAN; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_bss_handler, + &scan_params); return 0; } -COMMAND(scan, dump, NULL, - NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump); + +static int handle_scan_combined(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + char **trig_argv; + static char *dump_argv[] = { + NULL, + "scan", + "dump", + NULL, + }; + static const __u32 cmds[] = { + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + }; + int trig_argc, dump_argc, err; + + if (argc >= 3 && !strcmp(argv[2], "-u")) { + dump_argc = 4; + dump_argv[3] = "-u"; + } else if (argc >= 3 && !strcmp(argv[2], "-b")) { + dump_argc = 4; + dump_argv[3] = "-b"; + } else + dump_argc = 3; + + trig_argc = 3 + (argc - 2) + (3 - dump_argc); + trig_argv = calloc(trig_argc, sizeof(*trig_argv)); + if (!trig_argv) + return -ENOMEM; + trig_argv[0] = argv[0]; + trig_argv[1] = "scan"; + trig_argv[2] = "trigger"; + int i; + for (i = 0; i < argc - 2 - (dump_argc - 3); i++) + trig_argv[i + 3] = argv[i + 2 + (dump_argc - 3)]; + err = handle_cmd(state, id, trig_argc, trig_argv); + free(trig_argv); + if (err) + return err; + + /* + * WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION + * + * This code has a bug, which requires creating a separate + * nl80211 socket to fix: + * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or + * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel + * before (!) we listen to it, because we only start listening + * after we send our scan request. + * + * Doing it the other way around has a race condition as well, + * if you first open the events socket you may get a notification + * for a previous scan. + * + * The only proper way to fix this would be to listen to events + * before sending the command, and for the kernel to send the + * scan request along with the event, so that you can match up + * whether the scan you requested was finished or aborted (this + * may result in processing a scan that another application + * requested, but that doesn't seem to be a problem). + * + * Alas, the kernel doesn't do that (yet). + */ + + if (listen_events(state, ARRAY_SIZE(cmds), cmds) == + NL80211_CMD_SCAN_ABORTED) { + printf("scan aborted!\n"); + return 0; + } + + dump_argv[0] = argv[0]; + return handle_cmd(state, id, dump_argc, dump_argv); +} +TOPLEVEL(scan, "[-u] [freq *] [ies ] [ssid *|passive]", 0, 0, + CIB_NETDEV, handle_scan_combined, + "Scan on the given frequencies and probe for the given SSIDs\n" + "(or wildcard if not given) unless passive scanning is requested.\n" + "If -u is specified print unknown data in the scan results.\n" + "Specified (vendor) IEs must be well-formed."); +COMMAND(scan, dump, "[-u]", + NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump, + "Dump the current scan results. If -u is specified, print unknown\n" + "data in scan results."); +COMMAND(scan, trigger, "[freq *] [ies ] [ssid *|passive]", + NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan, + "Trigger a scan on the given frequencies with probing for the given\n" + "SSIDs (or wildcard if not given) unless passive scanning is requested.");