From patchwork Sun Jan 19 18:16:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Poole X-Patchwork-Id: 11340721 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 679E213A4 for ; Sun, 19 Jan 2020 18:16:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 10EB3206B7 for ; Sun, 19 Jan 2020 18:16:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=cradlepoint.com header.i=@cradlepoint.com header.b="NmC8Dczv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727243AbgASSQq (ORCPT ); Sun, 19 Jan 2020 13:16:46 -0500 Received: from mail-dm6nam11on2092.outbound.protection.outlook.com ([40.107.223.92]:32545 "EHLO NAM11-DM6-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727111AbgASSQq (ORCPT ); Sun, 19 Jan 2020 13:16:46 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AYze0CufayhGbaBqSo2zqPP/qZTDLdkowm/U7I4VvjcGn7XQq4DRgnHlhAqe0MCwlVUfYiWYms+wKt8bXYIVcsvyuadDbkajheA+u1xjRktqFgjbjwZsauCX8KYzewnYyc7L3XkS8umYthJQJQEifzzHQs3ES2GiCCmLVWafhjF+M4xvUjzQdrCUM857wrBVDiyXb3LmuD2+uJBxAxUV260PwRcPNws75xt+swdu+aoxaBxjxhpeziPeHsaaxWiDIe5NnHWkQgJFoNLssD6iUXUa7ytdvxrEK9pe1ZWOY+mkZ40Ca+gbI2KIOeGfGN5d8Qx/kJJepZicogM7l1dPyg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Biyfsz8wtiKvkyNfPQnyEqonU+KFn2Zz7eH41TOK0Y8=; b=JPOgVh3HfWrpO36m1DUCG3BgzRVcjnLWSkXsfYeGNqIOB8LEAfdxcJ4lP8uKwcLAKrDY/K/gHMdVwCx93ZJVAU+9cpWJElnCkznVezJFDpuIiPhKySFKtKFq5xqB7P2eYhkAJrSm28JBoMiG267/alf//BPhRzIDJnol/9RN0KM/4N71gxXBWejAvJHAorxBfhYvmY7JBsan6rXVtIfezNUgUVvaDn0ubsC0+QTBYHyAlC3hrcOVrLm5RjOJVdxAIe4k3X1X1sNCXWhUDyyOfvjqwU4hNRJfuphVuv+lZRcr4mbZE+NJzvIbKcf20/B9OEr9fOjjdOUDNL3YV6DnRg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=cradlepoint.com; dmarc=pass action=none header.from=cradlepoint.com; dkim=pass header.d=cradlepoint.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cradlepoint.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Biyfsz8wtiKvkyNfPQnyEqonU+KFn2Zz7eH41TOK0Y8=; b=NmC8Dczv0kqhQPFGIE5TmOS8qxdhrjo3yyxhXGC9Ia77LPzVX2ITJk8g3owGUrybMFObyF6w9e0jZOFP2TDrMzHAUjIbyMkaPQ1MfRpdJX7XCD5wnttOHGputWwC0vYC+5W6Znsqw/+Ff7J6X/yivExUMpj1U2L4rOLrKu0p/8g= Received: from MWHPR12MB1533.namprd12.prod.outlook.com (10.172.55.136) by MWHPR12MB1838.namprd12.prod.outlook.com (10.175.49.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2644.18; Sun, 19 Jan 2020 18:16:33 +0000 Received: from MWHPR12MB1533.namprd12.prod.outlook.com ([fe80::3c84:4a64:e509:51a9]) by MWHPR12MB1533.namprd12.prod.outlook.com ([fe80::3c84:4a64:e509:51a9%11]) with mapi id 15.20.2644.024; Sun, 19 Jan 2020 18:16:33 +0000 From: David Poole To: "johannes@sipsolutions.net" CC: linux-wireless , David Poole Subject: Adding 802.11ax decode to 'iw scan dump' Checking for interest and looking for feedback. Thread-Topic: Adding 802.11ax decode to 'iw scan dump' Checking for interest and looking for feedback. Thread-Index: AQHVzvO9TASlSfl6/EqOM7v3SXf4lw== Date: Sun, 19 Jan 2020 18:16:33 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=dpoole@cradlepoint.com; x-originating-ip: [2600:3c01::f03c:91ff:feac:40b] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 3cac0c8a-cb3c-44d7-331a-08d79d0bb6d6 x-ms-traffictypediagnostic: MWHPR12MB1838: x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:1751; x-forefront-prvs: 0287BBA78D x-forefront-antispam-report: SFV:NSPM;SFS:(10019020)(4636009)(346002)(396003)(366004)(136003)(39830400003)(376002)(199004)(189003)(71200400001)(186003)(316002)(8936002)(478600001)(6916009)(52536014)(54906003)(6506007)(33656002)(8676002)(7696005)(5660300002)(966005)(81156014)(81166006)(4326008)(86362001)(9686003)(55016002)(66946007)(66616009)(66556008)(66476007)(2906002)(91956017)(66446008)(76116006)(4744005)(64756008);DIR:OUT;SFP:1102;SCL:1;SRVR:MWHPR12MB1838;H:MWHPR12MB1533.namprd12.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: cradlepoint.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: T30OeWEMTS0TRSYupUK4ZhLAfL0am6arz/gTyd9RusCzYBFlv3t5INYYElnT8Qg3ntnVkg5lC2uGMnYlTriUWupseLj8pK2KIM4Vgpca28BEpEoN7c92IzmmQ1/Y6QUs+BerKBjevJfUBiLe75dyTXap68EdPQ/tWR0p9eg9Yljo77vEYIl2OyKSLF174K0TYSJVIcfU3qGsl+BYcJhSb42tMjrizl4m8nM2elPh1+qBRg8k0qUJ4vfVnEcZdtm9xfm0TibnYckN+tgi1f8+KesM/is4lAbhRa9aHuLcrACjNDVuj9h+rQwUxFwKLhcCDR+CvC5FXLTbxQ2W9BC9Nkf9Z5JDtiuwgsubDwk1il+QwWAxIa5AOZScR/jWxr6JI087OZht4r9ieQJa512OIKEXFfd7Y86qQugwzRYbVbnvdLwarzSD7hr7zuvsQ0v2oPnQ0LMI8vVnLExf4ajIEHHVRFOPNhiHaKq/8qOhBXt2CE0wH5C1BPW8LlIelmjo9FBX5p7BGacSQ82fvZzd8Q== x-ms-exchange-transport-forked: True MIME-Version: 1.0 X-OriginatorOrg: cradlepoint.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3cac0c8a-cb3c-44d7-331a-08d79d0bb6d6 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Jan 2020 18:16:33.1427 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 1c02e371-8283-43fd-a70d-1e8e10c567ec X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: g8mvG8WHFyAO8+IXOPF5y3xuCbRSuhFv9nD09v3agJlL1RXlrjOoQS9F7ujABSD8oZHQzanK8yLwR0tXc6j1Ow== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MWHPR12MB1838 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org 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@cradlepoint.com From 45a369ccda08211ddf123ed99b76abb698937539 Mon Sep 17 00:00:00 2001 From: David Poole Date: Sun, 19 Jan 2020 10:59:31 -0700 Subject: [PATCH] work in progress: first pass adding 802.11ax Signed-off-by: David Poole --- 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 +#include +#include +#include + +#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 + +// 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 #include #include +#include #include #include @@ -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; ishow_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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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