Wireless daemon for Linux
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7819 lines
200 KiB

/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2013-2019 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <linux/rtnetlink.h>
#include <linux/filter.h>
#include <ell/ell.h>
#ifndef ARPHRD_NETLINK
#define ARPHRD_NETLINK 824
#endif
#include "linux/nl80211.h"
#include "ell/useful.h"
#include "src/ie.h"
#include "src/mpdu.h"
#include "src/eapol.h"
#include "src/util.h"
#include "src/p2putil.h"
#include "src/nl80211cmd.h"
#include "monitor/pcap.h"
#include "monitor/display.h"
#include "monitor/nlmon.h"
#include "src/anqputil.h"
#define COLOR_TIMESTAMP COLOR_YELLOW
#define COLOR_REQUEST COLOR_BLUE
#define COLOR_RESPONSE COLOR_MAGENTA
#define COLOR_COMPLETE COLOR_MAGENTA
#define COLOR_RESULT COLOR_MAGENTA
#define COLOR_EVENT COLOR_CYAN
/* BSS Capabilities */
#define BSS_CAPABILITY_ESS (1<<0)
#define BSS_CAPABILITY_IBSS (1<<1)
#define BSS_CAPABILITY_CF_POLLABLE (1<<2)
#define BSS_CAPABILITY_CF_POLL_REQUEST (1<<3)
#define BSS_CAPABILITY_PRIVACY (1<<4)
#define BSS_CAPABILITY_SHORT_PREAMBLE (1<<5)
#define BSS_CAPABILITY_PBCC (1<<6)
#define BSS_CAPABILITY_CHANNEL_AGILITY (1<<7)
#define BSS_CAPABILITY_SPECTRUM_MGMT (1<<8)
#define BSS_CAPABILITY_QOS (1<<9)
#define BSS_CAPABILITY_SHORT_SLOT_TIME (1<<10)
#define BSS_CAPABILITY_APSD (1<<11)
#define BSS_CAPABILITY_DSSS_OFDM (1<<13)
struct nlmon *cur_nlmon;
enum msg_type {
MSG_REQUEST,
MSG_RESPONSE,
MSG_COMPLETE,
MSG_RESULT,
MSG_EVENT,
};
struct nlmon {
uint16_t id;
struct l_io *io;
struct l_io *pae_io;
struct l_queue *req_list;
struct pcap *pcap;
bool nortnl;
bool nowiphy;
bool noscan;
bool noies;
};
struct nlmon_req {
uint32_t seq;
uint32_t pid;
uint16_t flags;
uint8_t cmd;
uint8_t version;
};
typedef void (*attr_func_t) (unsigned int level, const char *label,
const void *data, uint16_t size);
enum attr_type {
ATTR_UNSPEC,
ATTR_FLAG,
ATTR_U8,
ATTR_U16,
ATTR_U32,
ATTR_U64,
ATTR_S8,
ATTR_S32,
ATTR_S64,
ATTR_STRING,
ATTR_ADDRESS,
ATTR_BINARY,
ATTR_NESTED,
ATTR_ARRAY,
ATTR_FLAG_OR_U16,
ATTR_CUSTOM,
};
struct attr_entry {
uint16_t attr;
const char *str;
enum attr_type type;
union {
const struct attr_entry *nested;
enum attr_type array_type;
attr_func_t function;
};
};
static void print_attributes(int indent, const struct attr_entry *table,
const void *buf, uint32_t len);
struct flag_names {
uint16_t flag;
const char *name;
};
struct wlan_iface {
int index;
};
static struct l_hashmap *wlan_iface_list = NULL;
static void wlan_iface_list_free(void *data)
{
struct wlan_iface *iface = data;
l_free(iface);
}
static void nlmon_req_free(void *data)
{
struct nlmon_req *req = data;
l_free(req);
}
static time_t time_offset = ((time_t) -1);
static inline void update_time_offset(const struct timeval *tv)
{
if (tv && time_offset == ((time_t) -1))
time_offset = tv->tv_sec;
}
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
do { \
printf("%*c%s%s%s%s" fmt "%s\n", (indent), ' ', \
use_color() ? (color1) : "", prefix, title, \
use_color() ? (color2) : "", ## args, \
use_color() ? COLOR_OFF : ""); \
} while (0)
#define print_text(color, fmt, args...) \
print_indent(4, COLOR_OFF, "", "", color, fmt, ## args)
#define print_field(fmt, args...) \
print_indent(4, COLOR_OFF, "", "", COLOR_OFF, fmt, ## args)
#define print_attr(level, fmt, args...) \
print_indent(4 + (level) * 4, COLOR_OFF, "", "", COLOR_OFF, \
fmt, ## args)
#define print_attr_color(level, color, fmt, args...) \
print_indent(4 + (level) * 4, COLOR_OFF, "", "", color, \
fmt, ## args)
#define print_space(x) printf("%*c", (x), ' ')
static void print_packet(const struct timeval *tv, char ident,
const char *color, const char *label,
const char *text, const char *extra)
{
int col = num_columns();
char line[256], ts_str[64];
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
if (tv) {
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
if (n > 0)
ts_pos += n;
}
n = sprintf(ts_str + ts_pos, " %" PRId64 ".%06" PRId64,
(int64_t)tv->tv_sec - time_offset,
(int64_t)tv->tv_usec);
if (n > 0) {
ts_pos += n;
ts_len += n;
}
}
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_OFF);
if (n > 0)
ts_pos += n;
}
if (use_color()) {
n = sprintf(line + pos, "%s", color);
if (n > 0)
pos += n;
}
n = sprintf(line + pos, "%c %s", ident, label);
if (n > 0) {
pos += n;
len += n;
}
if (text) {
int extra_len = extra ? strlen(extra) : 0;
int max_len = col - len - extra_len - ts_len - 3;
n = snprintf(line + pos, max_len + 1, ": %s", text);
if (n > max_len) {
line[pos + max_len - 1] = '.';
line[pos + max_len - 2] = '.';
if (line[pos + max_len - 3] == ' ')
line[pos + max_len - 3] = '.';
n = max_len;
}
if (n > 0) {
pos += n;
len += n;
}
}
if (use_color()) {
n = sprintf(line + pos, "%s", COLOR_OFF);
if (n > 0)
pos += n;
}
if (extra) {
n = sprintf(line + pos, " %s", extra);
if (n > 0) {
pos += n;
len += n;
}
}
if (ts_len > 0) {
printf("%s", line);
if (len < col)
print_space(col - len - ts_len - 1);
printf("%s%s\n", use_color() ? COLOR_TIMESTAMP : "", ts_str);
} else
printf("%s\n", line);
}
static void print_hexdump(unsigned int level,
const unsigned char *buf, uint16_t len)
{
static const char hexdigits[] = "0123456789abcdef";
char str[68];
uint16_t i;
if (!len)
return;
for (i = 0; i < len; i++) {
str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
str[((i % 16) * 3) + 2] = ' ';
str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
if ((i + 1) % 16 == 0) {
str[47] = ' ';
str[48] = ' ';
str[65] = '\0';
print_attr_color(level, COLOR_WHITE, "%s", str);
str[0] = ' ';
}
}
if (i % 16 > 0) {
uint16_t j;
for (j = (i % 16); j < 16; j++) {
str[(j * 3) + 0] = ' ';
str[(j * 3) + 1] = ' ';
str[(j * 3) + 2] = ' ';
str[j + 49] = ' ';
}
str[47] = ' ';
str[48] = ' ';
str[65] = '\0';
print_attr_color(level, COLOR_WHITE, "%s", str);
}
}
static void print_address(unsigned int level, const char *label,
const unsigned char address[6])
{
char addr[18];
snprintf(addr, sizeof(addr), "%02X:%02X:%02X:%02X:%02X:%02X",
address[0], address[1], address[2],
address[3], address[4], address[5]);
print_attr(level, "%s %s", label, addr);
}
static const struct {
const uint8_t oui[3];
const char *str;
} oui_table[] = {
{ { 0x00, 0x03, 0x7f }, "Atheros" },
{ { 0x00, 0x03, 0x93 }, "Apple" },
{ { 0x00, 0x04, 0x0e }, "AVM" },
{ { 0x00, 0x0f, 0xac }, "IEEE 802.11" },
{ { 0x00, 0x10, 0x18 }, "Broadcom" },
{ { 0x00, 0x17, 0xf2 }, "Apple" },
{ { 0x00, 0x40, 0x96 }, "Cisco Systems" },
{ { 0x00, 0x50, 0xf2 }, "Microsoft" },
{ { 0x00, 0x90, 0x4c }, "Epigram" },
{ { 0x50, 0x6f, 0x9a }, "Wi-Fi Alliance" },
{ }
};
static void print_ie_error(unsigned int level, const char *label,
uint16_t len, int err)
{
print_attr(level, "Error decoding %s IE len %d: %s (%d)", label, len,
strerror(-err), err);
}
static void print_ie_ssid(unsigned int level, const char *label,
const void *data, uint16_t size)
{
print_attr(level, "%s: %s", label, util_ssid_to_utf8(size, data));
}
static void print_ie_rate(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t *rate = (uint8_t *)data;
int pos = 0, i = 0;
char str[128];
if (!size) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s:", label);
while (i < size) {
bool mandatory = (rate[i] & 0x80);
if (rate[i] == 0xff) {
print_attr(level + 1, "BSS membership HT_PHY");
i++;
continue;
}
pos += snprintf(&str[pos], sizeof(str) - pos, "%.1f%s ",
(rate[i] & 127) * 0.5, mandatory? "(B)": "");
i++;
if (i % 8 && i != size)
continue;
if (pos) {
pos += snprintf(&str[pos], sizeof(str) - pos, "Mbit/s");
print_attr(level + 1, "%s", str);
pos = 0;
}
}
}
static void print_ie_ds(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t *channel = (uint8_t *)data;
if (!size) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s: channel %d", label, *channel);
}
static void print_ie_tim(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const char *dtim = data;
int t, len = size - 3, pos = 0;
uint8_t bit;
char str[128];
if (size < 4) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s:", label);
print_attr(level + 1, "DTIM count %2d %s", dtim[0],
dtim[0] ? "beacon frame(s)" :
"this beacon frame is DTIM");
print_attr(level + 1, "DTIM period %2d beacon frame(s)", dtim[1]);
print_attr(level + 1, "Group buffered %d offset %d",
!!(dtim[2] & 0x01), dtim[2] >> 1);
len = size - 3;
for (t = 0; t < len ; t++) {
if (((t + 1) % 4) == 1) {
pos = 0;
pos += snprintf(&str[pos], sizeof(str) - pos,
"AID %4d - %4d ",
t * 8 + 1,
t + 4 > len ? len * 8 : (t + 4) * 8);
}
for (bit = 0x01; bit; bit <<= 1)
pos += snprintf(&str[pos], sizeof(str) - pos,
"%d", !!(dtim[t + 3] & bit));
pos += snprintf(&str[pos], sizeof(str) - pos, " ");
if ((t + 1) % 4 == 0 || t + 1 == len)
print_attr(level + 1, "%s", str);
}
}
static void print_ie_country(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t *code = (uint8_t *)data;
int i = 3;
if (size < 6 || size % 2) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s: %c%c%c", label, code[0], code[1], code[2]);
while (i < size) {
if (code[i] > 200) {
print_attr(level + 1, "Regulatory ID %3d class %3d "
"coverage class %3d",
code[i], code[i + 1], code[i + 2]);
if (code[i + 2] < 32)
print_attr(level + 1, "%27c (air propagation "
"time %2d µs)", ' ', 3 * code[i + 2]);
} else {
print_attr(level + 1, "First channel %3d number of "
"channels %2d max tx power %2d dBm",
code[i], code[i + 1], code[i + 2]);
}
i += 3;
}
}
static void print_ie_bss_load(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t stations, capacity;
uint8_t utilization;
const uint8_t *bytes = data;
if (size != 5) {
print_ie_error(level, label, size, -EINVAL);
return;
}
stations = bytes[0] | bytes[1] << 8;
utilization = bytes[2];
capacity = bytes[3] | bytes[4] << 8;
print_attr(level, "%s: %2d station(s) utilization %d/255 available "
"capacity %d 32µs/s units",
label, stations, utilization, capacity);
}
static void print_ie_power_constraint(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t *dB = (uint8_t *)data;
if (!size) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s: %2d dB", label, *dB);
}
static void print_ie_tpc(unsigned int level, const char *label,
const void *data, uint16_t size)
{
signed char *dB = (signed char*)data;
if (size != 2) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s: transmit power %2d dB link margin %2d dB",
label, dB[0], dB[1]);
}
static void print_ie_erp(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t *flags = (uint8_t *)data;
if (!size) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "%s:", label);
print_attr(level + 1, "non-ERP present %d", !!(*flags & 0x01));
print_attr(level + 1, "use protection %d", !!(*flags & 0x02));
print_attr(level + 1, "Barker preamble mode %d", !!(*flags & 0x04));
}
struct cipher_suites {
uint32_t cipher;
const char *str;
};
static const struct cipher_suites rsn_cipher_selectors[] = {
{ 0x000fac00, "Use group cipher suite" },
{ 0x000fac01, "WEP-40" },
{ 0x000fac02, "TKIP" },
{ 0x000fac04, "CCMP" },
{ 0x000fac05, "WEP-104" },
{ 0x000fac06, "BIP" },
{ 0x000fac07, "Group traffic not allowed" },
{ 0x00147201, "WPI-SMS4" },
{ },
};
static const struct cipher_suites rsn_akm_selectors[] = {
{ 0x000fac01, "IEEE 802.1X/PMKSA; RSNA/PMKSA caching" },
{ 0x000fac02, "PSK; RSNA PSK" },
{ 0x000fac03, "IEEE 802.1X FT; FT" },
{ 0x000fac04, "PSK FT; FT" },
{ 0x000fac05, "IEEE 802.1X/PMKSA caching SHA256; RSNA/RSNA caching SHA256" },
{ 0x000fac06, "PSK SHA256; RSNA PSK SHA256" },
{ 0x000fac07, "TDLS; TPK" },
{ 0x000fac08, "SAE/PMKSA caching SHA256; RSNA PMKSA caching SHA256/mesh peering exchange" },
{ 0x000fac09, "FT SAE SHA256; FT" },
{ 0x000fac0e, "FILS SHA256" },
{ 0x000fac0f, "FILS SHA384" },
{ 0x000fac10, "FILS FT SHA256" },
{ 0x000fac11, "FILS FT SHA3854" },
{ 0x000fac12, "OWE" },
{ 0x506f9a01, "WFA OSEN" },
{ }
};
static const struct cipher_suites wpa_cipher_selectors[] = {
{ 0x0050f200, "Use group cipher suite" },
{ 0x0050f201, "WEP-40" },
{ 0x0050f202, "TKIP" },
{ 0x0050f204, "CCMP" },
{ 0x0050f205, "WEP-104" },
{ },
};
static const struct cipher_suites wpa_akm_selectors[] = {
{ 0x0050f201, "IEEE 802.1X/PMKSA; RSNA/PMKSA caching" },
{ 0x0050f202, "PSK; RSNA PSK" },
{ }
};
static void print_ie_cipher_suite(unsigned int level, const char *label,
const uint32_t cipher,
const struct cipher_suites cipher_table[])
{
const char *str = NULL;
unsigned int i;
unsigned char oui[] = {
(cipher & 0xff000000) >> 24,
(cipher & 0x00ff0000) >> 16,
(cipher & 0x0000ff00) >> 8,
};
char suite_value[32] = "";
for (i = 0; cipher_table[i].str; i++) {
if (cipher_table[i].cipher == cipher) {
str = cipher_table[i].str;
snprintf(suite_value, sizeof(suite_value), " %02x",
cipher & 0x000000ff);
break;
}
}
if (!str) {
for (i = 0; oui_table[i].str; i++) {
if (!memcmp(oui_table[i].oui, oui, 3)) {
str = oui_table[i].str;
snprintf(suite_value, sizeof(suite_value),
" %02x (vendor specific)",
cipher & 0x000000ff);
break;
}
}
}
if (!str) {
str = "unknown";
snprintf(suite_value, sizeof(suite_value), "%02x (unknown)",
cipher & 0x000000ff);
}
if (label)
print_attr(level, "%s: %s (%02x:%02x:%02x) suite %s",
label, str, oui[0], oui[1], oui[2], suite_value);
else
print_attr(level, "%s (%02x:%02x:%02x) suite %s",
str, oui[0], oui[1], oui[2], suite_value);
}
static void print_ie_cipher_suites(unsigned int level, const char *label,
const void *data, uint16_t size,
const struct cipher_suites cipher_table[])
{
uint32_t cipher;
print_attr(level, "%s: len %u", label, size);
while (size >= 4) {
cipher = l_get_be32(data);
print_ie_cipher_suite(level + 1, NULL, cipher, cipher_table);
data += 4;
size -= 4;
}
}
static const char *rsn_capabilities_bitfield[] = {
"Preauthentication",
"No Pairwise",
"",
"",
"",
"",
"Management Frame Protection Required",
"Management Frame Protection Capable",
"Reserved",
"Peerkey Enabled",
"SPP A-MSDU Capable",
"SPP A-MSDU Required",
"PBAC",
"Extended Key ID for Individually Addressed Frames",
"Reserved",
"Reserved",
NULL
};
static void print_ie_bitfield(unsigned int level, const char *label,
const uint8_t *bytes, const uint8_t *mask, size_t len,
const char *bitfield_table[])
{
unsigned int i;
for (i = 0; i < len * 8; i++) {
uint8_t byte = i / 8;
uint8_t bit = i % 8;
uint8_t masked = bytes[byte] & mask[byte];
if (!test_bit(&masked, bit))
continue;
print_attr(level, "%s: bit %2d: %s", label, i,
bitfield_table[i]);
}
}
static size_t print_ie_rsn_suites(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t count;
uint16_t orig_size = size;
print_ie_cipher_suites(level + 1, "Group Data Cipher Suite", data, 4,
rsn_cipher_selectors);
data += 4;
size -= 4;
if (size < 2)
goto end;
count = l_get_le16(data) * 4;
data += 2;
size -= 2;
if (size < count)
goto end;
print_ie_cipher_suites(level + 1, "Pairwise Cipher Suite", data,
count, rsn_cipher_selectors);
data += count;
size -= count;
if (size < 2)
goto end;
count = l_get_le16(data) * 4;
data += 2;
size -= 2;
if (size < count)
goto end;
print_ie_cipher_suites(level + 1, "AKM Suite", data, count,
rsn_akm_selectors);
data += count;
size -= count;
if (size < 2)
goto end;
return orig_size - size;
end:
if (size)
print_ie_error(level, label, size, -EINVAL);
return orig_size - size;
}
static void print_ie_rsn(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const void *end = data + size;
uint16_t version, count;
uint8_t bytemask[2];
int i;
const char *rsn_capabilities_replay_counter[] = {
"1 replay counter",
"2 replay counters",
"4 replay counters",
"16 replay counters"
};
print_attr(level, "RSN:");
if (end - data < 2) {
print_ie_error(level, label, size, -EINVAL);
return;
}
version = l_get_le16(data);
if (version != 1) {
print_attr(level, "Unknown RSN version %d", version);
return;
}
data += 2;
if (end - data < 4)
goto end;
data += print_ie_rsn_suites(level, label, data, size);
bytemask[0] = 0x03;
bytemask[1] = 0x00;
print_ie_bitfield(level + 1, "RSN capabilities", data, bytemask,
sizeof(bytemask), rsn_capabilities_bitfield);
count = (*((uint8_t *)data) & 0x0c) >> 2;
print_attr(level + 1, "RSN capabilities: bits 2 - 3: %s per PTKSA",
rsn_capabilities_replay_counter[count]);
count = (*((uint8_t *)data) & 0x30) >> 4;
print_attr(level + 1, "RSN capabilities: bits 4 - 5: %s per GTKSA",
rsn_capabilities_replay_counter[count]);
bytemask[0] = 0xc0;
bytemask[1] = 0xff;
print_ie_bitfield(level + 1, "RSN capabilities", data, bytemask,
sizeof(bytemask), rsn_capabilities_bitfield);
data += 2;
if (end - data < 2)
goto end;
count = l_get_le16(data) * 16;
data += 2;
if (end - data < count)
goto end;
for (i = 0; i < count; i += 16) {
const uint8_t *bytes = data;
print_attr(level + 1, "PMKID: %02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x",
bytes[i], bytes[i + 1],
bytes[i + 2], bytes[i + 3],
bytes[i + 4], bytes[i + 5],
bytes[i + 6], bytes[i + 7],
bytes[i + 8], bytes[i + 9],
bytes[i + 10], bytes[i + 11],
bytes[i + 12], bytes[i + 13],
bytes[i + 14], bytes[i + 15]);
}
data += count;
if (end - data < 4)
goto end;
print_ie_cipher_suites(level + 1, "Group Management Cipher Suite",
data, 4, rsn_cipher_selectors);
data += 4;
end:
if (end - data)
print_ie_error(level, label, size, -EINVAL);
}
static void print_ie_wpa(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint8_t offset;
uint16_t version, count;
if (size < 2)
return;
offset = 0;
version = l_get_le16(data + offset);
offset += 2;
if (version != 1)
return;
print_attr(level, "WPA:");
print_attr(level + 1, "Version: %d(%04x)", version, version);
if (offset + 4 > size)
goto end;
print_ie_cipher_suites(level + 1, "Group Data Cipher Suite",
data + offset, 4, wpa_cipher_selectors);
offset += 4;
if (offset + 2 > size)
goto end;
count = l_get_le16(data + offset) * 4;
offset += 2;
if (offset + count > size)
goto end;
print_ie_cipher_suites(level + 1, "Pairwise Cipher Suite",
data + offset, count, wpa_cipher_selectors);
offset += count;
if (offset + 2 > size)
goto end;
count = l_get_le16(data + offset) * 4;
offset += 2;
if (offset + count > size)
goto end;
print_ie_cipher_suites(level + 1, "AKM Suite", data + offset, count,
wpa_akm_selectors);
return;
end:
print_ie_error(level, label, size, -EINVAL);
}
static void print_ie_wfa_hs20(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *ptr = data;
bool pps_mo_id_present;
bool anpq_domain_id_present;
if (size < 1)
return;
pps_mo_id_present = test_bit(ptr, 1);
anpq_domain_id_present = test_bit(ptr, 2);
print_attr(level + 1, "HS2.0 Indication Element:");
print_attr(level + 2, "DGAF Disabled: %u", test_bit(ptr, 0));
print_attr(level + 2, "PPS MO ID Present: %u", pps_mo_id_present);
print_attr(level + 2, "ANQP Domain ID Present: %u",
anpq_domain_id_present);
switch (bit_field(ptr[0], 4, 7)) {
case 0:
print_attr(level + 2, "Version Number: 1.x");
break;
case 1:
print_attr(level + 2, "Version Number: 2.x");
break;
case 2:
print_attr(level + 2, "Version Number: 3.x");
break;
}
ptr += 1;
size -= 1;
if (pps_mo_id_present) {
if (size < 2)
return;
print_attr(level + 2, "PPS MO ID: %02x %02x", ptr[0], ptr[1]);
ptr += 2;
size -= 2;
}
if (anpq_domain_id_present) {
if (size < 2)
return;
print_attr(level + 2, "ANQP Domain ID: %02x %02x",
ptr[0], ptr[1]);
}
}
static bool print_oui(unsigned int level, const uint8_t *oui)
{
const char *str = NULL;
unsigned int i;
for (i = 0; oui_table[i].str; i++) {
if (!memcmp(oui_table[i].oui, oui, 3)) {
str = oui_table[i].str;
break;
}
}
if (!str) {
print_attr(level + 1, "OUI: %02x:%02x:%02x type:%02x",
oui[0], oui[1], oui[2],
oui[3]);
return false;
}
print_attr(level + 1, "%s (%02x:%02x:%02x) type: %02x", str,
oui[0], oui[1], oui[2],
oui[3]);
return true;
}
static void print_ipv4(unsigned int level, const char *label,
const uint8_t *addr)
{
print_attr(level, "%s: %u.%u.%u.%u", label,
addr[0], addr[1], addr[2], addr[3]);
}
static void print_ie_vendor(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *oui = data;
print_attr(level, "%s: len %u", label, size);
if (size < 4)
return;
if (!print_oui(level, oui))
return;
data += 4;
size -= 4;
if (!memcmp(oui, microsoft_oui, 3)) {
switch (oui[3]) {
case 1: /* MSoft WPA IE */
print_ie_wpa(level + 2, label, data, size);
return;
default:
return;
}
} else if (!memcmp(oui, wifi_alliance_oui, 3)) {
switch (oui[3]) {
case 0x04:
print_attr(level + 1, "IP Address Request KDE");
return;
case 0x05:
print_attr(level + 1, "IP Address Allocation KDE");
if (size < 12)
return;
print_ipv4(level + 2, "Client IP Address", data + 0);
print_ipv4(level + 2, "Subnet Mask", data + 4);
print_ipv4(level + 2, "GO IP Address", data + 8);
return;
case 0x10:
print_ie_wfa_hs20(level + 1, label, data, size);
return;
case 0x12:
print_ie_rsn_suites(level + 1, label, data, size);
return;
default:
return;
}
} else if (!memcmp(oui, ieee_oui, 3)) {
const char *kde;
/* EAPoL-Key KDEs */
switch (oui[3]) {
case 1:
kde = "GTK";
break;
case 3:
kde = "MAC address";
break;
case 4:
kde = "PMKID";
break;
case 5:
kde = "SMK";
break;
case 6:
kde = "Nonce";
break;
case 7:
kde = "Lifetime";
break;
case 8:
kde = "Error";
break;
case 9:
kde = "IGTK";
break;
case 10:
kde = "Key ID";
break;
case 11:
kde = "Multi-band GTK";
break;
case 12:
kde = "Multi-band Key ID";
break;
default:
return;
}
print_attr(level + 1, "%s KDE", kde);
return;
}
}
static void print_ie_mcs(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *bytes = data;
int i;
uint8_t bytemask[16];
uint16_t data_rate;
const char *mcs_set[128] = {
[77] = "Reserved",
[78] = "Reserved",
[79] = "Reserved",
[90] = "Reserved",
[91] = "Reserved",
[92] = "Reserved",
[93] = "Reserved",
[94] = "Reserved",
[95] = "Reserved",
[96] = "Tx MCS set defined",
[97] = "Tx Rx MCS set not equal",
[100] = "Tx unequal modulation supported",
[101] = "Reserved",
[102] = "Reserved",
[103] = "Reserved",
[104] = "Reserved",
[105] = "Reserved",
[106] = "Reserved",
[107] = "Reserved",
[108] = "Reserved",
[109] = "Reserved",
[110] = "Reserved",
[111] = "Reserved",
[112] = "Reserved",
[113] = "Reserved",
[114] = "Reserved",
[115] = "Reserved",
[116] = "Reserved",
[117] = "Reserved",
[118] = "Reserved",
[119] = "Reserved",
[120] = "Reserved",
[121] = "Reserved",
[122] = "Reserved",
[123] = "Reserved",
[124] = "Reserved",
[125] = "Reserved",
[126] = "Reserved",
[127] = "Reserved",
};
if (size != 16)
return print_ie_error(level, label, size, -EINVAL);
for (i = 0; i < 77; i++) {
if (test_bit(bytes, i))
print_attr(level, "%s: MCS %d", label, i);
}
memset(bytemask, 0, sizeof(bytemask));
bytemask[9] = 0xe0;
print_ie_bitfield(level, "MCS set", bytes, bytemask, sizeof(bytemask),
mcs_set);
data_rate = l_get_le16(&bytes[10]) & 0x3ff;
if (data_rate)
print_attr(level, "MCS set: Rx Highest data rate: %d Mbit/s",
data_rate);
bytemask[9] = 0x00;
bytemask[11] = 0xfc;
bytemask[12] = 0x03;
print_ie_bitfield(level, "MCS Set", bytes, bytemask, sizeof(bytemask),
mcs_set);
if (bytes[12] & 0x0c)
print_attr(level,
"MCS set: Tx max spatial streams supported: %d",
((bytes[12] & 0x0c) >> 2) + 1);
bytemask[11] = 0x00;
bytemask[12] = 0xf0;
bytemask[13] = 0xff;
bytemask[14] = 0xff;
bytemask[15] = 0xff;
print_ie_bitfield(level, "MCS set", bytes, bytemask, sizeof(bytemask),
mcs_set);
}
static void print_ie_ht_operation(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const char *secondary_offset[] = {
"no secondary channel",
"above primary channel",
"reserved",
"below primary channel"
};
const char *channel_width[] = {
"20 MHz channel width",
"Any supported channel width"
};
const char *ht_protection[] = {
"No protection",
"Nonmember protection mode",
"20 MHz protection mode",
"non-HT mixed mode"
};
const char *ht_ops_bitfield[] = {
"",
"",
"",
"RIFS permitted",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"",
"",
"Non-greenfield HT STAs present",
"Reserved",
"OBSS non-HT STAs present",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Dual beacon",
"Dual CTS protection",
"STBC beacon",
"L-SIG TXOP protection full support",
"PCO active",
"PCO Phase",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
NULL
};
uint8_t *bytes = (uint8_t *) data;
uint8_t bytemask[5];
int i;
if (size < 22) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr (level, "%s:", label);
print_attr (level + 1, "Primary channel %d", bytes[0]);
i = bytes[1] & 0x03;
print_attr (level + 1,
"Information: Secondary Channel Offset: %s",
secondary_offset[i]);
i = (bytes[1] & 0x04) >> 2;
print_attr (level + 1,
"Information: Channel width: bit 2: %s",
channel_width[i]);
memset(bytemask, 0, sizeof(bytemask));
bytemask[0] = 0xf8;
print_ie_bitfield(level + 1,
"Information", &bytes[1], bytemask,
sizeof(bytemask), ht_ops_bitfield);
i = bytes[2] & 0x03;
print_attr(level + 1,
"Information: HT Protection: bits 8 - 9: %s",
ht_protection[i]);
bytemask[0] = 0x00;
bytemask[1] = 0xfc;
bytemask[2] = 0xff;
bytemask[3] = 0xff;
bytemask[4] = 0xff;
print_ie_bitfield(level + 1, "Information", &bytes[1],
bytemask, sizeof(bytemask), ht_ops_bitfield);
print_ie_mcs(level + 1, "Basic MCS set", &bytes[6], 16);
}
static const char *extended_capabilities_bitfield[80] = {
[0] = "20/40 BSS coexistence management support",
[1] = "Reserved",
[2] = "Extended channel switching",
[3] = "Reserved",
[4] = "PSMP capability",
[5] = "Reserved",
[6] = "S-PSMP support",
[7] = "Event",
[8] = "Diagnostics",
[9] = "Multicast diagnostics",
[10] = "Location tracking",
[11] = "FMS",
[12] = "Proxy ARP service",
[13] = "Collocated interference reporting",
[14] = "Civic location",
[15] = "Geospatial location",
[16] = "TFS",
[17] = "WNM-Sleep mode",
[18] = "TIM broadcast",
[19] = "BSS transition",
[20] = "QoS traffic capability",
[21] = "AC station count",
[22] = "Multiple BSSID",
[23] = "Timing measurement",
[24] = "Channel usage",
[25] = "SSID list",
[26] = "DMS",
[27] = "UTC TSF offset",
[28] = "TDLS Peer U-APSD buffer STA support",
[29] = "TDLS Peer PSM support",
[30] = "TDLS channel switching",
[31] = "Interworking",
[32] = "QoS Map",
[33] = "EBR",
[34] = "SSPN Interface",
[35] = "Reserved",
[36] = "MSGCF Capability",
[37] = "TDLS Support",
[38] = "TDLS Prohibited",
[39] = "TDLS Channel Switching Prohibited",
[40] = "Reject Unadmitted Frame",
[41 ... 43] = "Reserved",
[44] = "Identifier Location",
[45] = "U-APSD Coexistence",
[46] = "WNM- Notification",
[47] = "Reserved",
[48] = "UTF-8 SSID",
[49] = "QMF Activated",
[50] = "QMF Reconfiguration Activated",
[51] = "Robust AV Streaming",
[52] = "Advanced GCR",
[53] = "Mesh GCR",
[54] = "SCS",
[55] = "QLoad Report",
[56] = "Alternate EDCA",
[57] = "Unprotected TXOP Negotiation",
[58] = "Protected TXOP Negotiation",
[59] = "Reserved",
[60] = "Protected QLoad Report",
[61] = "TDLS Wider Bandwidth",
[62] = "Opmode Notification",
[65] = "Channel Schedule Management",
[66] = "Geodatabase Inband Enabling Signal",
[67] = "Network Channel Control",
[68] = "White Space Map",
[69] = "Channel Availability Query",
[70] = "Fine Timing Measurement Responder",
[71] = "Fine Timing Measurement Initiator",
[72] = "FILS Capability",
[73] = "Extended Spectrum Management Capable",
[74] = "Future Channel Guidance",
};
static void print_ie_extended_capabilities(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
uint8_t bytemask1[] = { 0xff, 0xff, 0xff, 0xff,
0xff, 0x01 };
uint8_t bytemask2[] = { 0x00, 0x00, 0x00, 0x00,
0x00, 0xf0, 0xff, 0xff };
uint8_t interval;
size_t bytes;
bool spsmp;
print_attr(level, "%s: len %u", label, size);
if (size == 0)
return;
spsmp = test_bit(data, 6);
bytes = size < sizeof(bytemask1) ? size : sizeof(bytemask1);
/* Print first 40 bits */
print_ie_bitfield(level + 1, "Capability", data, bytemask1,
bytes, extended_capabilities_bitfield);
if (size <= bytes)
return;
/* Print Service Interval Granularity */
if (spsmp) {
interval = bit_field(*((uint8_t *) data + 5), 1, 3);
print_attr(level + 1,
"Shortest Service Interval Granularity: %d ms",
interval * 5 + 5);
}
bytes = size < sizeof(bytemask2) ? size : sizeof(bytemask2);
/* Print remainder */
print_ie_bitfield(level + 1, "Capability", data, bytemask2,
bytes, extended_capabilities_bitfield);
}
static void print_ie_ht_capabilities(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
static const char *ht_capabilities_info_bitfield[16] = {
[0] = "LDPC Coding Capability",
[1] = "Supported Channel Width Set",
[2] = "SM Power Save",
[3] = "SM Power Save",
[4] = "HT-Greenfield",
[5] = "Short GI for 20Mhz",
[6] = "Short GI for 40Mhz",
[7] = "Tx STBC",
[8] = "Rx STBC",
[9] = "Rx STBC",
[10] = "HT-Delayed Block Ack",
[11] = "Maximum A-MSDU Length",
[12] = "DSSS/CCK Mode in 40Mhz",
[13] = "Reserved",
[14] = "40 Mhz Intolerant",
[15] = "L-SIG TXOP Protection Support",
};
static const char *ht_capabilities_sm_power_save[4] = {
"Static", "Dynamic", "Reserved", "Disabled",
};
static const char *ht_capabilities_rx_stbc[4] = {
"Disabled", "One spatial stream", "One and two spatial streams",
"One, two and three spatial streams"
};
static const char *ht_capabilities_min_mpdu_start_spacing[8] = {
"No restriction", "1/4 us", "1/2 us", "1 us", "2 us",
"4 us", "8 us", "16 us",
};
static const char *ht_capabilities_pco_transition_time[4] = {
"No transition", "400 us", "1.5 ms", "5 ms",
};
static const char *ht_capabilities_mcs_feedback[4] = {
"No feedback", "Reserved", "Unsolicited", "Both",
};
uint8_t info_mask[] = { 0x03, 0xfc };
const uint8_t *htc = data;
uint8_t sm_power_save;
uint8_t rx_stbc;
uint8_t ampdu_exponent;
bool pco;
bool plus_htc;
bool rd_responder;
uint8_t bits;
print_attr(level, "%s: len %u", label, size);
if (size != 26)
return;
/* Print bits 0-1 */
print_ie_bitfield(level + 1, "HT Capabilities Info", data, info_mask,
1, ht_capabilities_info_bitfield);
/* Print SM Power Save */
sm_power_save = bit_field(htc[0], 2, 2);
print_attr(level + 1, "HT Capabilities Info: bits 2-3: %s",
ht_capabilities_sm_power_save[sm_power_save]);
/* Print bits 4-7 */
info_mask[0] = 0xf0;
print_ie_bitfield(level + 1, "HT Capabilities Info", data, info_mask,
1, ht_capabilities_info_bitfield);
rx_stbc = bit_field(htc[1], 0, 2);
print_attr(level + 1, "HT Capabilities Info: bits 8-9: %s",
ht_capabilities_rx_stbc[rx_stbc]);
/* Print bits 10-15 */
info_mask[0] = 0x00;
print_ie_bitfield(level + 1, "HT Capabilities Info", data, info_mask,
2, ht_capabilities_info_bitfield);
ampdu_exponent = bit_field(htc[2], 0, 2);
print_attr(level + 1, "A-MPDU Parameters: "
"Maximum A-MPDU Length Exponent: %d", ampdu_exponent);
bits = bit_field(htc[2], 2, 3);
print_attr(level + 1, "A-MPDU Parameters: "
"Minimum MPDU Start Spacing: %s",
ht_capabilities_min_mpdu_start_spacing[bits]);
print_ie_mcs(level + 1, "Supported MCS", htc + 3, 16);
pco = test_bit(htc + 18, 0);
print_attr(level + 1, "HT Extended Capabilities: PCO: %s",
bits ? "supported" : "not supported");
if (pco) {
bits = bit_field(htc[18], 1, 2);
print_attr(level + 1, "HT Extended Capabilities: "
"PCO Transition Time: %s",
ht_capabilities_pco_transition_time[bits]);
}
bits = bit_field(htc[19], 0, 2);
print_attr(level + 1, "HT Extended Capabilities: "
"MCS Feedback: %s", ht_capabilities_mcs_feedback[bits]);
plus_htc = test_bit(htc + 19, 2);
print_attr(level + 1, "HT Extended Capabilities: "
"+HTC: %s", plus_htc ? "supported" : "not supported");
rd_responder = test_bit(htc + 19, 3);
print_attr(level + 1, "HT Extended Capabilities: "
"RD Responder: %s",
rd_responder ? "supported" : "not supported");
/* TODO: Transmit Beamforming Capabilities field */
/* TODO: ASEL Capability field */
}
static void print_ie_rm_enabled_caps(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
static const char *capabilities[40] = {
[0] = "Link Measurement",
[1] = "Neighbor Report",
[2] = "Parallel Measurements",
[3] = "Repeated Measurements",
[4] = "Beacon Passive Measurement",
[5] = "Beacon Active Measurement",
[6] = "Beacon Table Measurement",
[7] = "Beacon Measurement Reporting Conditions",
[8] = "Frame Measurement",
[9] = "Channel Load Measurement",
[10] = "Noise Histogram Measurement",
[11] = "Statistics Measurement",
[12] = "LCI Measurement",
[13] = "LCI Azimuth",
[14] = "Transmit Stream / Category Measurement",
[15] = "Triggered Transmit Stream / Category Measurement",
[16] = "AP Channel Report",
[17] = "RM MIB",
[27] = "Measurement Pilot Transmission Information",
[28] = "Neighbor Report TSF Offset",
[29] = "RCPI Measurement capability enabled",
[30] = "RSNI Measurement",
[31] = "BSS Average Access Delay",
[32] = "BSS Available Admission Capacity",
[33] = "Antenna capability",
};
const uint8_t *bytes;
uint8_t bytemask1[3] = { 0xff, 0xff, 0x03 };
uint8_t bytemask2[2] = { 0xf8, 0x03 };
uint8_t byte;
print_attr(level, "%s: len %u", label, size);
if (size != 5)
return;
bytes = data;
print_ie_bitfield(level + 1, "Enabled", bytes,
bytemask1, sizeof(bytemask1), capabilities);
byte = bit_field(bytes[2], 2, 3);
print_attr(level + 1, "Operating Channel Max Measurement Duration: %u",
byte);
byte = bit_field(bytes[2], 5, 3);
print_attr(level + 1, "Non-Operating Channel Max Measurement "
"Duration: %u", byte);
byte = bit_field(bytes[3], 0, 3);
print_attr(level + 1, "Measurement Pilot Capability: %u", byte);
print_ie_bitfield(level + 1, "Enabled", bytes + sizeof(bytemask1),
bytemask2, sizeof(bytemask2), capabilities);
}
static void print_ie_interworking(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
const uint8_t *ptr = data;
const char *msg;
uint8_t type;
bool venue = false;
bool hessid = false;
print_attr(level, "%s: len %u", label, size);
type = bit_field(ptr[0], 0, 3);
switch (type) {
case 0:
msg = "Private network";
break;
case 1:
msg = "Private network w/ guest access";
break;
case 2:
msg = "Chargeable public network";
break;
case 3:
msg = "Free public network";
break;
case 4:
msg = "Personal device network";
break;
case 5:
msg = "Emergency services only network";
break;
case 14:
msg = "Test/Experimental";
break;
case 15:
msg = "Wildcard";
break;
default:
return;
}
print_attr(level + 1, "Network Type: %s", msg);
print_attr(level + 1, "Internet: %u", test_bit(ptr, 4));
print_attr(level + 1, "ASRA: %u", test_bit(ptr, 5));
print_attr(level + 1, "ESR: %u", test_bit(ptr, 6));
print_attr(level + 1, "UESA: %u", test_bit(ptr, 7));
size--;
ptr++;
if (!size)
return;
/*
* There is venue/hessid info optionally, and no way of determining if
* they exist except looking at the length.
*/
if (size == 2)
venue = true;
else if (size == 6)
hessid = true;
else if (size == 8) {
venue = true;
hessid = true;
}
if (venue) {
switch (ptr[0]) {
case 0:
msg = "Unspecified";
break;
case 1:
msg = "Assembly";
break;
case 2:
msg = "Business";
break;
case 3:
msg = "Educational";
break;
case 5:
msg = "Factory and Industrial";
break;
case 6:
msg = "Institutional";
break;
case 7:
msg = "Mercantile";
break;
case 8:
msg = "Residential";
break;
case 9:
msg = "Utility and Miscellaneous";
break;
case 10:
msg = "Vehicular";
break;
case 11:
msg = "Outdoor";
break;
default:
return;
}
/*
* Each of the above groups have many group types, but if
* anyone really cares they can cross reference the integer
* type with IEEE 802.11-2016 Table 9-62
*/
print_attr(level + 1, "Venue: %s, type: %u", msg, ptr[1]);
ptr += 2;
size -= 2;
}
if (hessid)
print_attr(level + 1, "HESSID: "MAC, MAC_STR(ptr));
}
static void print_ie_advertisement(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
const uint8_t *ptr = data;
const char *msg = NULL;
print_attr(level, "%s: len %u", label, size);
while (size) {
uint8_t qr_len = bit_field(ptr[0], 0, 7);
uint8_t id = ptr[1];
switch (id) {
case IE_ADVERTISEMENT_ANQP:
msg = "ANQP";
break;
case IE_ADVERTISEMENT_MIH_SERVICE:
msg = "MIH Information Service";
break;
case IE_ADVERTISEMENT_MIH_DISCOVERY:
msg = "MIH Command and Event Services";
break;
case IE_ADVERTISEMENT_EAS:
msg = "EAS";
break;
case IE_ADVERTISEMENT_RLQP:
msg = "RLQP";
break;
case IE_ADVERTISEMENT_VENDOR_SPECIFIC:
msg = "Vendor Specific";
break;
default:
return;
}
if (id == IE_ADVERTISEMENT_VENDOR_SPECIFIC) {
size -= ptr[3];
ptr += ptr[3];
} else {
size -= 2;
ptr += 2;
}
print_attr(level + 1, "Protocol: %s, Query Resp Limit: %u",
msg, qr_len);
}
}
static void print_ie_owe(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
uint16_t group;
print_attr(level, "%s: len %u", label, size);
group = l_get_le16(data);
print_attr(level + 1, "ECC Group: %u", group);
print_attr(level + 1, "Public Key:");
print_hexdump(level + 2, data + 2, size - 2);
}
static void print_fils_indication(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
const uint8_t *bytes = data;
print_attr(level, "FILS Indication: len %u", size);
print_attr(level + 1, "Num Public Key Identifiers: %u",
bit_field(*bytes, 0, 3));
print_attr(level + 1, "Num Realm Identifiers: %u",
bit_field(*bytes, 3, 3));
print_attr(level + 1, "IP configuration: %u", test_bit(bytes, 6));
print_attr(level + 1, "Cache Identifier Included: %u",
test_bit(bytes, 7));
bytes++;
print_attr(level + 1, "HES-SID Included: %u", test_bit(bytes, 0));
print_attr(level + 1, "SK Auth without PFS supported: %u",
test_bit(bytes, 1));
print_attr(level + 1, "SK Auth with PFS supported: %u",
test_bit(bytes, 2));
print_attr(level + 1, "PK Auth supported: %u", test_bit(bytes, 3));
bytes++;
print_hexdump(level + 1, bytes, size - 2);
}
static void print_fils_key_confirmation(unsigned int level, const char *label,
const void *data, uint16_t size)
{
print_attr(level, "FILS Key Confirmation (KeyAuth): len %u", size);
}
static void print_fils_session(unsigned int level, const char *label,
const void *data, uint16_t size)
{
print_attr(level, "FILS Session: len %u", size);
}
static void print_ie_supported_operating_classes(unsigned int level,
const char *label,
const void *data,
uint16_t size)
{
const void *end = data + size;
if (size < 1) {
print_ie_error(level, label, size, -EINVAL);
return;
}
print_attr(level, "Current Operating Class: %u", l_get_u8(data));
data += 1;
while (end - data) {
uint8_t cls = l_get_u8(data);
/*
* TODO: Support Current Operating Class Extension Sequence
* and Operating Class Duple Sequence
*/
if (cls == 130 || cls == 0) {
data = end;
break;
}
print_attr(level, "Supported Operating Class: %u", cls);
data += 1;
}
if (end - data)
print_ie_error(level, label, size, -EINVAL);
}
static void print_qos_map(unsigned int level, const char *label,
const void *data,
uint16_t size)
{
print_attr(level, "QoS Map");
}
static void print_measurement_request_beacon(unsigned int level,
const void *data,
uint16_t size)
{
uint8_t mode;
if (size < 13)
return;
print_attr(level, "Operating Class: %u", l_get_u8(data));
print_attr(level, "Channel: %u", l_get_u8(data + 1));
print_attr(level, "Randomization Interval: %u", l_get_le16(data + 2));
print_attr(level, "Duration: %u", l_get_le16(data + 4));
mode = l_get_u8(data + 6);
switch (mode) {
case 0:
print_attr(level, "Measurement: Passive");
break;
case 1:
print_attr(level, "Measurement: Active");
break;
case 2:
print_attr(level, "Measurement: Table");
break;
default:
print_attr(level, "Measurement: Invalid (%u)", mode);
return;
}
print_attr(level, "BSSID: "MAC, MAC_STR(((const uint8_t *)data + 7)));
}
static const char *rrm_measurement_types[] = {
[0] = "Basic",
[1] = "Clear Channel Assessment",
[2] = "Receive Power Indication",
[3] = "Channel Load",
[4] = "Noise Histogram",
[5] = "Beacon",
[6] = "Frame",
[7] = "STA Statistics",
[8] = "LCI",
[9] = "Transmit Stream/Category Measurement",
[10] = "Multicast Diagnostics",
[11] = "Location Civic",
[12] = "Location Identifier",
[13] = "Directional Channel Quality",
[14] = "Directional Measurement",
[15] = "Directional Statistics",
[16] = "Fine Timing Measurement Range",
[255] = "Measurement Pause"
};
static void print_measurement_request(unsigned int level, const char *label,
const void *data,
uint16_t size)
{
uint8_t mode;
uint8_t type;
print_attr(level, "Measurement Request");
if (size < 3)
return;
print_attr(level + 1, "Token: %u", l_get_u8(data));
mode = l_get_u8(data + 1);
print_attr(level + 1, "Request Mode: %u", mode);
if (test_bit(&mode, 0))
print_attr(level + 2, "Parallel bit set");
if (test_bit(&mode, 1))
print_attr(level + 2, "Enable bit set");
if (test_bit(&mode, 2))
print_attr(level + 2, "Request bit set");
if (test_bit(&mode, 3))
print_attr(level + 2, "Report bit set");
if (test_bit(&mode, 4))
print_attr(level + 2, "Duration Mandatory set");
type = l_get_u8(data + 2);
if (type > 16 && type != 255) {
print_attr(level + 1, "Type: Invalid (%u)", type);
return;
}
print_attr(level + 1, "Type: %s", rrm_measurement_types[type]);
switch (type) {
case 5:
print_measurement_request_beacon(level + 1, data + 3, size - 3);
break;
}
}
static void print_measurement_report_beacon(unsigned int level,
const void *data,
uint16_t size)
{
uint8_t frame_info;
if (size < 26)
return;
print_attr(level, "Operating Class: %u", l_get_u8(data));
print_attr(level, "Channel: %u", l_get_u8(data + 1));
print_attr(level, "Start Time: %"PRIu64, l_get_le64(data + 2));
print_attr(level, "Duration: %u", l_get_le16(data + 10));
frame_info = l_get_u8(data + 12);
print_attr(level, "PHY Type: %u", bit_field(frame_info, 0, 7));
print_attr(level, "Frame Type: %u", test_bit(&frame_info, 7));
print_attr(level, "RCPI: %u", l_get_u8(data + 13));
print_attr(level, "RSNI: %u", l_get_u8(data + 14));
print_attr(level, "BSSID: "MAC, MAC_STR(((const uint8_t *)data + 15)));
print_attr(level, "Antenna ID: %u", l_get_u8(data + 21));
print_attr(level, "Parent TSF: %u", l_get_le32(data + 22));
}
static void print_measurement_report(unsigned int level, const char *label,
const void *data,
uint16_t size)
{
uint8_t mode;
uint8_t type;
print_attr(level, "Measurement Report");
if (size < 3)
return;
print_attr(level + 1, "Token: %u", l_get_u8(data));
mode = l_get_u8(data + 1);
print_attr(level + 1, "Report Mode: %u", mode);
if (test_bit(&mode, 0))
print_attr(level + 2, "Late bit set");
if (test_bit(&mode, 1))
print_attr(level + 2, "Incapable bit set");
if (test_bit(&mode, 2))
print_attr(level + 2, "Refused bit set");
type = l_get_u8(data + 2);
if (type > 16 && type != 255) {
print_attr(level + 1, "Type: Invalid (%u)", type);
return;
}
print_attr(level + 1, "Type: %s", rrm_measurement_types[type]);
switch (type) {
case 5:
print_measurement_report_beacon(level + 1, data + 3, size - 3);
break;
}
}
static struct attr_entry ie_entry[] = {
{ IE_TYPE_SSID, "SSID",
ATTR_CUSTOM, { .function = print_ie_ssid } },
{ IE_TYPE_SUPPORTED_RATES, "Supported rates",
ATTR_CUSTOM, { .function = print_ie_rate } },
{ IE_TYPE_DSSS_PARAMETER_SET, "DSSS parameter set",
ATTR_CUSTOM, { .function = print_ie_ds } },
{ IE_TYPE_TIM, "TIM",
ATTR_CUSTOM, { .function = print_ie_tim } },
{ IE_TYPE_COUNTRY, "Country",
ATTR_CUSTOM, { .function = print_ie_country } },
{ IE_TYPE_BSS_LOAD, "BSS load",
ATTR_CUSTOM, { .function = print_ie_bss_load } },
{ IE_TYPE_POWER_CONSTRAINT, "Power constraint",
ATTR_CUSTOM, { .function = print_ie_power_constraint } },
{ IE_TYPE_TPC_REPORT, "TPC report",
ATTR_CUSTOM, { .function = print_ie_tpc } },
{ IE_TYPE_ERP, "ERP Information",
ATTR_CUSTOM, { .function = print_ie_erp } },
{ IE_TYPE_RSN, "RSN",
ATTR_CUSTOM, { .function = print_ie_rsn } },
{ IE_TYPE_EXTENDED_SUPPORTED_RATES, "Extended supported rates",
ATTR_CUSTOM, { .function = print_ie_rate } },
{ IE_TYPE_HT_OPERATION, "HT Operation",
ATTR_CUSTOM, { .function = print_ie_ht_operation } },
{ IE_TYPE_VENDOR_SPECIFIC, "Vendor specific",
ATTR_CUSTOM, { .function = print_ie_vendor } },
{ IE_TYPE_EXTENDED_CAPABILITIES, "Extended Capabilities",
ATTR_CUSTOM, { .function = print_ie_extended_capabilities } },
{ IE_TYPE_HT_CAPABILITIES, "HT Capabilities",
ATTR_CUSTOM, { .function = print_ie_ht_capabilities } },
{ IE_TYPE_RM_ENABLED_CAPABILITIES, "RM Enabled Capabilities",
ATTR_CUSTOM, { .function = print_ie_rm_enabled_caps } },
{ IE_TYPE_INTERWORKING, "Interworking",
ATTR_CUSTOM, { .function = print_ie_interworking } },
{ IE_TYPE_ADVERTISEMENT_PROTOCOL, "Advertisement Protocol",
ATTR_CUSTOM, { .function = print_ie_advertisement } },
{ IE_TYPE_OWE_DH_PARAM, "OWE Diffie-Hellman Parameter",
ATTR_CUSTOM, { .function = print_ie_owe } },
{ IE_TYPE_FILS_INDICATION, "FILS Indication",
ATTR_CUSTOM, { .function = print_fils_indication } },
{ IE_TYPE_FILS_KEY_CONFIRMATION, "FILS Key Confirmation",
ATTR_CUSTOM, { .function = print_fils_key_confirmation } },
{ IE_TYPE_FILS_SESSION, "FILS Session",
ATTR_CUSTOM, { .function = print_fils_session } },
{ IE_TYPE_SUPPORTED_OPERATING_CLASSES, "Supported Operating Classes",
ATTR_CUSTOM,
{ .function = print_ie_supported_operating_classes } },
{ IE_TYPE_QOS_MAP_SET, "QoS Map",
ATTR_CUSTOM, { .function = print_qos_map } },
{ IE_TYPE_MEASUREMENT_REQUEST, "Measurement Request",
ATTR_CUSTOM, { .function = print_measurement_request } },
{ IE_TYPE_MEASUREMENT_REPORT, "Measurement Report",
ATTR_CUSTOM, { .function = print_measurement_report } },
{ },
};
static void print_ie(unsigned int level, const char *label,
const void *data, uint16_t size)
{
struct ie_tlv_iter iter;
int i;
print_attr(level, "%s: len %u", label, size);
ie_tlv_iter_init(&iter, data, size);
while (ie_tlv_iter_next(&iter)) {
uint16_t tag = ie_tlv_iter_get_tag(&iter);
struct attr_entry *entry = NULL;
for (i = 0; ie_entry[i].str; i++) {
if (ie_entry[i].attr == tag) {
entry = &ie_entry[i];
break;
}
}
if (cur_nlmon && cur_nlmon->noies && tag != IE_TYPE_SSID)
continue;
if (entry && entry->function)
entry->function(level + 1, entry->str,
iter.data, iter.len);
else
print_attr(level + 1, "Tag %u: len %u", tag,
iter.len);
print_hexdump(level + 2, iter.data, iter.len);
}
}
static void print_wsc_byte(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *bytes = data;
if (size != 1) {
printf("malformed packet\n");
return;
}
print_attr(level, "%s: %u", label, bytes[0]);
}
static void print_wsc_bytes(unsigned int level, const char *label,
const void *data, uint16_t size)
{
print_attr(level, "%s: (len: %d)", label, size);
print_hexdump(level + 1, data, size);
}
static void print_wsc_bool(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *bytes = data;
if (size != 1) {
printf("malformed packet\n");
return;
}
print_attr(level, "%s: %s", label, bytes[0] ? "True" : "False");
}
static void print_wsc_ascii_string(unsigned int level, const char *label,
const void *data, uint16_t size,
uint16_t max_len)
{
const char *p = data;
unsigned int i;
if (size >= max_len) {
printf("malformed packet\n");
return;
}
for (i = 0; i < size; i++) {
if (!p[i])
break;
if (!l_ascii_isprint(p[i]))
goto invalid_ascii;
}
print_attr(level, "%s: %.*s", label, i, p);
return;
invalid_ascii:
print_attr(level, "%s: (Non-Ascii, len: %d)", label, size);
print_hexdump(level + 1, data, size);
}
static void print_wsc_utf8_string(unsigned int level, const char *label,
const void *data, uint16_t size,
uint16_t max_len)
{
const char *p = data;
unsigned int i;
if (size >= max_len) {
printf("malformed packet\n");
return;
}
for (i = 0; i < size; i++) {
if (!p[i])
break;
}
if (!l_utf8_validate((const char *) p, i, NULL))
goto invalid_utf8;
print_attr(level, "%s: %.*s", label, i, p);
return;
invalid_utf8:
print_attr(level, "%s: (Non-utf8, len: %d)", label, size);
print_hexdump(level + 1, data, size);
}
static void print_wsc_uuid(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *bytes = data;
if (size != 16) {
printf("malformed packet\n");
return;
}
print_attr(level, "%s: %02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x-%02x%02x%02x%02x%02x%02x",
label,
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]);
}
static void print_wsc_association_state(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t state;
static const char *state_table[] = {
"Not Associated",
"Connection Success",
"Configuration Failure",
"Association Failure",
"IP Failure",
};
if (size != 2) {
printf("malformed packet\n");
return;
}
state = l_get_be16(data);
if (state > 4)
print_attr(level, "%s: Reserved", label);
else
print_attr(level, "%s: %s", label, state_table[state]);
}
static void print_wsc_auth_type_flags(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t v;
if (size != 2)
return;
v = l_get_be16(data);
print_attr(level, "%s:", label);
if (v & WSC_AUTHENTICATION_TYPE_OPEN)
print_attr(level + 1, "Open");
if (v & WSC_AUTHENTICATION_TYPE_WPA_PERSONAL)
print_attr(level + 1, "WPA-Personal");
if (v & WSC_AUTHENTICATION_TYPE_SHARED)
print_attr(level + 1, "Shared");
if (v & WSC_AUTHENTICATION_TYPE_WPA_ENTERPRISE)
print_attr(level + 1, "WPA-Enterprise");
if (v & WSC_AUTHENTICATION_TYPE_WPA2_ENTERPRISE)
print_attr(level + 1, "WPA2-Enterprise");
if (v & WSC_AUTHENTICATION_TYPE_WPA2_PERSONAL)
print_attr(level + 1, "WPA2-Personal");
if (v & 0xffc0)
print_attr(level + 1, "Unknown: %04x", v & 0xffc0);
}
static void print_wsc_authenticator(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const uint8_t *a = data;
if (size != 8)
return;
print_attr(level, "%s: %02x%02x%02x%02x%02x%02x%02x%02x",
label, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
}
static void print_wsc_configuration_error(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t error;
static const char *error_table[] = {
"No Error",
"OOB Interface Read Error",
"Decryption CRC Failure",
"2.4 channel not supported",
"5.0 channel not supported",
"Signal too weak",
"Network auth failure",
"Network association failure",
"No DHCP response",
"Failed DHCP config",
"IP Address conflict",
"Couldn't connect to Registrar",
"Multiple PBC sessions detected",
"Rogue activity suspected",
"Device busy",
"Setup locked",
"Message timeout",
"Registration session timeout",
"Device Password Auth Failure",
"60 Ghz channel not supported",
"Public Key Hash Mismatch",
};
if (size != 2) {
printf("malformed packet\n");
return;
}
error = l_get_be16(data);
if (error > 20)
print_attr(level, "%s: Reserved", label);
else
print_attr(level, "%s: %s", label, error_table[error]);
}
static void print_wsc_config_methods(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t v;
uint16_t flags;
if (size != 2) {
printf("malformed packet\n");
return;
}
v = l_get_be16(data);
print_attr(level, "%s:", label);
if ((v & WSC_CONFIGURATION_METHOD_PHYSICAL_DISPLAY_PIN) ==
WSC_CONFIGURATION_METHOD_PHYSICAL_DISPLAY_PIN)
print_attr(level + 1, "Physical Display PIN");
if ((v & WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN) ==
WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN)
print_attr(level + 1, "Virtual Display PIN");
flags = WSC_CONFIGURATION_METHOD_PHYSICAL_DISPLAY_PIN |
WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN;
if (v & flags)
v &= ~flags;
if (v & WSC_CONFIGURATION_METHOD_P2P) {
print_attr(level + 1, "P2P");
v &= ~WSC_CONFIGURATION_METHOD_P2P;
}
if ((v & WSC_CONFIGURATION_METHOD_PHYSICAL_PUSH_BUTTON) ==
WSC_CONFIGURATION_METHOD_PHYSICAL_PUSH_BUTTON)
print_attr(level + 1, "Physical PushButton");
if ((v & WSC_CONFIGURATION_METHOD_VIRTUAL_PUSH_BUTTON) ==
WSC_CONFIGURATION_METHOD_VIRTUAL_PUSH_BUTTON)
print_attr(level + 1, "Virtual PushButton");
flags = WSC_CONFIGURATION_METHOD_PHYSICAL_PUSH_BUTTON |
WSC_CONFIGURATION_METHOD_VIRTUAL_PUSH_BUTTON;
if (v & flags)
v &= ~flags;
if (v & WSC_CONFIGURATION_METHOD_KEYPAD) {
print_attr(level + 1, "Keypad");
v &= ~WSC_CONFIGURATION_METHOD_KEYPAD;
}
if (v & WSC_CONFIGURATION_METHOD_PUSH_BUTTON) {
print_attr(level + 1, "PushButton");
v &= ~WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
}
if (v & WSC_CONFIGURATION_METHOD_NFC_INTERFACE) {
print_attr(level + 1, "NFC Interface");
v &= ~WSC_CONFIGURATION_METHOD_NFC_INTERFACE;
}
if (v & WSC_CONFIGURATION_METHOD_INTEGRATED_NFC_TOKEN) {
print_attr(level + 1, "Integrated NFC Token");
v &= ~WSC_CONFIGURATION_METHOD_INTEGRATED_NFC_TOKEN;
}
if (v & WSC_CONFIGURATION_METHOD_EXTERNAL_NFC_TOKEN) {
print_attr(level + 1, "External NFC Token");
v &= ~WSC_CONFIGURATION_METHOD_EXTERNAL_NFC_TOKEN;
}
if (v & WSC_CONFIGURATION_METHOD_DISPLAY) {
print_attr(level + 1, "Display");
v &= ~WSC_CONFIGURATION_METHOD_DISPLAY;
}
if (v & WSC_CONFIGURATION_METHOD_LABEL) {
print_attr(level + 1, "Label");
v &= ~WSC_CONFIGURATION_METHOD_LABEL;
}
if (v & WSC_CONFIGURATION_METHOD_ETHERNET) {
print_attr(level + 1, "Ethernet");
v &= ~WSC_CONFIGURATION_METHOD_ETHERNET;
}
if (v & WSC_CONFIGURATION_METHOD_USBA) {
print_attr(level + 1, "USBA");
v &= ~WSC_CONFIGURATION_METHOD_USBA;
}
if (v)
print_attr(level + 1, "Unknown: %04x", v);
}
static void print_wsc_connection_type_flags(unsigned int level,
const char *label,
const void *data, uint16_t size)
{
uint8_t v;
if (size != 1)
return;
v = *((uint8_t *) data);
print_attr(level, "%s:", label);
if (v & WSC_CONNECTION_TYPE_ESS)
print_attr(level + 1, "ESS");
if (v & WSC_CONNECTION_TYPE_IBSS)
print_attr(level + 1, "IBSS");
if (v & 0xfffc)
print_attr(level + 1, "Unknown: %04x", v & 0xfffc);
}
static void print_wsc_device_name(unsigned int level, const char *label,
const void *data, uint16_t size)
{
print_wsc_utf8_string(level, label, data, size, 32);
}
static void print_wsc_device_password_id(unsigned int level, const char *label,
const void *data, uint16_t size)
{
uint16_t v;
static const char *device_password_id_table[] = {
"Default (PIN)",
"User-specified",
"Machine-specified",
"Rekey",