}
}
+static char *dfs_state_name(enum nl80211_dfs_state state)
+{
+ switch (state) {
+ case NL80211_DFS_USABLE:
+ return "usable";
+ case NL80211_DFS_AVAILABLE:
+ return "available";
+ case NL80211_DFS_UNAVAILABLE:
+ return "unavailable";
+ default:
+ return "unknown";
+ }
+}
+
static int print_phy_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct nlattr *nl_mode;
struct nlattr *nl_cmd;
struct nlattr *nl_if, *nl_ftype;
- int bandidx = 1;
int rem_band, rem_freq, rem_rate, rem_mode, rem_cmd, rem_ftype, rem_if;
int open;
+ /*
+ * static variables only work here, other applications need to use the
+ * callback pointer and store them there so they can be multithreaded
+ * and/or have multiple netlink sockets, etc.
+ */
+ static int64_t phy_id = -1;
+ static int last_band = -1;
+ static bool band_had_freq = false;
+ bool print_name = true;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
- if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
- return NL_SKIP;
-
- if (tb_msg[NL80211_ATTR_WIPHY_NAME])
+ if (tb_msg[NL80211_ATTR_WIPHY]) {
+ if (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]) == phy_id)
+ print_name = false;
+ else
+ last_band = -1;
+ phy_id = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
+ }
+ if (print_name && tb_msg[NL80211_ATTR_WIPHY_NAME])
printf("Wiphy %s\n", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]));
- nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
- printf("\tBand %d:\n", bandidx);
- bandidx++;
+ /* needed for split dump */
+ if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
+ nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+ if (last_band != nl_band->nla_type) {
+ printf("\tBand %d:\n", nl_band->nla_type + 1);
+ band_had_freq = false;
+ }
+ last_band = nl_band->nla_type;
- nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
- nla_len(nl_band), NULL);
+ nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+ nla_len(nl_band), NULL);
-#ifdef NL80211_BAND_ATTR_HT_CAPA
- if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
- __u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
- print_ht_capability(cap);
- }
- if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
- __u8 exponent = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
- print_ampdu_length(exponent);
- }
- if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
- __u8 spacing = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
- print_ampdu_spacing(spacing);
- }
- if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
- nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16)
- print_ht_mcs(nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]));
-#endif
-
- printf("\t\tFrequencies:\n");
-
- nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
- uint32_t freq;
- nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
- nla_len(nl_freq), freq_policy);
- if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
- continue;
- freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
- printf("\t\t\t* %d MHz [%d]", freq, ieee80211_frequency_to_channel(freq));
-
- if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
- !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
- printf(" (%.1f dBm)", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
-
- open = 0;
- if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
- print_flag("disabled", &open);
- goto next;
+ if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
+ __u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
+ print_ht_capability(cap);
+ }
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
+ __u8 exponent = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
+ print_ampdu_length(exponent);
+ }
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
+ __u8 spacing = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
+ print_ampdu_spacing(spacing);
+ }
+ if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
+ nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16)
+ print_ht_mcs(nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]));
+ if (tb_band[NL80211_BAND_ATTR_VHT_CAPA] &&
+ tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])
+ print_vht_info(nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]),
+ nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]));
+
+ if (tb_band[NL80211_BAND_ATTR_FREQS]) {
+ if (!band_had_freq) {
+ printf("\t\tFrequencies:\n");
+ band_had_freq = true;
+ }
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ uint32_t freq;
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+ freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+ printf("\t\t\t* %d MHz [%d]", freq, ieee80211_frequency_to_channel(freq));
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+ !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ printf(" (%.1f dBm)", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
+
+ open = 0;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
+ print_flag("disabled", &open);
+ goto next;
+ }
+ if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+ print_flag("passive scanning", &open);
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
+ print_flag("no IBSS", &open);
+ if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+ print_flag("radar detection", &open);
+next:
+ if (open)
+ printf(")");
+ printf("\n");
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+ enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+ unsigned long time;
+
+ printf("\t\t\t DFS state: %s", dfs_state_name(state));
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
+ time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
+ printf(" (for %lu sec)", time/1000);
+ }
+ printf("\n");
+ }
+ }
}
- if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
- print_flag("passive scanning", &open);
- if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
- print_flag("no IBSS", &open);
- if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
- print_flag("radar detection", &open);
- next:
- if (open)
- printf(")");
- printf("\n");
- }
- printf("\t\tBitrates (non-HT):\n");
-
- nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
- nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
- nla_len(nl_rate), rate_policy);
- if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
- continue;
- printf("\t\t\t* %2.1f Mbps", 0.1 * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]));
- open = 0;
- if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE])
- print_flag("short preamble supported", &open);
- if (open)
- printf(")");
- printf("\n");
+ if (tb_band[NL80211_BAND_ATTR_RATES]) {
+ printf("\t\tBitrates (non-HT):\n");
+ nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+ nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+ nla_len(nl_rate), rate_policy);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ printf("\t\t\t* %2.1f Mbps", 0.1 * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]));
+ open = 0;
+ if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE])
+ print_flag("short preamble supported", &open);
+ if (open)
+ printf(")");
+ printf("\n");
+ }
+ }
}
}
[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+ [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
};
struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
}
printf(",\n\t\t ");
- printf("total <= %d, #channels <= %d%s\n",
+ printf("total <= %d, #channels <= %d%s",
nla_get_u32(tb_comb[NL80211_IFACE_COMB_MAXNUM]),
nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]),
tb_comb[NL80211_IFACE_COMB_STA_AP_BI_MATCH] ?
", STA/AP BI must match" : "");
+ if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) {
+ unsigned long widths = nla_get_u32(tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]);
+
+ if (widths) {
+ int width;
+ bool first = true;
+
+ printf(", radar detect widths: {");
+ for (width = 0; width < 32; width++)
+ if (widths & (1 << width)) {
+ printf("%s %s",
+ first ? "":",",
+ channel_width_name(width));
+ first = false;
+ }
+ printf(" }\n");
+ }
+ }
+ printf("\n");
broken_combination:
;
}
[NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
- [NL80211_WOWLAN_TRIG_PKT_PATTERN] = {
- .minlen = sizeof(struct nl80211_wowlan_pattern_support),
- },
+ [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .minlen = 12 },
[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
};
struct nl80211_wowlan_pattern_support *pat;
int err;
printf("\t\t * wake up on magic packet\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
pat = nla_data(tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]);
- printf("\t\t * wake up on pattern match, up to %u patterns of %u-%u bytes\n",
- pat->max_patterns, pat->min_pattern_len, pat->max_pattern_len);
+ printf("\t\t * wake up on pattern match, up to %u patterns of %u-%u bytes,\n"
+ "\t\t maximum packet offset %u bytes\n",
+ pat->max_patterns, pat->min_pattern_len, pat->max_pattern_len,
+ (nla_len(tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]) <
+ sizeof(*pat)) ? 0 : pat->max_pkt_offset);
}
if (tb_wowlan[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
printf("\t\t * can do GTK rekeying\n");
printf("\t\t * wake up on 4-way handshake\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
printf("\t\t * wake up on rfkill release\n");
+ if (tb_wowlan[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
+ printf("\t\t * wake up on TCP connection\n");
}
}
enum id_input id)
{
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_phy_handler, NULL);
+ /* kernels not supporting it will ignore this */
+ nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
return 0;
}
-__COMMAND(NULL, info, "info", NULL, NL80211_CMD_GET_WIPHY, 0, 0, CIB_PHY, handle_info,
+__COMMAND(NULL, info, "info", NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, 0, CIB_PHY, handle_info,
"Show capabilities for the specified wireless device.", NULL);
TOPLEVEL(list, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info,
"List all wireless devices and their capabilities.");
TOPLEVEL(phy, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info, NULL);
+
+static int handle_commands(struct nl80211_state *state,
+ struct nl_cb *cb, struct nl_msg *msg,
+ int argc, char **argv, enum id_input id)
+{
+ int i;
+ for (i = 1; i < NL80211_CMD_MAX; i++)
+ printf("%d (0x%x): %s\n", i, i, command_name(i));
+ /* don't send netlink messages */
+ return 2;
+}
+TOPLEVEL(commands, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_NONE, handle_commands,
+ "list all known commands and their decimal & hex value");
+
+static int print_feature_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) {
+ uint32_t feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+ printf("nl80211 features: 0x%x\n", feat);
+ if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+ printf("\t* split wiphy dump\n");
+ }
+
+ return NL_SKIP;
+}
+
+static int handle_features(struct nl80211_state *state,
+ struct nl_cb *cb, struct nl_msg *msg,
+ int argc, char **argv, enum id_input id)
+{
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_feature_handler, NULL);
+ return 0;
+}
+
+TOPLEVEL(features, NULL, NL80211_CMD_GET_PROTOCOL_FEATURES, 0, CIB_NONE,
+ handle_features, "");