Hello.
Attached is a first pass of adding 802.11ax decode to iw scan.c It's a little messy still. Posting here to check if there is any interest in my continuing to add the decode to 'iw'. If so, please let me know how I can get the code up to standard.
Here is the output so far: https://gist.github.com/linuxlizard/45914f588d49fd077205c851c62fa013
I don't have the 802.11ax spec so I'm using Wireshark's AX decode to guide me. Also sanity checking with WiFi Explorer (MacOS). I've tried to make the correct attributes in ie_he.[ch] but could also use feedback in that area as well.
I've been testing with an ASUS RT_AX88U, a Netgear Nighthawk RAX80 (both with Broadcom chips), and a couple Qualcomm 807x devices. For another test, I've captured the nlattr to a file where I can rebuild the nlmsg, feed it back into scan.c.
Thanks,
David
--
David Poole | Firmware Engineer | Cradlepoint | dpoole@xxxxxxxxxxxxxxx
From 45a369ccda08211ddf123ed99b76abb698937539 Mon Sep 17 00:00:00 2001
From: David Poole <dpoole@xxxxxxxxxxxxxxx>
Date: Sun, 19 Jan 2020 10:59:31 -0700
Subject: [PATCH] work in progress: first pass adding 802.11ax
Signed-off-by: David Poole <dpoole@xxxxxxxxxxxxxxx>
---
Makefile | 8 +-
ie_he.c | 706 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ie_he.h | 236 ++++++++++++++++++
scan.c | 77 +++++-
test_scan.c | 213 ++++++++++++++++
5 files changed, 1236 insertions(+), 4 deletions(-)
create mode 100644 ie_he.c
create mode 100644 ie_he.h
create mode 100644 test_scan.c
diff --git a/Makefile b/Makefile
index 90f2251..631b9e9 100644
--- a/Makefile
+++ b/Makefile
@@ -13,13 +13,13 @@ cc-option = $(shell set -e ; $(CC) $(1) -c -x c /dev/null -o /dev/null >/dev/nul
CFLAGS_EVAL := $(call cc-option,-Wstringop-overflow=4)
-CFLAGS ?= -O2 -g
+CFLAGS = -g
CFLAGS += -Wall -Wextra -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration -Wsign-compare -Wno-unused-parameter \
$(CFLAGS_EVAL)
_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
-VERSION_OBJS := $(filter-out version.o, $(_OBJS))
+VERSION_OBJS := $(filter-out version.o test_scan.o, $(_OBJS))
OBJS := $(VERSION_OBJS) version.o
ALL = iw
@@ -108,6 +108,10 @@ iw: $(OBJS)
$(Q)$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o iw
endif
+test_scan: test_scan.o scan.o util.o version.o ie_he.o
+ @$(NQ) ' CC ' test_scan
+ $(Q)$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
+
check:
$(Q)$(MAKE) all CC="REAL_CC=$(CC) CHECK=\"sparse -Wall\" cgcc"
diff --git a/ie_he.c b/ie_he.c
new file mode 100644
index 0000000..589bcd1
--- /dev/null
+++ b/ie_he.c
@@ -0,0 +1,706 @@
+/* Uses chunks of Wireshark, specifically the strings from packet-ieee80211.c
+ *
+ * So this file needs to be carefully aligned with Wireshark's license.
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "iw.h"
+#include "ie_he.h"
+
+int ie_he_capabilities_init(struct IE_HE_Capabilities* ie, const uint8_t* data, uint8_t len)
+{
+ // I don't know officially how big this IE is (PPE is variable length, one
+ // entry per SS (Spatial Stream). Everything forward of PPE seems fixed length
+ // 1 byte exten id
+ // 26 bytes of data, min
+ // TODO I'm seeing 26 bytes sometimes and it's driving me crazy
+ if (len < 26) {
+ fprintf(stderr, "%s len=%u expected 26\n", __func__, len);
+ return -EINVAL;
+ }
+
+ const uint8_t* ptr = data;
+ // skip the Extension Id
+ ptr++;
+ // point into the ie buf
+ ie->mac_capa = ptr;
+ ptr += IE_HE_CAPA_MAC_SIZE;
+ ie->phy_capa = ptr;
+ ptr += IE_HE_CAPA_PHY_SIZE;
+ ie->mcs_and_nss_set = ptr;
+ ptr += IE_HE_CAPA_MCS_SIZE;
+ ie->ppe_threshold = ptr;
+
+ ie->mac = (const struct IE_HE_MAC*)ie->mac_capa;
+ ie->phy = (const struct IE_HE_PHY*)ie->phy_capa;
+
+ return 0;
+}
+
+int ie_he_operation_init(struct IE_HE_Operation* ie, const uint8_t* data, uint8_t len)
+{
+ // 1 byte for the extension ID
+ // 6 bytes for the payload
+ if (len != 6) {
+ return -EINVAL;
+ }
+
+ ie->params = data;
+ ie->bss_color = data + 4;
+ ie->mcs_and_nss_set = data + 5;
+
+ ie->fields = (struct IE_HE_Operation_Fields*)data;
+
+ return 0;
+}
+
+// from packet-ieee80211.c function max_frag_msdus_base_custom()
+int he_max_frag_msdus_base_to_str(uint8_t max_frag_msdus_value, char* s, size_t len)
+{
+ if (max_frag_msdus_value == 7)
+ return snprintf(s, len, "No restriction");
+ else
+ return snprintf( s, len, "%u", 1 << max_frag_msdus_value);
+}
+
+static const char* he_mac_cap_str[] = {
+ "+HTC HE Support", // htc_he_support
+ "TWT Requester Support", // twt_req_support
+ "TWT Responder Support", // twt_rsp_support
+ "Fragmentation Support", // fragmentation_support
+ NULL,
+ "Maximum Number of Fragmented MSDUs", // max_frag_msdus
+ NULL,
+ NULL,
+
+ "Minimum Fragment Size", // min_frag_size
+ NULL,
+ "Trigger Frame MAC Padding Duration", // trig_frm_mac_padding_dur
+ NULL,
+ "Multi-TID Aggregation Support", // multi_tid_agg_support
+ NULL,
+ NULL,
+
+ "HE Link Adaptation Support", // he_link_adaptation_support
+ NULL,
+ "All Ack Support", // all_ack_support
+ "TRS Support", // trs_support
+ "BSR Support", // bsr_support
+ "Broadcast TWT Support", // broadcast_twt_support
+ "32-bit BA Bitmap Support", // 32_bit_ba_bitmap_support
+ "MU Cascading Support", // mu_cascading_support
+ "Ack-Enabled Aggregation Support", // ack_enabled_agg_support
+
+ "Reserved", // reserved_b24
+ "OM Control Support", // om_control_support
+ "OFDMA RA Support", // ofdma_ra_support
+ "Maximum A-MPDU Length Exponent Extension", // max_a_mpdu_len_exp_ext
+ NULL,
+ "A-MSDU Fragmentation Support", // a_msdu_frag_support
+ "Flexible TWT Schedule Support", // flexible_twt_sched_support
+ "Rx Control Frame to MultiBSS", // rx_ctl_frm_multibss
+
+ "BSRP BQRP A-MPDU Aggregation", // bsrp_bqrp_a_mpdu_agg
+ "QTP Support", // qtp_support
+ "BQR Support", // bqr_support
+ "SRP Responder Role", // sr_responder
+ "NDP Feedback Report Support", // ndp_feedback_report_support
+ "OPS Support", // ops_support
+ "A-MSDU in A-MPDU Support", // a_msdu_in_a_mpdu_support
+ "Multi-TID Aggregation TX Support", // multi_tid_agg_support
+ NULL,
+ NULL,
+
+ "HE Subchannel Selective Transmission Support", // subchannel_selective_xmit_support
+ "UL 2x996-tone RU Support", // ul_2_996_tone_ru_support
+ "OM Control UL MU Data Disable RX Support", // om_cntl_ul_mu_data_disable_rx_support
+ "HE Dynamic SM Power Save", // he_dynamic_sm_power_save
+ "Punctured Sounding Support", // he_punctured_sounding_support
+ "HT And VHT Trigger Frame RX Support", // he_ht_and_vht_trigger_frame_rx_support
+};
+
+static const char* he_phy_cap_str[] = {
+ // bits 0-7
+ NULL,
+ "40MHz in 2.4GHz band", // 40mhz_in_2_4ghz
+ "40 & 80MHz in the 5GHz band", // 40_80_in_5ghz
+ "160MHz in the 5GHz band", // 160_in_5ghz
+ "160/80+80MHz in the 5GHz band", // 160_80_80_in_5ghz
+ "242 tone RUs in the 2.4GHz band", // 242_tone_in_2_4ghz
+ "242 tone RUs in the 5GHz band", // 242_tone_in_5ghz
+ NULL,
+
+ // bits 8-23
+ "Punctured Preamble RX", // punc_preamble_rx 4-bits
+ NULL,
+ NULL,
+ NULL,
+ "Device Class", // device_class
+ "LDPC Coding In Payload", // ldpc_coding_in_payload
+ "HE SU PPDU With 1x HE-LTF and 0.8us GI", // he_su_ppdu_with_1x_he_ltf_08us
+ "Midamble Rx Max NSTS", // midamble_rx_max_nsts 2-bits
+ NULL,
+ "NDP With 4x HE-LTF and 3.2us GI", // 2us
+ "STBC Tx <= 80 MHz", // stbc_tx_lt_80mhz
+ "STBC Rx <= 80 MHz", // stbc_rx_lt_80mhz
+ "Doppler Tx", // doppler_tx
+ "Doppler Rx", // doppler_rx
+ "Full Bandwidth UL MU-MIMO", // full_bw_ul_mu_mimo
+ "Partial Bandwidth UL MU-MIMO", // partial_bw_ul_mu_mimo
+
+ "DCM Max Constellation Tx", // dcm_max_const_tx 2-bits
+ NULL,
+ "DCM Max NSS Tx", // dcm_max_nss_tx
+ "DCM Max Constellation Rx", // dcm_max_const_rx 2-bits
+ NULL,
+ "DCM Max NSS Rx", // dcm_max_nss_tx
+ "Rx HE MU PPDU from Non-AP STA", // rx_he_mu_ppdu
+ "SU Beamformer", // su_beamformer
+ "SU Beamformee", // su_beamformee
+ "MU Beamformer", // mu_beamformer
+ "Beamformee STS <= 80 MHz", // beamformee_sts_lte_80mhz 3-bits
+ NULL,
+ NULL,
+ "Beamformee STS > 80 MHz", // beamformee_sts_gt_80mhz 3-bits
+ NULL,
+ NULL,
+
+ "Number Of Sounding Dimensions <= 80 MHz", // no_sounding_dims_lte_80 3-bits
+ NULL,
+ NULL,
+ "Number Of Sounding Dimensions > 80 MHz", // no_sounding_dims_gt_80 3-bits
+ NULL,
+ NULL,
+ "Ng = 16 SU Feedback", // ng_eq_16_su_fb
+ "Ng = 16 MU Feedback", // ng_eq_16_mu_fb
+ "Codebook Size SU Feedback", // codebook_size_su_fb
+ "Codebook Size MU Feedback", // codebook_size_mu_fb
+ "Triggered SU Beamforming Feedback", // trig_su_bf_fb
+ "Triggered MU Beamforming Feedback", // trig_mu_bf_fb
+ "Triggered CQI Feedback", // trig_cqi_fb
+ "Partial Bandwidth Extended Range", // partial_bw_er
+ "Partial Bandwidth DL MU-MIMO", // partial_bw_dl_mu_mimo
+ "PPE Threshold Present", // ppe_thres_present
+
+ "SRP-based SR Support", // srp_based_sr_sup
+ "Power Boost Factor ar Support", // pwr_bst_factor_ar_sup
+ "HE SU PPDU & HE MU PPDU w 4x HE-LTF & 0.8us GI", // he_su_ppdu_etc_gi
+ "Max Nc", // max_nc 3-bits
+ NULL,
+ NULL,
+ "STBC Tx > 80 MHz", // stbc_tx_gt_80_mhz
+ "STBC Rx > 80 MHz", // stbc_rx_gt_80_mhz
+ "HE ER SU PPDU W 4x HE-LTF & 0.8us GI", // he_er_su_ppdu_4xxx_gi
+ "20 MHz In 40 MHz HE PPDU In 2.4GHz Band", // 20_mhz_in_40_in_2_4ghz
+ "20 MHz In 160/80+80 MHz HE PPDU", // 20_mhz_in_160_80p80_ppdu
+ "80 MHz In 160/80+80 MHz HE PPDU", // 80_mhz_in_160_80p80_ppdu
+ "HE ER SU PPDU W 1x HE-LTF & 0.8us GI", // he_er_su_ppdu_1xxx_gi
+ "Midamble Rx 2x & 1x HE-LTF", // midamble_rx_2x_1x_he_ltf
+ "DCM Max BW", // dcm_max_bw 2-bits
+ NULL,
+
+ "Longer Than 16 HE SIG-B OFDM Symbols Support", // longer_than_16_he_sigb_ofdm_sym_support
+ "Non-Triggered CQI Feedback", // non_triggered_feedback
+ "Tx 1024-QAM Support < 242-tone RU", // tx_1024_qam_support_lt_242_tone_ru
+ "Rx 1024-QAM Support < 242-tone RU", // rx_1024_qam_support_lt_242_tone_ru
+ "Rx Full BW SU Using HE MU PPDU With Compressed SIGB", // rx_full_bw_su_using_he_mu_ppdu_with_compressed_sigb
+ "Rx Full BW SU Using HE MU PPDU With Non-Compressed SIGB", // rx_full_bw_su_using_he_mu_ppdu_with_non_compressed_sigb
+ "Nominal Packet Padding", // nominal_packet_padding 2-bits
+ NULL,
+};
+
+// packet-ieee80211.c array of same name
+static const char* he_fragmentation_support_vals[] = {
+ "No support for dynamic fragmentation",
+ "Support for dynamic fragments in MPDUs or S-MPDUs",
+ "Support for dynamic fragments in MPDUs and S-MPDUs and up to 1 dyn frag in MSDUs...",
+ "Support for all types of dynamic fragments",
+};
+
+// from packet-ieee80211.c array of same name
+static const char* he_link_adaptation_support_vals[] = {
+ "No feedback if the STA does not provide HE MFB",
+ "Reserved",
+ "Unsolicited if the STA can receive and provide only unsolicited HE MFB",
+ "Both",
+};
+
+// from packet-ieee80211.c array of same name
+static const char* he_minimum_fragmentation_size_vals[] = {
+ "No restriction on minimum payload size",
+ "Minimum payload size of 128 bytes",
+ "Minimum payload size of 256 bytes",
+ "Minimum payload size of 512 bytes",
+};
+
+int he_mac_capa_to_str(const struct IE_HE_MAC* mac, unsigned int idx, char* s, size_t len)
+{
+
+ if (idx >= ARRAY_SIZE(he_mac_cap_str)){
+ return -EINVAL;
+ }
+
+ if (!he_mac_cap_str[idx]) {
+ return -ENOENT;
+ }
+
+ char tmpstr[32];
+ int ret;
+
+ switch (idx) {
+ case 3: // 4
+ // fragmentation support
+ return snprintf(s, len, "%s: %s (%d)",
+ he_mac_cap_str[idx],
+ he_fragmentation_support_vals[mac->fragmentation_support],
+ mac->fragmentation_support);
+
+ case 5: // 6 7
+ // max frag msdu
+ ret = he_max_frag_msdus_base_to_str(mac->max_number_fragmented_msdus, tmpstr, sizeof(tmpstr));
+ if (ret < 0 ) {
+ return ret;
+ }
+ return snprintf(s, len, "%s: %s",
+ he_mac_cap_str[idx], tmpstr);
+
+ case 8: // 9
+ return snprintf(s, len, "%s: %s (%d)",
+ he_mac_cap_str[idx],
+ he_minimum_fragmentation_size_vals[mac->min_fragment_size],
+ mac->min_fragment_size);
+
+ case 10: // 11
+ // min trigger frame mac
+ return snprintf(s, len, "%s (%d)",
+ he_mac_cap_str[idx],
+ mac->trigger_frame_mac_padding_dur);
+
+ case 12: // 13 14
+ // multi tid
+ return snprintf(s, len, "%s: %d",
+ he_mac_cap_str[idx],
+ mac->multi_tid_aggregation_support);
+
+ case 15: // 16
+ // he link adaptation
+ return snprintf(s, len, "%s: %s (%d)",
+ he_mac_cap_str[idx],
+ he_link_adaptation_support_vals[mac->he_link_adaptation_support],
+ mac->he_link_adaptation_support);
+
+ case 27: // 28
+ // max ampdu len exponent exten
+ return snprintf(s, len, "%s (%d)",
+ he_mac_cap_str[idx], mac->max_a_mpdu_length_exponent_ext);
+
+ case 39: // 40 41
+ // multi-tid agg support
+ return snprintf(s, len, "%s (%d)",
+ he_mac_cap_str[idx], mac->multi_tid_aggregation_support);
+
+ default:
+ break;
+ }
+ return snprintf(s, len, "%s", he_mac_cap_str[idx]);
+}
+
+static const char* he_phy_device_class_vals[] = {
+ "Class A Device",
+ "Class B Device",
+};
+
+static const char* he_phy_midamble_rx_max_nsts_vals[] = {
+ "1 Space-Time Stream",
+ "2 Space-Time Streams",
+ "3 Space-Time Streams",
+ "4 Space-Time Streams",
+};
+
+static const char* he_phy_dcm_max_constellation_vals[] = {
+ "DCM is not supported",
+ "BPSK",
+ "QPSK",
+ "16-QAM",
+};
+
+static const char* he_phy_dcm_max_nss_vals[] = {
+ "1 Space-Time Stream",
+ "2 Space-Time Streams",
+};
+
+static const char* he_phy_nominal_packet_padding_vals[] = {
+ "0 µs for all Constellations",
+ "8 µs for all Constellations",
+ "16 µs for all Constellations",
+ "Reserved",
+};
+
+int he_phy_capa_to_str(const struct IE_HE_PHY* phy, unsigned int idx, char* s, size_t len)
+{
+
+ if (idx >= ARRAY_SIZE(he_phy_cap_str)){
+ return -EINVAL;
+ }
+
+ if (!he_phy_cap_str[idx]) {
+ return -ENOENT;
+ }
+
+ switch(idx)
+ {
+ case 8:
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->punctured_preamble_rx);
+
+ case 12:
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_device_class_vals[phy->device_class],
+ phy->device_class);
+
+ case 15: // 16
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_midamble_rx_max_nsts_vals[phy->midamble_rx_max_nsts],
+ phy->midamble_rx_max_nsts);
+
+ case 24: // 25
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_dcm_max_constellation_vals[phy->dcm_max_constellation_tx],
+ phy->dcm_max_constellation_tx);
+
+ case 26:
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_dcm_max_nss_vals[phy->dcm_max_nss_tx],
+ phy->dcm_max_nss_tx);
+
+ case 27: // 28
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_dcm_max_constellation_vals[phy->dcm_max_constellation_rx],
+ phy->dcm_max_constellation_rx);
+
+ case 29:
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_dcm_max_nss_vals[phy->dcm_max_nss_rx],
+ phy->dcm_max_nss_rx);
+
+ case 34: // 35 36
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->beamformer_sts_lte_80mhz);
+
+ case 37: // 38 39
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->beamformer_sts_gt_80mhz);
+
+ case 40: // 41 42
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->number_of_sounding_dims_lte_80);
+
+ case 43: // 44 45
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->number_of_sounding_dims_gt_80);
+
+ case 59: // 60 61
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->max_nc);
+
+ case 70: // 71
+ return snprintf(s, len, "%s: %d",
+ he_phy_cap_str[idx],
+ phy->dcm_max_bw);
+
+ case 78: // 79
+ return snprintf(s, len, "%s: %s (%d)",
+ he_phy_cap_str[idx],
+ he_phy_nominal_packet_padding_vals[phy->nominal_packet_padding],
+ phy->nominal_packet_padding);
+
+ default:
+ break;
+ }
+
+ return snprintf(s, len, "%s", he_phy_cap_str[idx]);
+}
+
+static const char* he_operation_str[] = {
+ "Default PE Duration", // 3 bits
+ NULL, NULL,
+ "TWT Required",
+ "TXOP Duration RTS Threshold", // 10 bits
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "VHT Operation Information Present",
+ "Co-located BSS",
+ "ER SU Disable",
+ // 7 bits reserved
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ // bits 24-31
+ "BSS Color", // 6 bits
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "Partial BSS Color",
+ "BSS Color Disabled",
+};
+
+int he_operation_to_str(const struct IE_HE_Operation* ie, unsigned int idx, char* s, size_t len)
+{
+ if (idx >= ARRAY_SIZE(he_operation_str)){
+ return -EINVAL;
+ }
+
+ if (!he_operation_str[idx]) {
+ return -ENOENT;
+ }
+
+ switch (idx) {
+ // default PE duration
+ case 0:
+ return snprintf(s, len, "%s: %d",
+ he_operation_str[idx],
+ ie->fields->default_pe_duration);
+
+ case 3:
+ return snprintf(s, len, "%s: %s",
+ he_operation_str[idx],
+ ie->fields->twt_required ? "Required" : "Not required" );
+
+ case 4:
+ return snprintf(s, len, "%s: %d",
+ he_operation_str[idx],
+ ie->fields->txop_duration_rts_thresh);
+
+ case 24:
+ // bss color
+ return snprintf(s, len, "%s: 0x%02x",
+ he_operation_str[idx],
+ ie->fields->bss_color);
+
+ default:
+ break;
+ }
+
+ return snprintf(s, len, "%s", he_operation_str[idx]);
+}
+
+void ie_print_he_capabilities(const struct IE_HE_Capabilities* ie)
+{
+ printf("\tHE capabilities:\n");
+ ie_print_he_capabilities_mac(ie->mac);
+ ie_print_he_capabilities_phy(ie->phy);
+}
+
+#define INDENT "\t\t\t"
+
+#define PRN(field, _idx)\
+ do {\
+ typeof (_idx) s_idx = (_idx);\
+ ret = STRING_FN(_struct, s_idx, s, sizeof(s));\
+ printf(INDENT " * %s\n", s);\
+ } while(0);
+
+#define PRNBOOL(field, _idx) \
+ do {\
+ typeof (_idx) s_idx = (_idx);\
+ if(_struct->field) {\
+ ret = STRING_FN(_struct, s_idx, s, sizeof(s));\
+ printf(INDENT " * %s\n", s);\
+ }\
+ } while(0);
+
+void ie_print_he_capabilities_mac(const struct IE_HE_MAC* mac)
+{
+ char s[128];
+ unsigned int bit;
+ int ret;
+
+#define STRING_FN he_mac_capa_to_str
+#define _struct mac
+
+ printf("\t\tHE MAC capabilities:\n");
+ // 0-7
+ bit = 0;
+ PRNBOOL(htc_he_support, bit++)
+ PRNBOOL(twt_requester_support,bit++)
+ PRNBOOL(twt_responder_support,bit++)
+ PRN(fragmentation_support,bit); bit+=2; // 2 bits
+ PRN(max_number_fragmented_msdus, bit); bit+=3; // 3 bits
+
+ // 8-14
+ PRN(min_fragment_size, bit); bit+=2; // 2 bits
+ PRN(trigger_frame_mac_padding_dur, bit); bit+=2;
+ PRN(multi_tid_aggregation_support, bit); bit+=3;
+ // bits 15,16
+ PRN(he_link_adaptation_support, bit); bit+=2;
+
+ // bits 16-23
+ bit = 17; // first field is at bit1
+ PRNBOOL(all_ack_support,bit++)
+ PRNBOOL(trs_support,bit++)
+ PRNBOOL(bsr_support,bit++)
+ PRNBOOL(broadcast_twt_support,bit++)
+ PRNBOOL(_32_bit_ba_bitmap_support,bit++)
+ PRNBOOL(mu_cascading_support,bit++)
+ PRNBOOL(ack_enabled_aggregation_support,bit++)
+
+ // bits 24-31
+ bit = 25; // bit 24 reserved
+ PRNBOOL(om_control_support,bit++)
+ PRNBOOL(ofdma_ra_support,bit++)
+ PRN(max_a_mpdu_length_exponent_ext,bit ); bit += 2; // 2 bits
+ PRNBOOL(a_msdu_fragmentation_support,bit++)
+ PRNBOOL(flexible_twt_schedule_support,bit++)
+ PRNBOOL(rx_control_frame_to_multibss,bit++)
+
+ // bits 32-39
+ PRNBOOL(bsrp_bqrp_a_mpdu_aggregation,bit++)
+ PRNBOOL(qtp_support,bit++)
+ PRNBOOL(bqr_support,bit++)
+ PRNBOOL(srp_responder,bit++)
+ PRNBOOL(ndp_feedback_report_support,bit++)
+ PRNBOOL(ops_support,bit++)
+ PRNBOOL(a_msdu_in_a_mpdu_support,bit++)
+
+ // bits 39-41
+ PRN(multi_tid_aggregation_support, bit); bit+= 3;
+
+ PRNBOOL(subchannel_selective_trans_support,bit++)
+ PRNBOOL(ul_2_996_tone_ru_support,bit++)
+ PRNBOOL(om_control_ul_mu_data_disable_rx_support,bit++)
+}
+
+void ie_print_he_capabilities_phy(const struct IE_HE_PHY* phy)
+{
+ char s[128];
+ unsigned int bit;
+ int ret;
+
+#undef STRING_FN
+#undef _struct
+#define _struct phy
+#define STRING_FN he_phy_capa_to_str
+
+ printf("\t\tHE PHY capabilities:\n");
+ // bit 0 reserved
+ bit = 1;
+ PRNBOOL(ch40mhz_channel_2_4ghz, bit++)
+ PRNBOOL(ch40_and_80_mhz_5ghz , bit++)
+ PRNBOOL(ch160_mhz_5ghz , bit++)
+ PRNBOOL(ch160_80_plus_80_mhz_5ghz , bit++)
+ PRNBOOL(ch242_tone_rus_in_2_4ghz , bit++)
+ PRNBOOL(ch242_tone_rus_in_5ghz, bit++)
+ bit++; // bit 7 reserved
+
+ // bits 8-23
+ PRN(punctured_preamble_rx,bit); bit+=4;
+ PRN(device_class,bit++)
+ PRNBOOL(ldpc_coding_in_payload, bit++);
+ PRNBOOL(he_su_ppdu_1x_he_ltf_08us, bit++);
+ PRN(midamble_rx_max_nsts, bit); bit+=2;
+ PRNBOOL(ndp_with_4x_he_ltf_32us, bit++);
+ PRNBOOL(stbc_tx_lt_80mhz, bit++);
+ PRNBOOL(stbc_rx_lt_80mhz, bit++);
+ PRNBOOL(doppler_tx, bit++);
+ PRNBOOL(doppler_rx, bit++);
+ PRNBOOL(full_bw_ul_mu_mimo, bit++);
+ PRNBOOL(partial_bw_ul_mu_mimo, bit++);
+
+ // 24-39
+ PRN(dcm_max_constellation_tx, bit); bit+=2;
+ PRNBOOL(dcm_max_nss_tx, bit++); // 1
+ PRN(dcm_max_constellation_rx, bit); bit+=2; // 2
+ PRNBOOL(dcm_max_nss_rx, bit++); // 1
+ PRNBOOL(rx_he_muppdu_from_non_ap, bit++); // 1
+ PRNBOOL(su_beamformer, bit++); // 1
+ PRNBOOL(su_beamformee, bit++); // 1
+ PRNBOOL(mu_beamformer, bit++); // 1
+ PRN(beamformer_sts_lte_80mhz, bit); bit+=3; // 3
+ PRN(beamformer_sts_gt_80mhz, bit); bit+=3;
+
+ // 40-55
+ PRN(number_of_sounding_dims_lte_80, bit); bit+=3;
+ PRN(number_of_sounding_dims_gt_80, bit); bit+=3; // 3
+ PRNBOOL(ng_eq_16_su_fb, bit++); // 1
+ PRNBOOL(ng_eq_16_mu_fb, bit++); // 1
+ PRNBOOL(codebook_size_eq_4_2_fb, bit++); // 1
+ PRNBOOL(codebook_size_eq_7_5_fb, bit++); // 1
+ PRNBOOL(triggered_su_beamforming_fb, bit++); // 1
+ PRNBOOL(triggered_mu_beamforming_fb, bit++); // 1
+ PRNBOOL(triggered_cqi_fb, bit++); // 1
+ PRNBOOL(partial_bw_extended_range, bit++); // 1
+ PRNBOOL(partial_bw_dl_mu_mimo, bit++); // 1
+ PRNBOOL(ppe_threshold_present, bit++);
+
+ // 56-71
+ PRNBOOL(srp_based_sr_support, bit++);
+ PRNBOOL(power_boost_factor_ar_support, bit++); // 1
+ PRNBOOL(he_su_ppdu_etc_gi, bit++); // 1
+ PRN(max_nc, bit); bit+=3; // 3
+ PRNBOOL(stbc_tx_gt_80_mhz, bit++); // 1
+ PRNBOOL(stbc_rx_gt_80_mhz, bit++); // 1
+ PRNBOOL(he_er_su_ppdu_4xxx_gi, bit++); // 1
+ PRNBOOL(_20mhz_in_40mhz_24ghz_band, bit++); // 1
+ PRNBOOL(_20mhz_in_160_80p80_ppdu, bit++); // 1
+ PRNBOOL(_80mgz_in_160_80p80_ppdu, bit++); // 1
+ PRNBOOL(he_er_su_ppdu_1xxx_gi, bit++); // 1
+ PRNBOOL(midamble_rx_2x_xxx_ltf, bit++); // 1
+ PRN(dcm_max_bw, bit); bit += 2;
+
+ // 72-87
+ PRNBOOL(longer_than_16_he_sigb_ofdm_symbol_support, bit++);
+ PRNBOOL(non_triggered_cqi_feedback, bit++); // 1
+ PRNBOOL(tx_1024_qam_242_tone_ru_support, bit++); // 1
+ PRNBOOL(rx_1024_qam_242_tone_ru_support, bit++); // 1
+ PRNBOOL(rx_full_bw_su_using_he_muppdu_w_compressed_sigb, bit++); // 1
+ PRNBOOL(rx_full_bw_su_using_he_muppdu_w_non_compressed_sigb, bit++); // 1
+ PRN(nominal_packet_padding, bit ); bit++; // 2
+
+#undef STRING_FN
+#undef _struct
+}
+
+void ie_print_he_operation(const struct IE_HE_Operation* sie)
+{
+ char s[128];
+ unsigned int bit;
+ int ret;
+
+#define STRING_FN he_operation_to_str
+#define _struct sie
+
+ printf("\tHE operation:\n");
+ printf("\t\tHE Operation Parameters:\n");
+ bit = 0;
+ PRN(default_pe_duration, bit); bit += 3;
+ PRN(twt_required, bit++);
+ PRN(txop_duration_rts_thresh, bit); bit += 10;
+ PRNBOOL(fields->vht_op_info_present, bit++);
+ PRNBOOL(fields->co_located_bss, bit++);
+ PRNBOOL(fields->er_su_disable, bit++);
+ bit += 7; // skip reserved bits
+
+ printf("\t\tHE BSS Color Information\n");
+ // bits 24-31
+ PRN(bss_color, bit); bit+= 6;
+ PRNBOOL(fields->partial_bss_color, bit++);
+ PRNBOOL(fields->bss_color_disabled, bit++);
+
+#undef STRING_FN
+#undef _struct
+}
+
diff --git a/ie_he.h b/ie_he.h
new file mode 100644
index 0000000..52c6243
--- /dev/null
+++ b/ie_he.h
@@ -0,0 +1,236 @@
+#ifndef IE_HE_H
+#define IE_HE_H
+
+#include <stdint.h>
+
+// I don't have the US$3000 to get the IEEE 80211.ax standard so I'm using
+// Wireshark's decode code. I'm putting HE decode into its own file to
+// carefully show the HE code from Wireshark.
+
+// the structure member names taken from Wireshark epan/dissectors/packet-ieee80211.c
+
+struct __attribute__((__packed__)) IE_HE_MAC
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ // 0-7
+ uint64_t htc_he_support : 1,
+ twt_requester_support : 1,
+ twt_responder_support : 1,
+ fragmentation_support : 2,
+ max_number_fragmented_msdus : 3,
+
+ // 8-14
+ min_fragment_size : 2,
+ trigger_frame_mac_padding_dur : 2,
+ multi_tid_aggregation_support : 3,
+
+ // 15,16
+ he_link_adaptation_support : 2,
+
+ // 17-23
+ all_ack_support : 1,
+ trs_support : 1,
+ bsr_support : 1,
+ broadcast_twt_support : 1,
+ _32_bit_ba_bitmap_support : 1,
+ mu_cascading_support : 1,
+ ack_enabled_aggregation_support : 1,
+
+ // 24-31
+ reserved_b24 : 1,
+ om_control_support : 1,
+ ofdma_ra_support : 1,
+ max_a_mpdu_length_exponent_ext : 2,
+ a_msdu_fragmentation_support : 1,
+ flexible_twt_schedule_support : 1,
+ rx_control_frame_to_multibss : 1,
+
+ // 32-38
+ bsrp_bqrp_a_mpdu_aggregation : 1,
+ qtp_support : 1,
+ bqr_support : 1,
+ srp_responder : 1,
+ ndp_feedback_report_support : 1,
+ ops_support : 1,
+ a_msdu_in_a_mpdu_support : 1,
+
+ // 39,40,41
+ multi_tid_aggregation_tx_support : 3,
+
+ // 42
+ subchannel_selective_trans_support : 1,
+ ul_2_996_tone_ru_support : 1,
+ om_control_ul_mu_data_disable_rx_support : 1,
+ reserved_b45: 1,
+ reserved_b46: 1,
+ reserved_b47: 1;
+#else
+#error TODO big endian
+#endif
+} ;
+
+struct __attribute__((__packed__)) IE_HE_PHY
+{
+ // 0-7
+ uint8_t reserved_b0 : 1,
+ ch40mhz_channel_2_4ghz : 1,
+ ch40_and_80_mhz_5ghz : 1,
+ ch160_mhz_5ghz : 1,
+ ch160_80_plus_80_mhz_5ghz : 1,
+ ch242_tone_rus_in_2_4ghz : 1,
+ ch242_tone_rus_in_5ghz : 1,
+ reserved_b7 : 1;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ // wireshark using uint16 for reasons I don't fully comprehend so when in
+ // Rome...
+ // 8-23
+ uint16_t punctured_preamble_rx : 4,
+ device_class : 1,
+ ldpc_coding_in_payload : 1,
+ he_su_ppdu_1x_he_ltf_08us : 1,
+ midamble_rx_max_nsts : 2,
+ ndp_with_4x_he_ltf_32us : 1,
+ stbc_tx_lt_80mhz : 1,
+ stbc_rx_lt_80mhz : 1,
+ doppler_tx : 1,
+ doppler_rx : 1,
+ full_bw_ul_mu_mimo : 1,
+ partial_bw_ul_mu_mimo : 1;
+
+ // 24-39
+ uint16_t dcm_max_constellation_tx : 2,
+ dcm_max_nss_tx : 1,
+ dcm_max_constellation_rx : 2,
+ dcm_max_nss_rx : 1,
+ rx_he_muppdu_from_non_ap : 1,
+ su_beamformer : 1,
+ su_beamformee : 1,
+ mu_beamformer : 1,
+ beamformer_sts_lte_80mhz : 3,
+ beamformer_sts_gt_80mhz : 3;
+
+ // 40-55
+ uint16_t number_of_sounding_dims_lte_80 : 3,
+ number_of_sounding_dims_gt_80 : 3,
+ ng_eq_16_su_fb : 1,
+ ng_eq_16_mu_fb : 1,
+ codebook_size_eq_4_2_fb : 1,
+ codebook_size_eq_7_5_fb : 1,
+ triggered_su_beamforming_fb : 1,
+ triggered_mu_beamforming_fb : 1,
+ triggered_cqi_fb : 1,
+ partial_bw_extended_range : 1,
+ partial_bw_dl_mu_mimo : 1,
+ ppe_threshold_present : 1;
+
+ // 56-71
+ uint16_t srp_based_sr_support : 1,
+ power_boost_factor_ar_support : 1,
+ he_su_ppdu_etc_gi : 1,
+ max_nc : 3,
+ stbc_tx_gt_80_mhz : 1,
+ stbc_rx_gt_80_mhz : 1,
+ he_er_su_ppdu_4xxx_gi : 1,
+ _20mhz_in_40mhz_24ghz_band : 1,
+ _20mhz_in_160_80p80_ppdu : 1,
+ _80mgz_in_160_80p80_ppdu : 1,
+ he_er_su_ppdu_1xxx_gi : 1,
+ midamble_rx_2x_xxx_ltf : 1,
+ dcm_max_bw : 2;
+
+ // 72-87
+ uint8_t longer_than_16_he_sigb_ofdm_symbol_support : 1,
+ non_triggered_cqi_feedback : 1,
+ tx_1024_qam_242_tone_ru_support : 1,
+ rx_1024_qam_242_tone_ru_support : 1,
+ rx_full_bw_su_using_he_muppdu_w_compressed_sigb : 1,
+ rx_full_bw_su_using_he_muppdu_w_non_compressed_sigb : 1,
+ nominal_packet_padding : 2,
+ reserved_b80_b87;
+#else
+# error TODO big endian
+#endif
+} ;
+
+struct __attribute__((__packed__)) IE_HE_Operation_Fields
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint16_t default_pe_duration : 3,
+ twt_required : 1,
+ txop_duration_rts_thresh : 10,
+ vht_op_info_present : 1,
+ co_located_bss : 1;
+ uint8_t er_su_disable : 1,
+ reserved_b17_b23 : 7;
+
+ uint8_t bss_color : 6,
+ partial_bss_color : 1,
+ bss_color_disabled : 1;
+
+ // HE MCS and NSS Set
+ // TODO
+
+#else
+# error TODO big endian
+#endif
+};
+
+#define IE_HE_CAPA_MAC_SIZE 6
+#define IE_HE_CAPA_PHY_SIZE 11
+#define IE_HE_CAPA_MCS_SIZE 4
+
+struct IE_HE_Capabilities
+{
+ // pointers into ie buf
+ const uint8_t* mac_capa; // 6 bytes
+ const uint8_t* phy_capa; // 11 bytes
+ const uint8_t* mcs_and_nss_set; // 4 bytes
+ const uint8_t* ppe_threshold; // 1+3*SS bytes
+
+ // HE Mac Capabilities
+ const struct IE_HE_MAC* mac;
+ const struct IE_HE_PHY* phy;
+
+ // HE MCS and NSS Set
+ // TODO
+
+ // PPE Thresholds
+ // Note this is a variable length field. Has an entry for each SS (Spatial
+ // Stream).
+ // TODO
+};
+
+struct IE_HE_Operation
+{
+ // pointers into ie buf
+ const uint8_t* params; // 3 bytes
+ const uint8_t* bss_color; // 1 byte
+ const uint8_t* mcs_and_nss_set; // 2 bytes
+
+ struct IE_HE_Operation_Fields* fields;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ie_he_operation_init(struct IE_HE_Operation* ie, const uint8_t* data, uint8_t len);
+int ie_he_capabilities_init(struct IE_HE_Capabilities* ie, const uint8_t* data, uint8_t len);
+
+int he_max_frag_msdus_base_to_str(uint8_t max_frag_msdus_value, char* s, size_t len);
+int he_mac_capa_to_str(const struct IE_HE_MAC* sie, unsigned int idx, char* s, size_t len);
+int he_phy_capa_to_str(const struct IE_HE_PHY* sie, unsigned int idx, char* s, size_t len);
+int he_operation_to_str(const struct IE_HE_Operation* sie, unsigned int idx, char* s, size_t len);
+
+void ie_print_he_capabilities(const struct IE_HE_Capabilities* sie);
+void ie_print_he_capabilities_mac(const struct IE_HE_MAC* mac);
+void ie_print_he_capabilities_phy(const struct IE_HE_PHY* phy);
+void ie_print_he_operation(const struct IE_HE_Operation* sie);
+
+#ifdef __cplusplus
+} // end extern "C"
+#endif
+
+#endif
+
diff --git a/scan.c b/scan.c
index bfd39e4..a984e6a 100644
--- a/scan.c
+++ b/scan.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <string.h>
#include <stdbool.h>
+#include <assert.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
@@ -11,6 +12,7 @@
#include "nl80211.h"
#include "iw.h"
+#include "ie_he.h"
#define WLAN_CAPABILITY_ESS (1<<0)
#define WLAN_CAPABILITY_IBSS (1<<1)
@@ -2011,6 +2013,74 @@ static void print_vendor(unsigned char len, unsigned char *data,
printf("\n");
}
+enum nl80211_ie_extension
+{
+ NL80211_IE_EXT_HE_CAPABILITIES = 35,
+ NL80211_IE_EXT_HE_OPERATION = 36,
+ NL80211_IE_EXT_MU_EDCA_PARAM_SET = 38,
+ NL80211_IE_EXT_SPATIAL_REUSE_PARAM_SET = 39,
+};
+
+static void print_he_capabilities(const uint8_t type, uint8_t len, const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ struct IE_HE_Capabilities ie;
+ int ret = ie_he_capabilities_init(&ie, data, len);
+ assert(ret==0);
+ if (ret==0) {
+ ie_print_he_capabilities(&ie);
+ }
+}
+
+static void print_he_operation(const uint8_t type, uint8_t len, const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ struct IE_HE_Operation ie;
+ int ret = ie_he_operation_init(&ie, data, len);
+ assert(ret==0);
+ if (ret==0) {
+ ie_print_he_operation(&ie);
+ }
+}
+
+static const struct ie_print extension_ieprinters[] = {
+ [NL80211_IE_EXT_HE_CAPABILITIES] = { "HE Capabilities", print_he_capabilities, 25, 254, BIT(PRINT_SCAN), },
+ [NL80211_IE_EXT_HE_OPERATION] = { "HE Operation", print_he_operation, 6, 254, BIT(PRINT_SCAN), },
+};
+
+
+static void print_extension_ie(unsigned char len, unsigned char *data,
+ bool unknown, enum print_ie_type ptype)
+{
+ struct print_ies_data ie_buffer = {
+ .ie = data,
+ .ielen = len };
+
+ printf("%s len=%u ", __func__, len);
+ for(int i = 0; i < len; i++)
+ printf(" %.02x", data[i]);
+ printf("\n");
+
+ uint8_t ext_id = data[0];
+ if (ext_id < ARRAY_SIZE(extension_ieprinters) &&
+ extension_ieprinters[ext_id].name &&
+ extension_ieprinters[ext_id].flags & BIT(ptype))
+ {
+ // note the length of the extension IE is not re-encoded after the EXT
+ // ID so we pass the original length minus the extension id
+ print_ie(&extension_ieprinters[ext_id],
+ ext_id, len-1, data+1, &ie_buffer);
+ }
+ else if (unknown) {
+ int i;
+ printf("\tUnknown Extension IE (%d):", ext_id);
+ for (i=0; i<data[1]; i++)
+ printf(" %.2x", data[2+i]);
+ printf("\n");
+ }
+
+}
+
void print_ies(unsigned char *ie, int ielen, bool unknown,
enum print_ie_type ptype)
{
@@ -2026,6 +2096,8 @@ void print_ies(unsigned char *ie, int ielen, bool unknown,
ie[0], ie[1], ie + 2, &ie_buffer);
} else if (ie[0] == 221 /* vendor */) {
print_vendor(ie[1], ie + 2, unknown, ptype);
+ } else if (ie[0] == 255 /* extension */) {
+ print_extension_ie(ie[1], ie + 2, unknown, ptype);
} else if (unknown) {
int i;
@@ -2103,7 +2175,7 @@ static void print_capa_non_dmg(__u16 capa)
printf(" ImmediateBACK");
}
-static int print_bss_handler(struct nl_msg *msg, void *arg)
+int print_bss_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -2126,8 +2198,9 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
int show = params->show_both_ie_sets ? 2 : 1;
bool is_dmg = false;
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ int ret = nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
+ assert(ret >= 0);
if (!tb[NL80211_ATTR_BSS]) {
fprintf(stderr, "bss info missing!\n");
diff --git a/test_scan.c b/test_scan.c
new file mode 100644
index 0000000..1985109
--- /dev/null
+++ b/test_scan.c
@@ -0,0 +1,213 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "nl80211.h"
+#include "iw.h"
+
+#define PTR_FREE(p) do { free(p); (p)=NULL; } while(0)
+#define PTR_ASSIGN(dst,src) do { (dst)=(src); (src)=NULL; } while(0)
+
+// from scan.c
+struct scan_params {
+ bool unknown;
+ enum print_ie_type type;
+ bool show_both_ie_sets;
+};
+
+extern int print_bss_handler(struct nl_msg *msg, void *arg);
+
+static struct nl_msg* msg_encode(uint8_t* buf, size_t buf_len)
+{
+// DBG("%s\n", __func__);
+
+ // can I rebuild an nl_msg containing buf as a payload?
+ struct nl_msg* msg = nlmsg_alloc();
+
+ int nl80211_id = 0;
+
+ void* p = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ,
+ nl80211_id,
+ 0,
+ NLM_F_DUMP, NL80211_CMD_NEW_SCAN_RESULTS, 0);
+ (void)p;
+
+ // have to parse the blob containing the nlattr into individual nlattrs so
+ // we can put them back into the msg
+ struct nlattr* attr = (struct nlattr*)buf;
+ size_t attr_len = buf_len;
+ struct nlattr* tb_msg[NL80211_ATTR_MAX + 1];
+ int err = nla_parse(tb_msg, NL80211_ATTR_MAX, attr, attr_len, NULL);
+ if (err<0) {
+ fprintf(stderr, "%s nla_parse failed err=%d\n", __func__, err);
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ for (size_t i=0 ; i<NL80211_ATTR_MAX ; i++ ) {
+ if (tb_msg[i]) {
+ err = nla_put(msg,
+ nla_type(tb_msg[i]),
+ nla_len(tb_msg[i]),
+ (void *)nla_data(tb_msg[i]));
+ if (err<0) {
+ fprintf(stderr, "%s nla_put failed err=%d\n", __func__, err);
+ nlmsg_free(msg);
+ return NULL;
+ }
+ }
+ }
+
+ // now let's try taking it apart again
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ struct genlmsghdr* gnlh = (struct genlmsghdr*)nlmsg_data(hdr);
+
+ attr = genlmsg_attrdata(gnlh,0);
+ attr_len = genlmsg_attrlen(gnlh,0);
+
+ printf("%s buf=%p buflen=%zu attr=%p len=%zu\n", __func__,
+ (void*)buf,
+ buf_len,
+ (void*)attr,
+ attr_len);
+// hex_dump(__func__, (unsigned char*)attr, len);
+
+// peek_nla_attr(tb_msg, NL80211_ATTR_MAX);
+
+ return msg;
+}
+
+// stubs
+
+__u32 listen_events(struct nl80211_state *state,
+ const int n_waits, const __u32 *waits)
+{
+ (void)state;
+ (void)n_waits;
+ (void)waits;
+ return 0;
+}
+
+int handle_cmd(struct nl80211_state *state, enum id_input idby,
+ int argc, char **argv)
+{
+ (void)state;
+ (void)idby;
+ (void)argc;
+ (void)argv;
+ return 0;
+}
+
+void register_handler(int (*handler)(struct nl_msg *, void *), void *data)
+{
+ (void)handler;
+ (void)data;
+}
+
+static int load_file(const char* filename, uint8_t** p_buf, size_t* p_size)
+{
+ struct stat stats;
+ int err = stat(filename, &stats);
+ if (err<0) {
+ fprintf(stderr, "stat file \"%s\" failed err=%d %s\n", filename, errno, strerror(errno));
+ return err;
+ }
+
+ uint8_t* buf = malloc(stats.st_size);
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ int fd = open(filename, O_RDONLY);
+ if (fd<0) {
+ PTR_FREE(buf);
+ fprintf(stderr, "open file \"%s\" failed err=%d %s\n", filename, errno, strerror(errno));
+ return -errno;
+ }
+
+ ssize_t count = read(fd, buf, stats.st_size);
+ if (count < 0) {
+ PTR_FREE(buf);
+ fprintf(stderr, "read file \"%s\" failed err=%d %s\n", filename, errno, strerror(errno));
+ return -errno;
+ }
+ close(fd);
+
+ if (count != stats.st_size) {
+ PTR_FREE(buf);
+ return -EIO;
+ }
+
+ PTR_ASSIGN(*p_buf, buf);
+ *p_size = stats.st_size;
+ return 0;
+}
+
+static int test_bss_handler( struct nl_msg* msg)
+{
+ nl_msg_dump(msg, stdout);
+
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr* tb_msg[NL80211_ATTR_MAX + 1];
+ int err = nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (err < 0) {
+ fprintf(stderr, "nla_parse failed err=%d\n", err);
+ return err;
+ }
+
+ if (!tb_msg[NL80211_ATTR_BSS]) {
+ fprintf(stderr, "%s bss info missing!\n", __func__);
+ return -EINVAL;
+ }
+
+ struct scan_params scan_params;
+ memset(&scan_params, 0, sizeof(scan_params));
+ scan_params.unknown = true;
+ scan_params.type = PRINT_SCAN;
+
+ err = print_bss_handler(msg, (void*)&scan_params);
+
+ return 0;
+}
+
+int main(int argc, char*argv[] )
+{
+ for (int i=1 ; i<argc ; i++) {
+ uint8_t* buf;
+ size_t size;
+
+ int err = load_file(argv[i], &buf, &size);
+ if (err < 0) {
+ fprintf(stderr, "failed to load file \"%s\"; err=%d\n", argv[i], err);
+ continue;
+ }
+
+ struct nl_msg* msg = msg_encode(buf, size);
+ if (!msg) {
+ // encode logs error
+ goto clean;
+ }
+
+ test_bss_handler(msg);
+clean:
+ nlmsg_free(msg);
+ PTR_FREE(buf);
+ }
+
+ return 0;
+}
+
+
--
2.24.1