diff --git a/Cargo.toml b/Cargo.toml index 978e156..e6d30e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "wl-nl80211" version = "0.1.2" authors = ["Gris Ge "] license = "MIT" -edition = "2018" +edition = "2021" description = "Linux kernel wireless(802.11) netlink Library" homepage = "https://github.com/rust-netlink/wl-nl80211" repository = "https://github.com/rust-netlink/wl-nl80211" @@ -25,6 +25,7 @@ smol_socket = ["netlink-proto/smol_socket", "async-std"] [dependencies] anyhow = "1.0.44" async-std = { version = "1.9.0", optional = true} +bitflags = "2" byteorder = "1.4.3" futures = "0.3.17" log = "0.4.14" diff --git a/examples/dump_nl80211_wiphy.rs b/examples/dump_nl80211_wiphy.rs new file mode 100644 index 0000000..5141b96 --- /dev/null +++ b/examples/dump_nl80211_wiphy.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +use futures::stream::TryStreamExt; + +fn main() { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); + rt.block_on(get_wireless_physics()); +} + +async fn get_wireless_physics() { + let (connection, handle, _) = wl_nl80211::new_connection().unwrap(); + tokio::spawn(connection); + + let mut phy_handle = handle.wireless_physic().get().execute().await; + + let mut msgs = Vec::new(); + while let Some(msg) = phy_handle.try_next().await.unwrap() { + msgs.push(msg); + } + assert!(!msgs.is_empty()); + for msg in msgs { + println!("{:?}", msg); + } +} diff --git a/src/attr.rs b/src/attr.rs index bbdc2db..bf50f5e 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,62 +1,549 @@ // SPDX-License-Identifier: MIT +// Most documentation comments are copied and modified from linux kernel +// include/uapi/linux/nl80211.h which is holding these license disclaimer: +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2024 Intel Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + use anyhow::Context; -use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, - parsers::{parse_string, parse_u32, parse_u64, parse_u8}, - DecodeError, Emitable, Parseable, + parsers::{parse_string, parse_u16, parse_u32, parse_u64, parse_u8}, + DecodeError, Emitable, Parseable, ParseableParametrized, }; use crate::{ - Nl80211ChannelWidth, Nl80211InterfaceType, Nl80211StationInfo, - Nl80211TransmitQueueStat, Nl80211WiPhyChannelType, + bytes::{write_u16, write_u32, write_u64}, + wiphy::Nl80211Commands, + Nl80211Band, Nl80211BandTypes, Nl80211ChannelWidth, Nl80211CipherSuit, + Nl80211Command, Nl80211ExtFeature, Nl80211ExtFeatures, + Nl80211ExtendedCapability, Nl80211Features, Nl80211HtCapabilityMask, + Nl80211HtWiphyChannelType, Nl80211IfMode, Nl80211IfTypeExtCapa, + Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType, + Nl80211InterfaceType, Nl80211InterfaceTypes, Nl80211MloLink, + Nl80211StationInfo, Nl80211TransmitQueueStat, Nl80211VhtCapability, + Nl80211WowlanTrigersSupport, }; +struct MacAddressNlas(Vec); + +impl std::ops::Deref for MacAddressNlas { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&Vec<[u8; ETH_ALEN]>> for MacAddressNlas { + fn from(macs: &Vec<[u8; ETH_ALEN]>) -> Self { + let mut nlas = Vec::new(); + for (i, mac) in macs.iter().enumerate() { + let nla = MacAddressNla { + index: i as u16, + mac: *mac, + }; + nlas.push(nla); + } + MacAddressNlas(nlas) + } +} + +impl From for Vec<[u8; ETH_ALEN]> { + fn from(macs: MacAddressNlas) -> Self { + let mut macs = macs; + macs.0.drain(..).map(|c| c.mac).collect() + } +} + +impl MacAddressNlas { + pub fn parse(payload: &[u8]) -> Result { + let mut macs: Vec = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let error_msg = format!("Invalid NL80211_ATTR_MAC_ADDRS: {nla:?}"); + let nla = &nla.context(error_msg.clone())?; + let mut mac = [0u8; ETH_ALEN]; + mac.copy_from_slice(&nla.value()[..ETH_ALEN]); + macs.push(MacAddressNla { + index: index as u16, + mac, + }); + } + Ok(Self(macs)) + } +} + +struct MacAddressNla { + index: u16, + mac: [u8; ETH_ALEN], +} + +impl Nla for MacAddressNla { + fn value_len(&self) -> usize { + ETH_ALEN + } + + fn emit_value(&self, buffer: &mut [u8]) { + buffer[..ETH_ALEN].copy_from_slice(&self.mac) + } + + fn kind(&self) -> u16 { + self.index + } +} + +// const NL80211_ATTR_UNSPEC:u16 = 0; const NL80211_ATTR_WIPHY: u16 = 1; +const NL80211_ATTR_WIPHY_NAME: u16 = 2; const NL80211_ATTR_IFINDEX: u16 = 3; const NL80211_ATTR_IFNAME: u16 = 4; const NL80211_ATTR_IFTYPE: u16 = 5; const NL80211_ATTR_MAC: u16 = 6; +// const NL80211_ATTR_KEY_DATA:u16 = 7; +// const NL80211_ATTR_KEY_IDX:u16 = 8; +// const NL80211_ATTR_KEY_CIPHER:u16 = 9; +// const NL80211_ATTR_KEY_SEQ:u16 = 10; +// const NL80211_ATTR_KEY_DEFAULT:u16 = 11; +// const NL80211_ATTR_BEACON_INTERVAL:u16 = 12; +// const NL80211_ATTR_DTIM_PERIOD:u16 = 13; +// const NL80211_ATTR_BEACON_HEAD:u16 = 14; +// const NL80211_ATTR_BEACON_TAIL:u16 = 15; +// const NL80211_ATTR_STA_AID:u16 = 16; +// const NL80211_ATTR_STA_FLAGS:u16 = 17; +// const NL80211_ATTR_STA_LISTEN_INTERVAL:u16 = 18; +// const NL80211_ATTR_STA_SUPPORTED_RATES:u16 = 19; +// const NL80211_ATTR_STA_VLAN:u16 = 20; const NL80211_ATTR_STA_INFO: u16 = 21; +const NL80211_ATTR_WIPHY_BANDS: u16 = 22; +// const NL80211_ATTR_MNTR_FLAGS:u16 = 23; +// const NL80211_ATTR_MESH_ID:u16 = 24; +// const NL80211_ATTR_STA_PLINK_ACTION:u16 = 25; +// const NL80211_ATTR_MPATH_NEXT_HOP:u16 = 26; +// const NL80211_ATTR_MPATH_INFO:u16 = 27; +// const NL80211_ATTR_BSS_CTS_PROT:u16 = 28; +// const NL80211_ATTR_BSS_SHORT_PREAMBLE:u16 = 29; +// const NL80211_ATTR_BSS_SHORT_SLOT_TIME:u16 = 30; +// const NL80211_ATTR_HT_CAPABILITY:u16 = 31; +const NL80211_ATTR_SUPPORTED_IFTYPES: u16 = 32; +// const NL80211_ATTR_REG_ALPHA2:u16 = 33; +// const NL80211_ATTR_REG_RULES:u16 = 34; +// const NL80211_ATTR_MESH_CONFIG:u16 = 35; +// const NL80211_ATTR_BSS_BASIC_RATES:u16 = 36; +// const NL80211_ATTR_WIPHY_TXQ_PARAMS:u16 = 37; const NL80211_ATTR_WIPHY_FREQ: u16 = 38; const NL80211_ATTR_WIPHY_CHANNEL_TYPE: u16 = 39; +// const NL80211_ATTR_KEY_DEFAULT_MGMT:u16 = 40; +// const NL80211_ATTR_MGMT_SUBTYPE:u16 = 41; +// const NL80211_ATTR_IE:u16 = 42; +const NL80211_ATTR_MAX_NUM_SCAN_SSIDS: u16 = 43; +// const NL80211_ATTR_SCAN_FREQUENCIES:u16 = 44; +// const NL80211_ATTR_SCAN_SSIDS:u16 = 45; const NL80211_ATTR_GENERATION: u16 = 46; +// const NL80211_ATTR_BSS:u16 = 47; +// const NL80211_ATTR_REG_INITIATOR:u16 = 48; +// const NL80211_ATTR_REG_TYPE:u16 = 49; +const NL80211_ATTR_SUPPORTED_COMMANDS: u16 = 50; +// const NL80211_ATTR_FRAME:u16 = 51; const NL80211_ATTR_SSID: u16 = 52; +// const NL80211_ATTR_AUTH_TYPE:u16 = 53; +// const NL80211_ATTR_REASON_CODE:u16 = 54; +// const NL80211_ATTR_KEY_TYPE:u16 = 55; +const NL80211_ATTR_MAX_SCAN_IE_LEN: u16 = 56; +const NL80211_ATTR_CIPHER_SUITES: u16 = 57; +// const NL80211_ATTR_FREQ_BEFORE:u16 = 58; +// const NL80211_ATTR_FREQ_AFTER:u16 = 59; +// const NL80211_ATTR_FREQ_FIXED:u16 = 60; +const NL80211_ATTR_WIPHY_RETRY_SHORT: u16 = 61; +const NL80211_ATTR_WIPHY_RETRY_LONG: u16 = 62; +const NL80211_ATTR_WIPHY_FRAG_THRESHOLD: u16 = 63; +const NL80211_ATTR_WIPHY_RTS_THRESHOLD: u16 = 64; +// const NL80211_ATTR_TIMED_OUT:u16 = 65; +// const NL80211_ATTR_USE_MFP:u16 = 66; +// const NL80211_ATTR_STA_FLAGS2:u16 = 67; +// const NL80211_ATTR_CONTROL_PORT:u16 = 68; +// const NL80211_ATTR_TESTDATA:u16 = 69; +// const NL80211_ATTR_PRIVACY:u16 = 70; +// const NL80211_ATTR_DISCONNECTED_BY_AP:u16 = 71; +// const NL80211_ATTR_STATUS_CODE:u16 = 72; +// const NL80211_ATTR_CIPHER_SUITES_PAIRWISE:u16 = 73; +// const NL80211_ATTR_CIPHER_SUITE_GROUP:u16 = 74; +// const NL80211_ATTR_WPA_VERSIONS:u16 = 75; +// const NL80211_ATTR_AKM_SUITES:u16 = 76; +// const NL80211_ATTR_REQ_IE:u16 = 77; +// const NL80211_ATTR_RESP_IE:u16 = 78; +// const NL80211_ATTR_PREV_BSSID:u16 = 79; +// const NL80211_ATTR_KEY:u16 = 80; +// const NL80211_ATTR_KEYS:u16 = 81; +// const NL80211_ATTR_PID:u16 = 82; const NL80211_ATTR_4ADDR: u16 = 83; +// const NL80211_ATTR_SURVEY_INFO:u16 = 84; +// const NL80211_ATTR_PMKID:u16 = 85; +const NL80211_ATTR_MAX_NUM_PMKIDS: u16 = 86; +// const NL80211_ATTR_DURATION:u16 = 87; +// const NL80211_ATTR_COOKIE:u16 = 88; +const NL80211_ATTR_WIPHY_COVERAGE_CLASS: u16 = 89; +// const NL80211_ATTR_TX_RATES:u16 = 90; +// const NL80211_ATTR_FRAME_MATCH:u16 = 91; +// const NL80211_ATTR_ACK:u16 = 92; +// const NL80211_ATTR_PS_STATE:u16 = 93; +// const NL80211_ATTR_CQM:u16 = 94; +// const NL80211_ATTR_LOCAL_STATE_CHANGE:u16 = 95; +// const NL80211_ATTR_AP_ISOLATE:u16 = 96; +// const NL80211_ATTR_WIPHY_TX_POWER_SETTING:u16 = 97; const NL80211_ATTR_WIPHY_TX_POWER_LEVEL: u16 = 98; +const NL80211_ATTR_TX_FRAME_TYPES: u16 = 99; +const NL80211_ATTR_RX_FRAME_TYPES: u16 = 100; +// Covered by frame_type.rs +// const NL80211_ATTR_FRAME_TYPE:u16 = 101; +const NL80211_ATTR_CONTROL_PORT_ETHERTYPE: u16 = 102; +// const NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT:u16 = 103; +const NL80211_ATTR_SUPPORT_IBSS_RSN: u16 = 104; +const NL80211_ATTR_WIPHY_ANTENNA_TX: u16 = 105; +const NL80211_ATTR_WIPHY_ANTENNA_RX: u16 = 106; +// const NL80211_ATTR_MCAST_RATE:u16 = 107; +const NL80211_ATTR_OFFCHANNEL_TX_OK: u16 = 108; +// const NL80211_ATTR_BSS_HT_OPMODE:u16 = 109; +// const NL80211_ATTR_KEY_DEFAULT_TYPES:u16 = 110; +const NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: u16 = 111; +// const NL80211_ATTR_MESH_SETUP:u16 = 112; +const NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: u16 = 113; +const NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: u16 = 114; +const NL80211_ATTR_SUPPORT_MESH_AUTH: u16 = 115; +// const NL80211_ATTR_STA_PLINK_STATE:u16 = 116; +// const NL80211_ATTR_WOWLAN_TRIGGERS:u16 = 117; +const NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: u16 = 118; +// const NL80211_ATTR_SCHED_SCAN_INTERVAL:u16 = 119; +const NL80211_ATTR_INTERFACE_COMBINATIONS: u16 = 120; +const NL80211_ATTR_SOFTWARE_IFTYPES: u16 = 121; +// const NL80211_ATTR_REKEY_DATA:u16 = 122; +const NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: u16 = 123; +const NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: u16 = 124; +// const NL80211_ATTR_SCAN_SUPP_RATES:u16 = 125; +// const NL80211_ATTR_HIDDEN_SSID:u16 = 126; +// const NL80211_ATTR_IE_PROBE_RESP:u16 = 127; +// const NL80211_ATTR_IE_ASSOC_RESP:u16 = 128; +// const NL80211_ATTR_STA_WME:u16 = 129; +const NL80211_ATTR_SUPPORT_AP_UAPSD: u16 = 130; +const NL80211_ATTR_ROAM_SUPPORT: u16 = 131; +// const NL80211_ATTR_SCHED_SCAN_MATCH:u16 = 132; +const NL80211_ATTR_MAX_MATCH_SETS: u16 = 133; +// const NL80211_ATTR_PMKSA_CANDIDATE:u16 = 134; +// const NL80211_ATTR_TX_NO_CCK_RATE:u16 = 135; +// const NL80211_ATTR_TDLS_ACTION:u16 = 136; +// const NL80211_ATTR_TDLS_DIALOG_TOKEN:u16 = 137; +// const NL80211_ATTR_TDLS_OPERATION:u16 = 138; +const NL80211_ATTR_TDLS_SUPPORT: u16 = 139; +const NL80211_ATTR_TDLS_EXTERNAL_SETUP: u16 = 140; +// const NL80211_ATTR_DEVICE_AP_SME:u16 = 141; +// const NL80211_ATTR_DONT_WAIT_FOR_ACK:u16 = 142; +const NL80211_ATTR_FEATURE_FLAGS: u16 = 143; +const NL80211_ATTR_PROBE_RESP_OFFLOAD: u16 = 144; +// const NL80211_ATTR_PROBE_RESP:u16 = 145; +// const NL80211_ATTR_DFS_REGION:u16 = 146; +// const NL80211_ATTR_DISABLE_HT:u16 = 147; +const NL80211_ATTR_HT_CAPABILITY_MASK: u16 = 148; +// const NL80211_ATTR_NOACK_MAP:u16 = 149; +// const NL80211_ATTR_INACTIVITY_TIMEOUT:u16 = 150; +// const NL80211_ATTR_RX_SIGNAL_DBM:u16 = 151; +// const NL80211_ATTR_BG_SCAN_PERIOD:u16 = 152; const NL80211_ATTR_WDEV: u16 = 153; +// const NL80211_ATTR_USER_REG_HINT_TYPE:u16 = 154; +// const NL80211_ATTR_CONN_FAILED_REASON:u16 = 155; +// const NL80211_ATTR_AUTH_DATA:u16 = 156; +const NL80211_ATTR_VHT_CAPABILITY: u16 = 157; +// const NL80211_ATTR_SCAN_FLAGS:u16 = 158; const NL80211_ATTR_CHANNEL_WIDTH: u16 = 159; const NL80211_ATTR_CENTER_FREQ1: u16 = 160; const NL80211_ATTR_CENTER_FREQ2: u16 = 161; +// const NL80211_ATTR_P2P_CTWINDOW:u16 = 162; +// const NL80211_ATTR_P2P_OPPPS:u16 = 163; +// const NL80211_ATTR_LOCAL_MESH_POWER_MODE:u16 = 164; +// const NL80211_ATTR_ACL_POLICY:u16 = 165; +const NL80211_ATTR_MAC_ADDRS: u16 = 166; +// const NL80211_ATTR_MAC_ACL_MAX:u16 = 167; +// const NL80211_ATTR_RADAR_EVENT:u16 = 168; +const NL80211_ATTR_EXT_CAPA: u16 = 169; +const NL80211_ATTR_EXT_CAPA_MASK: u16 = 170; +// const NL80211_ATTR_STA_CAPABILITY:u16 = 171; +// const NL80211_ATTR_STA_EXT_CAPABILITY:u16 = 172; +// const NL80211_ATTR_PROTOCOL_FEATURES:u16 = 173; +const NL80211_ATTR_SPLIT_WIPHY_DUMP: u16 = 174; +// const NL80211_ATTR_DISABLE_VHT:u16 = 175; +const NL80211_ATTR_VHT_CAPABILITY_MASK: u16 = 176; +// const NL80211_ATTR_MDID:u16 = 177; +// const NL80211_ATTR_IE_RIC:u16 = 178; +// const NL80211_ATTR_CRIT_PROT_ID:u16 = 179; +// const NL80211_ATTR_MAX_CRIT_PROT_DURATION:u16 = 180; +// const NL80211_ATTR_PEER_AID:u16 = 181; +// const NL80211_ATTR_COALESCE_RULE:u16 = 182; +// const NL80211_ATTR_CH_SWITCH_COUNT:u16 = 183; +// const NL80211_ATTR_CH_SWITCH_BLOCK_TX:u16 = 184; +// const NL80211_ATTR_CSA_IES:u16 = 185; +// const NL80211_ATTR_CNTDWN_OFFS_BEACON:u16 = 186; +// const NL80211_ATTR_CNTDWN_OFFS_PRESP:u16 = 187; +// const NL80211_ATTR_RXMGMT_FLAGS:u16 = 188; +// const NL80211_ATTR_STA_SUPPORTED_CHANNELS:u16 = 189; +// const NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES:u16 = 190; +// const NL80211_ATTR_HANDLE_DFS:u16 = 191; +// const NL80211_ATTR_SUPPORT_5_MHZ:u16 = 192; +// const NL80211_ATTR_SUPPORT_10_MHZ:u16 = 193; +// const NL80211_ATTR_OPMODE_NOTIF:u16 = 194; +// const NL80211_ATTR_VENDOR_ID:u16 = 195; +// const NL80211_ATTR_VENDOR_SUBCMD:u16 = 196; +// const NL80211_ATTR_VENDOR_DATA:u16 = 197; +// const NL80211_ATTR_VENDOR_EVENTS:u16 = 198; +// const NL80211_ATTR_QOS_MAP:u16 = 199; +// const NL80211_ATTR_MAC_HINT:u16 = 200; +// const NL80211_ATTR_WIPHY_FREQ_HINT:u16 = 201; +// const NL80211_ATTR_MAX_AP_ASSOC_STA:u16 = 202; +// const NL80211_ATTR_TDLS_PEER_CAPABILITY:u16 = 203; +// const NL80211_ATTR_SOCKET_OWNER:u16 = 204; +// const NL80211_ATTR_CSA_C_OFFSETS_TX:u16 = 205; +const NL80211_ATTR_MAX_CSA_COUNTERS: u16 = 206; +// const NL80211_ATTR_TDLS_INITIATOR:u16 = 207; +// const NL80211_ATTR_USE_RRM:u16 = 208; +// const NL80211_ATTR_WIPHY_DYN_ACK:u16 = 209; +// const NL80211_ATTR_TSID:u16 = 210; +// const NL80211_ATTR_USER_PRIO:u16 = 211; +// const NL80211_ATTR_ADMITTED_TIME:u16 = 212; +// const NL80211_ATTR_SMPS_MODE:u16 = 213; +// const NL80211_ATTR_OPER_CLASS:u16 = 214; +// const NL80211_ATTR_MAC_MASK:u16 = 215; +const NL80211_ATTR_WIPHY_SELF_MANAGED_REG: u16 = 216; +const NL80211_ATTR_EXT_FEATURES: u16 = 217; +// const NL80211_ATTR_SURVEY_RADIO_STATS:u16 = 218; +// const NL80211_ATTR_NETNS_FD:u16 = 219; +// const NL80211_ATTR_SCHED_SCAN_DELAY:u16 = 220; +// const NL80211_ATTR_REG_INDOOR:u16 = 221; +const NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: u16 = 222; +const NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: u16 = 223; +const NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: u16 = 224; +// const NL80211_ATTR_SCHED_SCAN_PLANS:u16 = 225; +// const NL80211_ATTR_PBSS:u16 = 226; +// const NL80211_ATTR_BSS_SELECT:u16 = 227; +// const NL80211_ATTR_STA_SUPPORT_P2P_PS:u16 = 228; +// const NL80211_ATTR_PAD:u16 = 229; +const NL80211_ATTR_IFTYPE_EXT_CAPA: u16 = 230; +// const NL80211_ATTR_MU_MIMO_GROUP_DATA:u16 = 231; +// const NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR:u16 = 232; +// const NL80211_ATTR_SCAN_START_TIME_TSF:u16 = 233; +// const NL80211_ATTR_SCAN_START_TIME_TSF_BSSID:u16 = 234; +// const NL80211_ATTR_MEASUREMENT_DURATION:u16 = 235; +// const NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY:u16 = 236; +// const NL80211_ATTR_MESH_PEER_AID:u16 = 237; +// const NL80211_ATTR_NAN_MASTER_PREF:u16 = 238; +const NL80211_ATTR_BANDS: u16 = 239; +// const NL80211_ATTR_NAN_FUNC:u16 = 240; +// const NL80211_ATTR_NAN_MATCH:u16 = 241; +// const NL80211_ATTR_FILS_KEK:u16 = 242; +// const NL80211_ATTR_FILS_NONCES:u16 = 243; +// const NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED:u16 = 244; +// const NL80211_ATTR_BSSID:u16 = 245; +// const NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI:u16 = 246; +// const NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST:u16 = 247; +// const NL80211_ATTR_TIMEOUT_REASON:u16 = 248; +// const NL80211_ATTR_FILS_ERP_USERNAME:u16 = 249; +// const NL80211_ATTR_FILS_ERP_REALM:u16 = 250; +// const NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM:u16 = 251; +// const NL80211_ATTR_FILS_ERP_RRK:u16 = 252; +// const NL80211_ATTR_FILS_CACHE_ID:u16 = 253; +// const NL80211_ATTR_PMK:u16 = 254; +// const NL80211_ATTR_SCHED_SCAN_MULTI:u16 = 255; +const NL80211_ATTR_SCHED_SCAN_MAX_REQS: u16 = 256; +// const NL80211_ATTR_WANT_1X_4WAY_HS:u16 = 257; +// const NL80211_ATTR_PMKR0_NAME:u16 = 258; +// const NL80211_ATTR_PORT_AUTHORIZED:u16 = 259; +// const NL80211_ATTR_EXTERNAL_AUTH_ACTION:u16 = 260; +// const NL80211_ATTR_EXTERNAL_AUTH_SUPPORT:u16 = 261; +// const NL80211_ATTR_NSS:u16 = 262; +// const NL80211_ATTR_ACK_SIGNAL:u16 = 263; +// const NL80211_ATTR_CONTROL_PORT_OVER_NL80211:u16 = 264; const NL80211_ATTR_TXQ_STATS: u16 = 265; +const NL80211_ATTR_TXQ_LIMIT: u16 = 266; +const NL80211_ATTR_TXQ_MEMORY_LIMIT: u16 = 267; +const NL80211_ATTR_TXQ_QUANTUM: u16 = 268; +// const NL80211_ATTR_HE_CAPABILITY:u16 = 269; +// const NL80211_ATTR_FTM_RESPONDER:u16 = 270; +// const NL80211_ATTR_FTM_RESPONDER_STATS:u16 = 271; +// const NL80211_ATTR_TIMEOUT:u16 = 272; +// const NL80211_ATTR_PEER_MEASUREMENTS:u16 = 273; +// const NL80211_ATTR_AIRTIME_WEIGHT:u16 = 274; +// const NL80211_ATTR_STA_TX_POWER_SETTING:u16 = 275; +// const NL80211_ATTR_STA_TX_POWER:u16 = 276; +// const NL80211_ATTR_SAE_PASSWORD:u16 = 277; +// const NL80211_ATTR_TWT_RESPONDER:u16 = 278; +// const NL80211_ATTR_HE_OBSS_PD:u16 = 279; +// const NL80211_ATTR_WIPHY_EDMG_CHANNELS:u16 = 280; +// const NL80211_ATTR_WIPHY_EDMG_BW_CONFIG:u16 = 281; +// const NL80211_ATTR_VLAN_ID:u16 = 282; +// const NL80211_ATTR_HE_BSS_COLOR:u16 = 283; +// const NL80211_ATTR_IFTYPE_AKM_SUITES:u16 = 284; +// const NL80211_ATTR_TID_CONFIG:u16 = 285; +// const NL80211_ATTR_CONTROL_PORT_NO_PREAUTH:u16 = 286; +// const NL80211_ATTR_PMK_LIFETIME:u16 = 287; +// const NL80211_ATTR_PMK_REAUTH_THRESHOLD:u16 = 288; +// const NL80211_ATTR_RECEIVE_MULTICAST:u16 = 289; const NL80211_ATTR_WIPHY_FREQ_OFFSET: u16 = 290; +// const NL80211_ATTR_CENTER_FREQ1_OFFSET:u16 = 291; +// const NL80211_ATTR_SCAN_FREQ_KHZ:u16 = 292; +// const NL80211_ATTR_HE_6GHZ_CAPABILITY:u16 = 293; +// const NL80211_ATTR_FILS_DISCOVERY:u16 = 294; +// const NL80211_ATTR_UNSOL_BCAST_PROBE_RESP:u16 = 295; +// const NL80211_ATTR_S1G_CAPABILITY:u16 = 296; +// const NL80211_ATTR_S1G_CAPABILITY_MASK:u16 = 297; +// const NL80211_ATTR_SAE_PWE:u16 = 298; +// const NL80211_ATTR_RECONNECT_REQUESTED:u16 = 299; +// const NL80211_ATTR_SAR_SPEC:u16 = 300; +// const NL80211_ATTR_DISABLE_HE:u16 = 301; +// const NL80211_ATTR_OBSS_COLOR_BITMAP:u16 = 302; +// const NL80211_ATTR_COLOR_CHANGE_COUNT:u16 = 303; +// const NL80211_ATTR_COLOR_CHANGE_COLOR:u16 = 304; +// const NL80211_ATTR_COLOR_CHANGE_ELEMS:u16 = 305; +// const NL80211_ATTR_MBSSID_CONFIG:u16 = 306; +// const NL80211_ATTR_MBSSID_ELEMS:u16 = 307; +// const NL80211_ATTR_RADAR_BACKGROUND:u16 = 308; +// const NL80211_ATTR_AP_SETTINGS_FLAGS:u16 = 309; +// const NL80211_ATTR_EHT_CAPABILITY:u16 = 310; +// const NL80211_ATTR_DISABLE_EHT:u16 = 311; const NL80211_ATTR_MLO_LINKS: u16 = 312; -const NL80211_ATTR_MLO_LINK_ID: u16 = 313; +// Covered in mlo.rs +// const NL80211_ATTR_MLO_LINK_ID: u16 = 313; +// const NL80211_ATTR_MLD_ADDR:u16 = 314; +// const NL80211_ATTR_MLO_SUPPORT:u16 = 315; +const NL80211_ATTR_MAX_NUM_AKM_SUITES: u16 = 316; +const NL80211_ATTR_EML_CAPABILITY: u16 = 317; +const NL80211_ATTR_MLD_CAPA_AND_OPS: u16 = 318; +// const NL80211_ATTR_TX_HW_TIMESTAMP:u16 = 319; +// const NL80211_ATTR_RX_HW_TIMESTAMP:u16 = 320; +// const NL80211_ATTR_TD_BITMAP:u16 = 321; +// const NL80211_ATTR_PUNCT_BITMAP:u16 = 322; +const NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: u16 = 323; +// const NL80211_ATTR_HW_TIMESTAMP_ENABLED:u16 = 324; +// const NL80211_ATTR_EMA_RNR_ELEMS:u16 = 325; +// const NL80211_ATTR_MLO_LINK_DISABLED:u16 = 326; +// const NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA:u16 = 327; +// const NL80211_ATTR_MLO_TTLM_DLINK:u16 = 328; +// const NL80211_ATTR_MLO_TTLM_ULINK:u16 = 329; +// const NL80211_ATTR_ASSOC_SPP_AMSDU:u16 = 330; +// const NL80211_ATTR_WIPHY_RADIOS:u16 = 331; +// const NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS:u16 = 332; const ETH_ALEN: usize = 6; #[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] pub enum Nl80211Attr { - WiPhy(u32), + Wiphy(u32), + WiphyName(String), IfIndex(u32), IfName(String), IfType(Nl80211InterfaceType), + IfTypeExtCap(Vec), Mac([u8; ETH_ALEN]), + MacAddrs(Vec<[u8; ETH_ALEN]>), Wdev(u64), Generation(u32), Use4Addr(bool), - WiPhyFreq(u32), - WiPhyFreqOffset(u32), - WiPhyChannelType(Nl80211WiPhyChannelType), + WiphyFreq(u32), + WiphyFreqOffset(u32), + WiphyChannelType(Nl80211HtWiphyChannelType), ChannelWidth(Nl80211ChannelWidth), CenterFreq1(u32), CenterFreq2(u32), - WiPhyTxPowerLevel(u32), + WiphyTxPowerLevel(u32), Ssid(String), StationInfo(Vec), TransmitQueueStats(Vec), + TransmitQueueLimit(u32), + TransmitQueueMemoryLimit(u32), + TransmitQueueQuantum(u32), MloLinks(Vec), + WiphyRetryShort(u8), + WiphyRetryLong(u8), + WiphyFragThreshold(u32), + WiphyRtsThreshold(u32), + WiphyCoverageClass(u8), + MaxNumScanSsids(u8), + MaxNumSchedScanSsids(u8), + MaxScanIeLen(u16), + MaxSchedScanIeLen(u16), + MaxMatchSets(u8), + SupportIbssRsn, + SupportMeshAuth, + SupportApUapsd, + RoamSupport, + TdlsSupport, + TdlsExternalSetup, + CipherSuites(Vec), + MaxNumPmkids(u8), + ControlPortEthertype, + WiphyAntennaAvailTx(u32), + WiphyAntennaAvailRx(u32), + ApProbeRespOffload(u32), + WiphyAntennaTx(u32), + WiphyAntennaRx(u32), + SupportedIftypes(Vec), + WiphyBands(Vec), + /// flag attribute, indicate userspace supports + /// receiving the data for a single wiphy split across multiple + /// messages, given with wiphy dump message + SplitWiphyDump, + SupportedCommand(Vec), + /// in milliseconds + MaxRemainOnChannelDuration(u32), + OffchannelTxOk, + WowlanTrigersSupport(Vec), + SoftwareIftypes(Vec), + Features(Nl80211Features), + ExtFeatures(Vec), + InterfaceCombination(Vec), + HtCapabilityMask(Nl80211HtCapabilityMask), + TxFrameTypes(Vec), + RxFrameTypes(Vec), + MaxNumSchedScanPlans(u32), + MaxScanPlanInterval(u32), + MaxScanPlanIterations(u32), + ExtCap(Nl80211ExtendedCapability), + ExtCapMask(Nl80211ExtendedCapability), + VhtCap(Nl80211VhtCapability), + VhtCapMask(Nl80211VhtCapability), + MaxCsaCounters(u8), + WiphySelfManagedReg, + SchedScanMaxReqs(u32), + EmlCapability(u16), + MldCapaAndOps(u16), + Bands(Nl80211BandTypes), + /// Maximum number of AKM suites allowed for connect command. + MaxNumAkmSuites(u16), + /// Maximum number of peers that HW timestamping can be enabled for + /// concurrently. A value of 0xffff indicates setting for all peers(i.e. + /// not specifying an address with set hardware timestamp) is + /// supported. + MaxHwTimestampPeers(u16), Other(DefaultNla), } @@ -64,48 +551,184 @@ impl Nla for Nl80211Attr { fn value_len(&self) -> usize { match self { Self::IfIndex(_) - | Self::WiPhy(_) + | Self::Wiphy(_) | Self::IfType(_) | Self::Generation(_) - | Self::WiPhyFreq(_) - | Self::WiPhyFreqOffset(_) - | Self::WiPhyChannelType(_) + | Self::WiphyFreq(_) + | Self::WiphyFreqOffset(_) + | Self::WiphyChannelType(_) | Self::CenterFreq1(_) | Self::CenterFreq2(_) - | Self::WiPhyTxPowerLevel(_) - | Self::ChannelWidth(_) => 4, + | Self::WiphyTxPowerLevel(_) + | Self::ChannelWidth(_) + | Self::WiphyFragThreshold(_) + | Self::WiphyRtsThreshold(_) + | Self::WiphyAntennaAvailTx(_) + | Self::WiphyAntennaAvailRx(_) + | Self::ApProbeRespOffload(_) + | Self::WiphyAntennaTx(_) + | Self::WiphyAntennaRx(_) + | Self::MaxNumSchedScanPlans(_) + | Self::MaxScanPlanInterval(_) + | Self::MaxScanPlanIterations(_) + | Self::SchedScanMaxReqs(_) + | Self::TransmitQueueLimit(_) + | Self::TransmitQueueMemoryLimit(_) + | Self::TransmitQueueQuantum(_) => 4, Self::Wdev(_) => 8, - Self::IfName(ref s) | Self::Ssid(ref s) => s.len() + 1, + Self::IfName(s) | Self::Ssid(s) | Self::WiphyName(s) => s.len() + 1, Self::Mac(_) => ETH_ALEN, + Self::MacAddrs(s) => { + MacAddressNlas::from(s).as_slice().buffer_len() + } Self::Use4Addr(_) => 1, - Self::TransmitQueueStats(ref nlas) => nlas.as_slice().buffer_len(), - Self::StationInfo(ref nlas) => nlas.as_slice().buffer_len(), - Self::MloLinks(ref links) => links.as_slice().buffer_len(), + Self::WiphyRetryShort(_) + | Self::WiphyRetryLong(_) + | Self::WiphyCoverageClass(_) + | Self::MaxNumScanSsids(_) + | Self::MaxNumSchedScanSsids(_) + | Self::MaxMatchSets(_) + | Self::MaxNumPmkids(_) => 1, + Self::TransmitQueueStats(nlas) => nlas.as_slice().buffer_len(), + Self::StationInfo(nlas) => nlas.as_slice().buffer_len(), + Self::MloLinks(links) => links.as_slice().buffer_len(), + Self::MaxScanIeLen(_) | Self::MaxSchedScanIeLen(_) => 2, + Self::SupportIbssRsn + | Self::SupportMeshAuth + | Self::SupportApUapsd + | Self::RoamSupport + | Self::TdlsSupport + | Self::TdlsExternalSetup + | Self::ControlPortEthertype + | Self::OffchannelTxOk + | Self::WiphySelfManagedReg => 0, + Self::CipherSuites(s) => 4 * s.len(), + Self::SupportedIftypes(s) => s.as_slice().buffer_len(), + Self::WiphyBands(s) => s.as_slice().buffer_len(), + Self::SplitWiphyDump => 0, + Self::SupportedCommand(s) => { + Nl80211Commands::from(s).as_slice().buffer_len() + } + Self::MaxRemainOnChannelDuration(_) => 4, + Self::WowlanTrigersSupport(s) => s.as_slice().buffer_len(), + Self::SoftwareIftypes(s) => { + Nl80211InterfaceTypes::from(s).as_slice().buffer_len() + } + Self::Features(_) => 4, + Self::ExtFeatures(_) => Nl80211ExtFeatures::LENGTH, + Self::InterfaceCombination(s) => s.as_slice().buffer_len(), + Self::HtCapabilityMask(_) => Nl80211HtCapabilityMask::LENGTH, + Self::TxFrameTypes(s) => s.as_slice().buffer_len(), + Self::RxFrameTypes(s) => s.as_slice().buffer_len(), + Self::ExtCap(v) => v.len(), + Self::ExtCapMask(v) => v.len(), + Self::VhtCap(v) => v.buffer_len(), + Self::VhtCapMask(v) => v.buffer_len(), + Self::MaxCsaCounters(_) => 1, + Self::IfTypeExtCap(s) => { + Nl80211IfTypeExtCapas::from(s).as_slice().buffer_len() + } + Self::EmlCapability(_) + | Self::MldCapaAndOps(_) + | Self::MaxNumAkmSuites(_) + | Self::MaxHwTimestampPeers(_) => 2, + Self::Bands(_) => Nl80211BandTypes::LENGTH, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { - Self::WiPhy(_) => NL80211_ATTR_WIPHY, + Self::Wiphy(_) => NL80211_ATTR_WIPHY, + Self::WiphyName(_) => NL80211_ATTR_WIPHY_NAME, Self::IfIndex(_) => NL80211_ATTR_IFINDEX, Self::IfName(_) => NL80211_ATTR_IFNAME, Self::IfType(_) => NL80211_ATTR_IFTYPE, Self::Mac(_) => NL80211_ATTR_MAC, + Self::MacAddrs(_) => NL80211_ATTR_MAC_ADDRS, Self::Wdev(_) => NL80211_ATTR_WDEV, Self::Generation(_) => NL80211_ATTR_GENERATION, Self::Use4Addr(_) => NL80211_ATTR_4ADDR, - Self::WiPhyFreq(_) => NL80211_ATTR_WIPHY_FREQ, - Self::WiPhyFreqOffset(_) => NL80211_ATTR_WIPHY_FREQ_OFFSET, - Self::WiPhyChannelType(_) => NL80211_ATTR_WIPHY_CHANNEL_TYPE, + Self::WiphyFreq(_) => NL80211_ATTR_WIPHY_FREQ, + Self::WiphyFreqOffset(_) => NL80211_ATTR_WIPHY_FREQ_OFFSET, + Self::WiphyChannelType(_) => NL80211_ATTR_WIPHY_CHANNEL_TYPE, Self::ChannelWidth(_) => NL80211_ATTR_CHANNEL_WIDTH, Self::CenterFreq1(_) => NL80211_ATTR_CENTER_FREQ1, Self::CenterFreq2(_) => NL80211_ATTR_CENTER_FREQ2, - Self::WiPhyTxPowerLevel(_) => NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + Self::WiphyTxPowerLevel(_) => NL80211_ATTR_WIPHY_TX_POWER_LEVEL, Self::Ssid(_) => NL80211_ATTR_SSID, Self::StationInfo(_) => NL80211_ATTR_STA_INFO, Self::TransmitQueueStats(_) => NL80211_ATTR_TXQ_STATS, + Self::TransmitQueueLimit(_) => NL80211_ATTR_TXQ_LIMIT, + Self::TransmitQueueMemoryLimit(_) => NL80211_ATTR_TXQ_MEMORY_LIMIT, + Self::TransmitQueueQuantum(_) => NL80211_ATTR_TXQ_QUANTUM, Self::MloLinks(_) => NL80211_ATTR_MLO_LINKS, + Self::WiphyRetryShort(_) => NL80211_ATTR_WIPHY_RETRY_SHORT, + Self::WiphyRetryLong(_) => NL80211_ATTR_WIPHY_RETRY_LONG, + Self::WiphyFragThreshold(_) => NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + Self::WiphyRtsThreshold(_) => NL80211_ATTR_WIPHY_RTS_THRESHOLD, + Self::WiphyCoverageClass(_) => NL80211_ATTR_WIPHY_COVERAGE_CLASS, + Self::MaxNumScanSsids(_) => NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + Self::MaxNumSchedScanSsids(_) => { + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS + } + Self::MaxScanIeLen(_) => NL80211_ATTR_MAX_SCAN_IE_LEN, + Self::MaxSchedScanIeLen(_) => NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + Self::MaxMatchSets(_) => NL80211_ATTR_MAX_MATCH_SETS, + Self::SupportIbssRsn => NL80211_ATTR_SUPPORT_IBSS_RSN, + Self::SupportMeshAuth => NL80211_ATTR_SUPPORT_MESH_AUTH, + Self::SupportApUapsd => NL80211_ATTR_SUPPORT_AP_UAPSD, + Self::RoamSupport => NL80211_ATTR_ROAM_SUPPORT, + Self::TdlsSupport => NL80211_ATTR_TDLS_SUPPORT, + Self::TdlsExternalSetup => NL80211_ATTR_TDLS_EXTERNAL_SETUP, + Self::CipherSuites(_) => NL80211_ATTR_CIPHER_SUITES, + Self::MaxNumPmkids(_) => NL80211_ATTR_MAX_NUM_PMKIDS, + Self::ControlPortEthertype => NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + Self::WiphyAntennaAvailTx(_) => NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + Self::WiphyAntennaAvailRx(_) => NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + Self::ApProbeRespOffload(_) => NL80211_ATTR_PROBE_RESP_OFFLOAD, + Self::WiphyAntennaTx(_) => NL80211_ATTR_WIPHY_ANTENNA_TX, + Self::WiphyAntennaRx(_) => NL80211_ATTR_WIPHY_ANTENNA_RX, + Self::SupportedIftypes(_) => NL80211_ATTR_SUPPORTED_IFTYPES, + Self::WiphyBands(_) => NL80211_ATTR_WIPHY_BANDS, + Self::SplitWiphyDump => NL80211_ATTR_SPLIT_WIPHY_DUMP, + Self::SupportedCommand(_) => NL80211_ATTR_SUPPORTED_COMMANDS, + Self::MaxRemainOnChannelDuration(_) => { + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION + } + Self::OffchannelTxOk => NL80211_ATTR_OFFCHANNEL_TX_OK, + Self::WowlanTrigersSupport(_) => { + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED + } + Self::SoftwareIftypes(_) => NL80211_ATTR_SOFTWARE_IFTYPES, + Self::Features(_) => NL80211_ATTR_FEATURE_FLAGS, + Self::ExtFeatures(_) => NL80211_ATTR_EXT_FEATURES, + Self::InterfaceCombination(_) => { + NL80211_ATTR_INTERFACE_COMBINATIONS + } + Self::HtCapabilityMask(_) => NL80211_ATTR_HT_CAPABILITY_MASK, + Self::TxFrameTypes(_) => NL80211_ATTR_TX_FRAME_TYPES, + Self::RxFrameTypes(_) => NL80211_ATTR_RX_FRAME_TYPES, + Self::MaxNumSchedScanPlans(_) => { + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS + } + Self::MaxScanPlanInterval(_) => NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, + Self::MaxScanPlanIterations(_) => { + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS + } + Self::ExtCap(_) => NL80211_ATTR_EXT_CAPA, + Self::ExtCapMask(_) => NL80211_ATTR_EXT_CAPA_MASK, + Self::VhtCap(_) => NL80211_ATTR_VHT_CAPABILITY, + Self::VhtCapMask(_) => NL80211_ATTR_VHT_CAPABILITY_MASK, + Self::MaxCsaCounters(_) => NL80211_ATTR_MAX_CSA_COUNTERS, + Self::WiphySelfManagedReg => NL80211_ATTR_WIPHY_SELF_MANAGED_REG, + Self::SchedScanMaxReqs(_) => NL80211_ATTR_SCHED_SCAN_MAX_REQS, + Self::IfTypeExtCap(_) => NL80211_ATTR_IFTYPE_EXT_CAPA, + Self::EmlCapability(_) => NL80211_ATTR_EML_CAPABILITY, + Self::MldCapaAndOps(_) => NL80211_ATTR_MLD_CAPA_AND_OPS, + Self::Bands(_) => NL80211_ATTR_BANDS, + Self::MaxNumAkmSuites(_) => NL80211_ATTR_MAX_NUM_AKM_SUITES, + Self::MaxHwTimestampPeers(_) => NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS, Self::Other(attr) => attr.kind(), } } @@ -113,31 +736,103 @@ impl Nla for Nl80211Attr { fn emit_value(&self, buffer: &mut [u8]) { match self { Self::IfIndex(d) - | Self::WiPhy(d) + | Self::Wiphy(d) | Self::Generation(d) - | Self::WiPhyFreq(d) - | Self::WiPhyFreqOffset(d) + | Self::WiphyFreq(d) + | Self::WiphyFreqOffset(d) | Self::CenterFreq1(d) | Self::CenterFreq2(d) - | Self::WiPhyTxPowerLevel(d) => NativeEndian::write_u32(buffer, *d), - Self::Wdev(d) => NativeEndian::write_u64(buffer, *d), - Self::IfType(d) => NativeEndian::write_u32(buffer, (*d).into()), - Self::Mac(ref s) => buffer.copy_from_slice(s), - Self::IfName(ref s) | Self::Ssid(ref s) => { + | Self::WiphyTxPowerLevel(d) + | Self::WiphyFragThreshold(d) + | Self::WiphyRtsThreshold(d) + | Self::WiphyAntennaAvailTx(d) + | Self::WiphyAntennaAvailRx(d) + | Self::ApProbeRespOffload(d) + | Self::WiphyAntennaTx(d) + | Self::WiphyAntennaRx(d) + | Self::MaxNumSchedScanPlans(d) + | Self::MaxScanPlanInterval(d) + | Self::MaxScanPlanIterations(d) + | Self::SchedScanMaxReqs(d) + | Self::TransmitQueueLimit(d) + | Self::TransmitQueueMemoryLimit(d) + | Self::TransmitQueueQuantum(d) => write_u32(buffer, *d), + Self::MaxScanIeLen(d) | Self::MaxSchedScanIeLen(d) => { + write_u16(buffer, *d) + } + Self::Wdev(d) => write_u64(buffer, *d), + Self::IfType(d) => write_u32(buffer, (*d).into()), + Self::Mac(s) => buffer.copy_from_slice(s), + Self::MacAddrs(s) => { + MacAddressNlas::from(s).as_slice().emit(buffer) + } + Self::IfName(s) | Self::Ssid(s) | Self::WiphyName(s) => { buffer[..s.len()].copy_from_slice(s.as_bytes()); buffer[s.len()] = 0; } Self::Use4Addr(d) => buffer[0] = *d as u8, - Self::WiPhyChannelType(d) => { - NativeEndian::write_u32(buffer, (*d).into()) + Self::SupportIbssRsn + | Self::SupportMeshAuth + | Self::SupportApUapsd + | Self::RoamSupport + | Self::TdlsSupport + | Self::TdlsExternalSetup + | Self::ControlPortEthertype + | Self::OffchannelTxOk + | Self::WiphySelfManagedReg => (), + Self::WiphyChannelType(d) => write_u32(buffer, (*d).into()), + Self::ChannelWidth(d) => write_u32(buffer, (*d).into()), + Self::StationInfo(nlas) => nlas.as_slice().emit(buffer), + Self::TransmitQueueStats(nlas) => nlas.as_slice().emit(buffer), + Self::MloLinks(links) => links.as_slice().emit(buffer), + Self::WiphyRetryShort(d) + | Self::WiphyRetryLong(d) + | Self::WiphyCoverageClass(d) + | Self::MaxNumScanSsids(d) + | Self::MaxNumSchedScanSsids(d) + | Self::MaxMatchSets(d) + | Self::MaxNumPmkids(d) => buffer[0] = *d, + Self::CipherSuites(suits) => { + let nums: Vec = + suits.as_slice().iter().map(|s| u32::from(*s)).collect(); + for (i, v) in nums.as_slice().iter().enumerate() { + buffer[i * 4..(i + 1) * 4] + .copy_from_slice(&v.to_ne_bytes()); + } + } + Self::SupportedIftypes(s) => s.as_slice().emit(buffer), + Self::WiphyBands(s) => s.as_slice().emit(buffer), + Self::SplitWiphyDump => (), + Self::SupportedCommand(s) => { + Nl80211Commands::from(s).as_slice().emit(buffer) + } + Self::MaxRemainOnChannelDuration(d) => write_u32(buffer, *d), + Self::WowlanTrigersSupport(s) => s.as_slice().emit(buffer), + Self::SoftwareIftypes(s) => { + Nl80211InterfaceTypes::from(s).as_slice().emit(buffer) } - Self::ChannelWidth(d) => { - NativeEndian::write_u32(buffer, (*d).into()) + Self::Features(d) => { + buffer.copy_from_slice(&d.bits().to_ne_bytes()) } - Self::StationInfo(ref nlas) => nlas.as_slice().emit(buffer), - Self::TransmitQueueStats(ref nlas) => nlas.as_slice().emit(buffer), - Self::MloLinks(ref links) => links.as_slice().emit(buffer), - Self::Other(ref attr) => attr.emit(buffer), + Self::ExtFeatures(s) => Nl80211ExtFeatures::from(s).emit(buffer), + Self::InterfaceCombination(s) => s.as_slice().emit(buffer), + Self::HtCapabilityMask(s) => s.emit(buffer), + Self::TxFrameTypes(s) => s.as_slice().emit(buffer), + Self::RxFrameTypes(s) => s.as_slice().emit(buffer), + Self::ExtCap(v) => v.emit(buffer), + Self::ExtCapMask(v) => v.emit(buffer), + Self::VhtCap(v) => v.emit(buffer), + Self::VhtCapMask(v) => v.emit(buffer), + Self::MaxCsaCounters(v) => buffer[0] = *v, + Self::IfTypeExtCap(s) => { + Nl80211IfTypeExtCapas::from(s).as_slice().emit(buffer) + } + Self::EmlCapability(d) + | Self::MldCapaAndOps(d) + | Self::MaxNumAkmSuites(d) + | Self::MaxHwTimestampPeers(d) => write_u16(buffer, *d), + Self::Bands(v) => v.emit(buffer), + Self::Other(attr) => attr.emit(buffer), } } } @@ -154,7 +849,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { NL80211_ATTR_WIPHY => { let err_msg = format!("Invalid NL80211_ATTR_WIPHY value {:?}", payload); - Self::WiPhy(parse_u32(payload).context(err_msg)?) + Self::Wiphy(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_NAME => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_NAME value {:?}", + payload + ); + Self::WiphyName(parse_string(payload).context(err_msg)?) } NL80211_ATTR_IFNAME => { let err_msg = @@ -162,9 +864,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { Self::IfName(parse_string(payload).context(err_msg)?) } NL80211_ATTR_IFTYPE => { - let err_msg = - format!("Invalid NL80211_ATTR_IFTYPE value {:?}", payload); - Self::IfType(parse_u32(payload).context(err_msg)?.into()) + Self::IfType(Nl80211InterfaceType::parse(payload)?) } NL80211_ATTR_WDEV => { let err_msg = @@ -182,6 +882,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { ) .into()); }), + NL80211_ATTR_MAC_ADDRS => { + Self::MacAddrs(MacAddressNlas::parse(payload)?.into()) + } NL80211_ATTR_GENERATION => { let err_msg = format!( "Invalid NL80211_ATTR_GENERATION value {:?}", @@ -199,21 +902,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { "Invalid NL80211_ATTR_WIPHY_FREQ value {:?}", payload ); - Self::WiPhyFreq(parse_u32(payload).context(err_msg)?) + Self::WiphyFreq(parse_u32(payload).context(err_msg)?) } NL80211_ATTR_WIPHY_FREQ_OFFSET => { let err_msg = format!( "Invalid NL80211_ATTR_WIPHY_FREQ_OFFSET value {:?}", payload ); - Self::WiPhyFreqOffset(parse_u32(payload).context(err_msg)?) + Self::WiphyFreqOffset(parse_u32(payload).context(err_msg)?) } NL80211_ATTR_WIPHY_CHANNEL_TYPE => { let err_msg = format!( "Invalid NL80211_ATTR_WIPHY_CHANNEL_TYPE value {:?}", payload ); - Self::WiPhyChannelType( + Self::WiphyChannelType( parse_u32(payload).context(err_msg)?.into(), ) } @@ -243,7 +946,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { "Invalid NL80211_ATTR_WIPHY_TX_POWER_LEVEL value {:?}", payload ); - Self::WiPhyTxPowerLevel(parse_u32(payload).context(err_msg)?) + Self::WiphyTxPowerLevel(parse_u32(payload).context(err_msg)?) } NL80211_ATTR_SSID => { let err_msg = @@ -280,6 +983,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { } Self::TransmitQueueStats(nlas) } + NL80211_ATTR_TXQ_LIMIT => { + Self::TransmitQueueLimit(parse_u32(payload).context( + format!("Invalid NL80211_ATTR_TXQ_LIMIT {payload:?}"), + )?) + } + NL80211_ATTR_TXQ_MEMORY_LIMIT => Self::TransmitQueueMemoryLimit( + parse_u32(payload).context(format!( + "Invalid NL80211_ATTR_TXQ_MEMORY_LIMIT {payload:?}" + ))?, + ), + NL80211_ATTR_TXQ_QUANTUM => { + Self::TransmitQueueQuantum(parse_u32(payload).context( + format!("Invalid NL80211_ATTR_TXQ_QUANTUM {payload:?}"), + )?) + } NL80211_ATTR_MLO_LINKS => { let err_msg = format!( "Invalid NL80211_ATTR_MLO_LINKS value {:?}", @@ -294,127 +1012,329 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Attr { } Self::MloLinks(links) } - _ => Self::Other( - DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum Nl80211MloLinkNla { - Id(u8), - Mac([u8; ETH_ALEN]), - Other(DefaultNla), -} - -impl Nla for Nl80211MloLinkNla { - fn value_len(&self) -> usize { - match self { - Self::Id(_) => 1, - Self::Mac(_) => ETH_ALEN, - Self::Other(attr) => attr.value_len(), - } - } - - fn kind(&self) -> u16 { - match self { - Self::Id(_) => NL80211_ATTR_MLO_LINK_ID, - Self::Mac(_) => NL80211_ATTR_MAC, - Self::Other(attr) => attr.kind(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - match self { - Self::Id(d) => buffer[0] = *d, - Self::Mac(ref s) => buffer.copy_from_slice(s), - Self::Other(ref attr) => attr.emit(buffer), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> - for Nl80211MloLinkNla -{ - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - let payload = buf.value(); - Ok(match buf.kind() { - NL80211_ATTR_MLO_LINK_ID => { + NL80211_ATTR_WIPHY_RETRY_SHORT => { let err_msg = format!( - "Invalid NL80211_ATTR_MLO_LINK_ID value {:?}", + "Invalid NL80211_ATTR_WIPHY_RETRY_SHORT value {:?}", payload ); - Self::Id(parse_u8(payload).context(err_msg)?) + Self::WiphyRetryShort(parse_u8(payload).context(err_msg)?) } - NL80211_ATTR_MAC => Self::Mac(if payload.len() == ETH_ALEN { - let mut ret = [0u8; ETH_ALEN]; - ret.copy_from_slice(&payload[..ETH_ALEN]); - ret - } else { - return Err(format!( - "Invalid length of NL80211_ATTR_MAC, expected length {} got {:?}", - ETH_ALEN, payload + NL80211_ATTR_WIPHY_RETRY_LONG => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_RETRY_LONG value {:?}", + payload + ); + Self::WiphyRetryLong(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_FRAG_THRESHOLD => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_FRAG_THRESHOLD value {:?}", + payload + ); + Self::WiphyFragThreshold(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_RTS_THRESHOLD => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_RTS_THRESHOLD value {:?}", + payload + ); + Self::WiphyRtsThreshold(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_COVERAGE_CLASS => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_COVERAGE_CLASS value {:?}", + payload + ); + Self::WiphyCoverageClass(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_MAX_NUM_SCAN_SSIDS => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_NUM_SCAN_SSIDS value {:?}", + payload + ); + Self::MaxNumScanSsids(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS value {:?}", + payload + ); + Self::MaxNumSchedScanSsids(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_MAX_SCAN_IE_LEN => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_SCAN_IE_LEN value {:?}", + payload + ); + Self::MaxScanIeLen(parse_u16(payload).context(err_msg)?) + } + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN value {:?}", + payload + ); + Self::MaxSchedScanIeLen(parse_u16(payload).context(err_msg)?) + } + NL80211_ATTR_MAX_MATCH_SETS => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_MATCH_SETS value {:?}", + payload + ); + Self::MaxMatchSets(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_SUPPORT_IBSS_RSN => Self::SupportIbssRsn, + NL80211_ATTR_SUPPORT_MESH_AUTH => Self::SupportMeshAuth, + NL80211_ATTR_SUPPORT_AP_UAPSD => Self::SupportApUapsd, + NL80211_ATTR_ROAM_SUPPORT => Self::RoamSupport, + NL80211_ATTR_TDLS_SUPPORT => Self::TdlsSupport, + NL80211_ATTR_TDLS_EXTERNAL_SETUP => Self::TdlsExternalSetup, + NL80211_ATTR_CIPHER_SUITES => { + let err_msg = format!( + "Invalid NL80211_ATTR_CIPHER_SUITES value {:?}", + payload + ); + let mut suits = Vec::new(); + for i in 0..(payload.len() / 4) { + suits.push( + parse_u32(&payload[i * 4..(i + 1) * 4]) + .context(err_msg.clone())? + .into(), + ); + } + Self::CipherSuites(suits) + } + NL80211_ATTR_MAX_NUM_PMKIDS => { + let err_msg = format!( + "Invalid NL80211_ATTR_MAX_NUM_PMKIDS value {:?}", + payload + ); + Self::MaxNumPmkids(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_CONTROL_PORT_ETHERTYPE => Self::ControlPortEthertype, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX value {:?}", + payload + ); + Self::WiphyAntennaAvailTx(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX value {:?}", + payload + ); + Self::WiphyAntennaAvailRx(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_PROBE_RESP_OFFLOAD => { + let err_msg = format!( + "Invalid NL80211_ATTR_PROBE_RESP_OFFLOAD value {:?}", + payload + ); + Self::ApProbeRespOffload(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_ANTENNA_TX => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_ANTENNA_TX value {:?}", + payload + ); + Self::WiphyAntennaTx(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_WIPHY_ANTENNA_RX => { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_ANTENNA_RX value {:?}", + payload + ); + Self::WiphyAntennaRx(parse_u32(payload).context(err_msg)?) + } + NL80211_ATTR_SUPPORTED_IFTYPES => { + let err_msg = format!( + "Invalid NL80211_ATTR_SUPPORTED_IFTYPES value {:?}", + payload + ); + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + nlas.push( + Nl80211IfMode::parse(nla).context(err_msg.clone())?, + ); + } + Self::SupportedIftypes(nlas) + } + NL80211_ATTR_WIPHY_BANDS => { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = format!( + "Invalid NL80211_ATTR_WIPHY_BANDS value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211Band::parse(nla)?); + } + Self::WiphyBands(nlas) + } + NL80211_ATTR_SPLIT_WIPHY_DUMP => Self::SplitWiphyDump, + NL80211_ATTR_SUPPORTED_COMMANDS => { + Self::SupportedCommand(Nl80211Commands::parse(payload)?.into()) + } + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION => { + let err_msg = format!( + "Invalid \ + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION {payload:?}" + ); + Self::MaxRemainOnChannelDuration( + parse_u32(payload).context(err_msg)?, ) - .into()); - }), + } + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED => { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = format!( + "Invalid NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED \ + value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211WowlanTrigersSupport::parse(nla)?); + } + Self::WowlanTrigersSupport(nlas) + } + NL80211_ATTR_OFFCHANNEL_TX_OK => Self::OffchannelTxOk, + NL80211_ATTR_SOFTWARE_IFTYPES => Self::SoftwareIftypes( + Nl80211InterfaceTypes::parse( + payload, + "NL80211_ATTR_SOFTWARE_IFTYPES", + )? + .0, + ), + NL80211_ATTR_FEATURE_FLAGS => Self::Features( + Nl80211Features::from_bits_retain(parse_u32(payload).context( + format!("Invalid NL80211_ATTR_FEATURE_FLAGS {payload:?}"), + )?), + ), + NL80211_ATTR_EXT_FEATURES => { + Self::ExtFeatures(Nl80211ExtFeatures::parse(payload)?.0) + } + NL80211_ATTR_INTERFACE_COMBINATIONS => { + let mut nlas = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let err_msg = format!( + "Invalid NL80211_ATTR_INTERFACE_COMBINATIONS \ + value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211IfaceComb::parse_with_param( + nla, + index as u16, + )?); + } + Self::InterfaceCombination(nlas) + } + NL80211_ATTR_HT_CAPABILITY_MASK => { + Self::HtCapabilityMask(Nl80211HtCapabilityMask::new(payload)) + } + NL80211_ATTR_RX_FRAME_TYPES => { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = format!( + "Invalid NL80211_ATTR_RX_FRAME_TYPES value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211IfaceFrameType::parse(nla)?); + } + Self::RxFrameTypes(nlas) + } + NL80211_ATTR_TX_FRAME_TYPES => { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = format!( + "Invalid NL80211_ATTR_RX_FRAME_TYPES value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211IfaceFrameType::parse(nla)?); + } + Self::TxFrameTypes(nlas) + } + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS => { + Self::MaxNumSchedScanPlans(parse_u32(payload).context( + format!( + "Invalid NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS \ + {payload:?}" + ), + )?) + } + NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL => Self::MaxScanPlanInterval( + parse_u32(payload).context(format!( + "Invalid NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL \ + {payload:?}" + ))?, + ), + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS => { + Self::MaxScanPlanIterations(parse_u32(payload).context( + format!( + "Invalid NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS \ + {payload:?}" + ), + )?) + } + NL80211_ATTR_EXT_CAPA => { + Self::ExtCap(Nl80211ExtendedCapability::new(payload)) + } + NL80211_ATTR_EXT_CAPA_MASK => { + Self::ExtCapMask(Nl80211ExtendedCapability::new(payload)) + } + NL80211_ATTR_VHT_CAPABILITY => { + Self::VhtCap(Nl80211VhtCapability::parse(payload)?) + } + NL80211_ATTR_VHT_CAPABILITY_MASK => { + Self::VhtCapMask(Nl80211VhtCapability::parse(payload)?) + } + NL80211_ATTR_MAX_CSA_COUNTERS => { + Self::MaxCsaCounters(parse_u8(payload).context(format!( + "Invalid NL80211_ATTR_MAX_CSA_COUNTERS {:?}", + payload + ))?) + } + NL80211_ATTR_WIPHY_SELF_MANAGED_REG => Self::WiphySelfManagedReg, + NL80211_ATTR_SCHED_SCAN_MAX_REQS => { + Self::SchedScanMaxReqs(parse_u32(payload).context(format!( + "Invalid NL80211_ATTR_SCHED_SCAN_MAX_REQS {:?}", + payload + ))?) + } + NL80211_ATTR_IFTYPE_EXT_CAPA => { + Self::IfTypeExtCap(Nl80211IfTypeExtCapas::parse(buf)?.into()) + } + NL80211_ATTR_EML_CAPABILITY => { + Self::EmlCapability(parse_u16(payload).context(format!( + "Invalid NL80211_ATTR_EML_CAPABILITY {payload:?}" + ))?) + } + NL80211_ATTR_MLD_CAPA_AND_OPS => { + Self::MldCapaAndOps(parse_u16(payload).context(format!( + "Invalid NL80211_ATTR_MLD_CAPA_AND_OPS {payload:?}" + ))?) + } + NL80211_ATTR_BANDS => { + Self::Bands(Nl80211BandTypes::parse(payload)?) + } + NL80211_ATTR_MAX_NUM_AKM_SUITES => { + Self::MaxNumAkmSuites(parse_u16(payload).context(format!( + "Invalid NL80211_ATTR_MAX_NUM_AKM_SUITES {:?}", + payload + ))?) + } + NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS => Self::MaxHwTimestampPeers( + parse_u16(payload).context(format!( + "Invalid NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS {:?}", + payload + ))?, + ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct Nl80211MloLink { - pub id: u8, - pub mac: [u8; ETH_ALEN], -} - -impl Nla for Nl80211MloLink { - fn value_len(&self) -> usize { - Vec::::from(self).as_slice().buffer_len() - } - - fn kind(&self) -> u16 { - self.id as u16 + 1 - } - - fn emit_value(&self, buffer: &mut [u8]) { - Vec::::from(self).as_slice().emit(buffer) - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> - for Nl80211MloLink -{ - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - let mut ret = Self::default(); - let payload = buf.value(); - let err_msg = - format!("Invalid NL80211_ATTR_MLO_LINKS value {:?}", payload); - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err_msg.clone())?; - match Nl80211MloLinkNla::parse(nla).context(err_msg.clone())? { - Nl80211MloLinkNla::Id(d) => ret.id = d, - Nl80211MloLinkNla::Mac(s) => ret.mac = s, - Nl80211MloLinkNla::Other(attr) => { - log::warn!( - "Got unsupported NL80211_ATTR_MLO_LINKS value {:?}", - attr - ) - } - } - } - Ok(ret) - } -} - -impl From<&Nl80211MloLink> for Vec { - fn from(link: &Nl80211MloLink) -> Self { - vec![ - Nl80211MloLinkNla::Id(link.id), - Nl80211MloLinkNla::Mac(link.mac), - ] - } -} diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 0000000..709cfe6 --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT + +pub(crate) fn write_u16(buffer: &mut [u8], value: u16) { + buffer[..2].copy_from_slice(&value.to_ne_bytes()) +} + +pub(crate) fn write_u16_le(buffer: &mut [u8], value: u16) { + buffer[..2].copy_from_slice(&value.to_le_bytes()) +} + +pub(crate) fn write_u32(buffer: &mut [u8], value: u32) { + buffer[..4].copy_from_slice(&value.to_ne_bytes()) +} + +pub(crate) fn write_u64(buffer: &mut [u8], value: u64) { + buffer[..8].copy_from_slice(&value.to_ne_bytes()) +} + +pub(crate) fn get_bit(data: &[u8], pos: usize) -> bool { + let index: usize = pos / 8; + let bit_pos: usize = pos % 8; + if data.len() < index { + panic!( + "BUG: get_bit(): out of index: got data {:?} pos {pos}", + data + ); + } + (data[index] & 1u8 << bit_pos) >= 1 +} + +pub(crate) fn get_bits_as_u8(data: &[u8], start: usize, end: usize) -> u8 { + if (end - start) >= 8 { + panic!( + "BUG: get_bits_as_u8(): more than 8 bits defined by \ + start({start}) and end({end})" + ); + } + + let mut ret = 0u8; + for pos in start..(end + 1) { + if get_bit(data, pos) { + ret |= 1 << (pos - start); + } + } + ret +} diff --git a/src/channel.rs b/src/channel.rs index d7a82e8..6cba02e 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,43 +1,5 @@ // SPDX-License-Identifier: MIT -const NL80211_CHAN_NO_HT: u32 = 0; -const NL80211_CHAN_HT20: u32 = 1; -const NL80211_CHAN_HT40MINUS: u32 = 2; -const NL80211_CHAN_HT40PLUS: u32 = 3; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Nl80211WiPhyChannelType { - NoHT, - HT20, - HT40Minus, - HT40Plus, - Other(u32), -} - -impl From for Nl80211WiPhyChannelType { - fn from(d: u32) -> Self { - match d { - NL80211_CHAN_NO_HT => Self::NoHT, - NL80211_CHAN_HT20 => Self::HT20, - NL80211_CHAN_HT40MINUS => Self::HT40Plus, - NL80211_CHAN_HT40PLUS => Self::HT40Plus, - _ => Self::Other(d), - } - } -} - -impl From for u32 { - fn from(v: Nl80211WiPhyChannelType) -> u32 { - match v { - Nl80211WiPhyChannelType::NoHT => NL80211_CHAN_NO_HT, - Nl80211WiPhyChannelType::HT20 => NL80211_CHAN_HT20, - Nl80211WiPhyChannelType::HT40Minus => NL80211_CHAN_HT40MINUS, - Nl80211WiPhyChannelType::HT40Plus => NL80211_CHAN_HT40PLUS, - Nl80211WiPhyChannelType::Other(d) => d, - } - } -} - const NL80211_CHAN_WIDTH_20_NOHT: u32 = 0; const NL80211_CHAN_WIDTH_20: u32 = 1; const NL80211_CHAN_WIDTH_40: u32 = 2; diff --git a/src/error.rs b/src/error.rs index bf9deaf..d4e6f6c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,10 +4,11 @@ use thiserror::Error; use netlink_packet_core::{ErrorMessage, NetlinkMessage}; use netlink_packet_generic::GenlMessage; +use netlink_packet_utils::DecodeError; use crate::Nl80211Message; -#[derive(Clone, Eq, PartialEq, Debug, Error)] +#[derive(Debug, Error)] pub enum Nl80211Error { #[error("Received an unexpected message {0:?}")] UnexpectedMessage(NetlinkMessage>), @@ -18,6 +19,9 @@ pub enum Nl80211Error { #[error("A netlink request failed")] RequestFailed(String), + #[error("Failed to decode netlink package: {0}")] + DecodeFailed(DecodeError), + #[error("A bug in this crate")] Bug(String), } diff --git a/src/ext_cap.rs b/src/ext_cap.rs new file mode 100644 index 0000000..424a15c --- /dev/null +++ b/src/ext_cap.rs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{Nla, NlaBuffer, NlasIterator}, + DecodeError, Emitable, Parseable, +}; + +use crate::Nl80211Attr; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211ExtendedCapability(pub Vec); + +//TODO: 802.11-2020 section `9.4.2.26 Extended Capabilities element` has +// definition on every bit, we can expose getter and setter function +// when required. +impl Nl80211ExtendedCapability { + pub fn new(payload: &[u8]) -> Self { + Self(payload.to_vec()) + } +} + +impl Emitable for Nl80211ExtendedCapability { + fn buffer_len(&self) -> usize { + self.0.len() + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < self.0.len() { + log::error!( + "Buffer size is smaller than desired size {}", + self.0.len() + ); + return; + } + buffer[..self.0.len()].copy_from_slice(self.0.as_slice()) + } +} + +impl std::ops::Deref for Nl80211ExtendedCapability { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211IfTypeExtCapas(pub Vec); + +impl std::ops::Deref for Nl80211IfTypeExtCapas { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfTypeExtCapas +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + let mut capas: Vec = Vec::new(); + let err_msg = + format!("Invalid NL80211_ATTR_IFTYPE_EXT_CAPA {payload:?}"); + for nla in NlasIterator::new(payload) { + let nla = nla.context(err_msg.clone())?; + capas.push(Nl80211IfTypeExtCapa::parse(&nla)?); + } + Ok(Self(capas)) + } +} + +impl From<&Vec> for Nl80211IfTypeExtCapas { + fn from(v: &Vec) -> Self { + Self(v.clone()) + } +} + +impl From for Vec { + fn from(v: Nl80211IfTypeExtCapas) -> Vec { + v.0 + } +} + +// For linux kernel, NL80211_ATTR_IFTYPE_EXT_CAPA is indexing from 0 but +// `capa_start`, hence we expose the index to user in case they want to generate +// identical data as linux kernel does. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211IfTypeExtCapa { + pub index: u16, + pub attributes: Vec, +} + +impl Nla for Nl80211IfTypeExtCapa { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer) + } + + fn kind(&self) -> u16 { + self.index + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfTypeExtCapa +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let index = buf.kind(); + let payload = buf.value(); + let mut attributes: Vec = Vec::new(); + let err_msg = + format!("Invalid NL80211_ATTR_IFTYPE_EXT_CAPA {payload:?}"); + for nla in NlasIterator::new(payload) { + let nla = nla.context(err_msg.clone())?; + attributes.push(Nl80211Attr::parse(&nla).context(err_msg.clone())?); + } + Ok(Self { index, attributes }) + } +} diff --git a/src/feature.rs b/src/feature.rs new file mode 100644 index 0000000..bb7880c --- /dev/null +++ b/src/feature.rs @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{DecodeError, Emitable}; + +const NL80211_FEATURE_SK_TX_STATUS: u32 = 1 << 0; +const NL80211_FEATURE_HT_IBSS: u32 = 1 << 1; +const NL80211_FEATURE_INACTIVITY_TIMER: u32 = 1 << 2; +const NL80211_FEATURE_CELL_BASE_REG_HINTS: u32 = 1 << 3; +const NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: u32 = 1 << 4; +const NL80211_FEATURE_SAE: u32 = 1 << 5; +const NL80211_FEATURE_LOW_PRIORITY_SCAN: u32 = 1 << 6; +const NL80211_FEATURE_SCAN_FLUSH: u32 = 1 << 7; +const NL80211_FEATURE_AP_SCAN: u32 = 1 << 8; +const NL80211_FEATURE_VIF_TXPOWER: u32 = 1 << 9; +const NL80211_FEATURE_NEED_OBSS_SCAN: u32 = 1 << 10; +const NL80211_FEATURE_P2P_GO_CTWIN: u32 = 1 << 11; +const NL80211_FEATURE_P2P_GO_OPPPS: u32 = 1 << 12; +// bit 13 is reserved +const NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: u32 = 1 << 14; +const NL80211_FEATURE_FULL_AP_CLIENT_STATE: u32 = 1 << 15; +const NL80211_FEATURE_USERSPACE_MPM: u32 = 1 << 16; +const NL80211_FEATURE_ACTIVE_MONITOR: u32 = 1 << 17; +const NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: u32 = 1 << 18; +const NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: u32 = 1 << 19; +const NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: u32 = 1 << 20; +const NL80211_FEATURE_QUIET: u32 = 1 << 21; +const NL80211_FEATURE_TX_POWER_INSERTION: u32 = 1 << 22; +const NL80211_FEATURE_ACKTO_ESTIMATION: u32 = 1 << 23; +const NL80211_FEATURE_STATIC_SMPS: u32 = 1 << 24; +const NL80211_FEATURE_DYNAMIC_SMPS: u32 = 1 << 25; +const NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: u32 = 1 << 26; +const NL80211_FEATURE_MAC_ON_CREATE: u32 = 1 << 27; +const NL80211_FEATURE_TDLS_CHANNEL_SWITCH: u32 = 1 << 28; +const NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: u32 = 1 << 29; +const NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: u32 = 1 << 30; +const NL80211_FEATURE_ND_RANDOM_MAC_ADDR: u32 = 1 << 31; + +bitflags::bitflags! { + #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] + #[non_exhaustive] + pub struct Nl80211Features: u32 { + const SkTxStatus = NL80211_FEATURE_SK_TX_STATUS; + const HtIbss = NL80211_FEATURE_HT_IBSS; + const InactivityTimer = NL80211_FEATURE_INACTIVITY_TIMER; + const CellBaseRegHints = NL80211_FEATURE_CELL_BASE_REG_HINTS; + const P2pDeviceNeedsChannel = NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL; + const Sae = NL80211_FEATURE_SAE; + const LowPriorityScan = NL80211_FEATURE_LOW_PRIORITY_SCAN; + const ScanFlush = NL80211_FEATURE_SCAN_FLUSH; + const ApScan = NL80211_FEATURE_AP_SCAN; + const VifTxpower = NL80211_FEATURE_VIF_TXPOWER; + const NeedObssScan = NL80211_FEATURE_NEED_OBSS_SCAN; + const P2pGoCtwin = NL80211_FEATURE_P2P_GO_CTWIN; + const P2pGoOppps = NL80211_FEATURE_P2P_GO_OPPPS; + const AdvertiseChanLimits = NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; + const FullApClientState = NL80211_FEATURE_FULL_AP_CLIENT_STATE; + const UserspaceMpm = NL80211_FEATURE_USERSPACE_MPM; + const ActiveMonitor = NL80211_FEATURE_ACTIVE_MONITOR; + const ApModeChanWidthChange = NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; + const DsParamSetIeInProbes = NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; + const WfaTpcIeInProbes = NL80211_FEATURE_WFA_TPC_IE_IN_PROBES; + const Quiet = NL80211_FEATURE_QUIET; + const TxPowerInsertion = NL80211_FEATURE_TX_POWER_INSERTION; + const AcktoEstimation = NL80211_FEATURE_ACKTO_ESTIMATION; + const StaticSmps = NL80211_FEATURE_STATIC_SMPS; + const DynamicSmps = NL80211_FEATURE_DYNAMIC_SMPS; + const SupportsWmmAdmission = NL80211_FEATURE_SUPPORTS_WMM_ADMISSION; + const MacOnCreate = NL80211_FEATURE_MAC_ON_CREATE; + const TdlsChannelSwitch = NL80211_FEATURE_TDLS_CHANNEL_SWITCH; + const ScanRandomMacAddr = NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + const SchedScanRandomMacAddr = + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; + const NdRandomMacAddr = NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + const _ = !0; + } +} + +// Kernel is using [u8; DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)] to +// store these extended features, allowing it to support any count of +// features more than u128. The maximum data type bitflags can use is u128, +// which might be not enough in the future, hence we do it by ourselves without +// using bitflags. +#[derive(Debug, Default, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub(crate) struct Nl80211ExtFeatures(pub(crate) Vec); + +impl Nl80211ExtFeatures { + // Kernel(6.10.8) is using 9 bytes to store these 68 bits features and + // will expand to more bytes + pub(crate) const LENGTH: usize = 9; + + pub(crate) fn parse(payload: &[u8]) -> Result { + let mut features = Vec::new(); + for (index, byte) in payload.iter().enumerate() { + for pos in 0..7 { + if (byte & (1 << pos)) >= 1 { + let feature = Nl80211ExtFeature::from(index * 8 + pos); + if feature != Nl80211ExtFeature::Unknown { + features.push(feature); + } + } + } + } + Ok(Self(features)) + } +} + +impl Emitable for Nl80211ExtFeatures { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + for feature in self.0.as_slice() { + let index = *feature as usize / 8; + let pos = *feature as usize % 8; + buffer[index] |= 1 << pos; + } + } +} + +impl From<&Vec> for Nl80211ExtFeatures { + fn from(v: &Vec) -> Self { + Self(v.clone()) + } +} + +// We cannot have Other() as it would make `repr(usize)` not supporting `as` +// casting so we just discard unknown features with a log +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +#[repr(usize)] +pub enum Nl80211ExtFeature { + VhtIbss = 0, + Rrm = 1, + MuMimoAirSniffer = 2, + ScanStartTime = 3, + BssParentTsf = 4, + SetScanDwell = 5, + BeaconRateLegacy = 6, + BeaconRateHt = 7, + BeaconRateVht = 8, + FilsSta = 9, + MgmtTxRandomTa = 10, + MgmtTxRandomTaConnected = 11, + SchedScanRelativeRssi = 12, + CqmRssiList = 13, + FilsSkOffload = 14, + FourWayHandshakeStaPsk = 15, + FourWayHandshakeSta1X = 16, + FilsMaxChannelTime = 17, + AcceptBcastProbeResp = 18, + OceProbeReqHighTxRate = 19, + OceProbeReqDeferralSuppression = 20, + MfpOptional = 21, + LowSpanScan = 22, + LowPowerScan = 23, + HighAccuracyScan = 24, + DfsOffload = 25, + ControlPortOverNl80211 = 26, + AckSignalSupport = 27, + Txqs = 28, + ScanRandomSn = 29, + ScanMinPreqContent = 30, + CanReplacePtk0 = 31, + EnableFtmResponder = 32, + AirtimeFairness = 33, + ApPmksaCaching = 34, + SchedScanBandSpecificRssiThold = 35, + ExtKeyId = 36, + StaTxPwr = 37, + SaeOffload = 38, + VlanOffload = 39, + Aql = 40, + BeaconProtection = 41, + ControlPortNoPreauth = 42, + ProtectedTwt = 43, + DelIbssSta = 44, + MulticastRegistrations = 45, + BeaconProtectionClient = 46, + ScanFreqKhz = 47, + ControlPortOverNl80211TxStatus = 48, + OperatingChannelValidation = 49, + FourWayHandshakeApPsk = 50, + SaeOffloadAp = 51, + FilsDiscovery = 52, + UnsolBcastProbeResp = 53, + BeaconRateHe = 54, + SecureLtf = 55, + SecureRtt = 56, + ProtRangeNegoAndMeasure = 57, + BssColor = 58, + FilsCryptoOffload = 59, + RadarBackground = 60, + PoweredAddrChange = 61, + Punct = 62, + SecureNan = 63, + AuthAndDeauthRandomTa = 64, + OweOffload = 65, + OweOffloadAp = 66, + DfsConcurrent = 67, + SppAmsduSupport = 68, + // Please check Nl80211ExtFeatures::LENGTH when you adding more features + #[default] + Unknown = 0xffff, +} + +impl From for Nl80211ExtFeature { + fn from(d: usize) -> Self { + match d { + d if d == Self::VhtIbss as usize => Self::VhtIbss, + d if d == Self::Rrm as usize => Self::Rrm, + d if d == Self::MuMimoAirSniffer as usize => Self::MuMimoAirSniffer, + d if d == Self::ScanStartTime as usize => Self::ScanStartTime, + d if d == Self::BssParentTsf as usize => Self::BssParentTsf, + d if d == Self::SetScanDwell as usize => Self::SetScanDwell, + d if d == Self::BeaconRateLegacy as usize => Self::BeaconRateLegacy, + d if d == Self::BeaconRateHt as usize => Self::BeaconRateHt, + d if d == Self::BeaconRateVht as usize => Self::BeaconRateVht, + d if d == Self::FilsSta as usize => Self::FilsSta, + d if d == Self::MgmtTxRandomTa as usize => Self::MgmtTxRandomTa, + d if d == Self::MgmtTxRandomTaConnected as usize => { + Self::MgmtTxRandomTaConnected + } + d if d == Self::SchedScanRelativeRssi as usize => { + Self::SchedScanRelativeRssi + } + d if d == Self::CqmRssiList as usize => Self::CqmRssiList, + d if d == Self::FilsSkOffload as usize => Self::FilsSkOffload, + d if d == Self::FourWayHandshakeStaPsk as usize => { + Self::FourWayHandshakeStaPsk + } + d if d == Self::FourWayHandshakeSta1X as usize => { + Self::FourWayHandshakeSta1X + } + d if d == Self::FilsMaxChannelTime as usize => { + Self::FilsMaxChannelTime + } + d if d == Self::AcceptBcastProbeResp as usize => { + Self::AcceptBcastProbeResp + } + d if d == Self::OceProbeReqHighTxRate as usize => { + Self::OceProbeReqHighTxRate + } + d if d == Self::OceProbeReqDeferralSuppression as usize => { + Self::OceProbeReqDeferralSuppression + } + d if d == Self::MfpOptional as usize => Self::MfpOptional, + d if d == Self::LowSpanScan as usize => Self::LowSpanScan, + d if d == Self::LowPowerScan as usize => Self::LowPowerScan, + d if d == Self::HighAccuracyScan as usize => Self::HighAccuracyScan, + d if d == Self::DfsOffload as usize => Self::DfsOffload, + d if d == Self::ControlPortOverNl80211 as usize => { + Self::ControlPortOverNl80211 + } + d if d == Self::AckSignalSupport as usize => Self::AckSignalSupport, + d if d == Self::Txqs as usize => Self::Txqs, + d if d == Self::ScanRandomSn as usize => Self::ScanRandomSn, + d if d == Self::ScanMinPreqContent as usize => { + Self::ScanMinPreqContent + } + d if d == Self::CanReplacePtk0 as usize => Self::CanReplacePtk0, + d if d == Self::EnableFtmResponder as usize => { + Self::EnableFtmResponder + } + d if d == Self::AirtimeFairness as usize => Self::AirtimeFairness, + d if d == Self::ApPmksaCaching as usize => Self::ApPmksaCaching, + d if d == Self::SchedScanBandSpecificRssiThold as usize => { + Self::SchedScanBandSpecificRssiThold + } + d if d == Self::ExtKeyId as usize => Self::ExtKeyId, + d if d == Self::StaTxPwr as usize => Self::StaTxPwr, + d if d == Self::SaeOffload as usize => Self::SaeOffload, + d if d == Self::VlanOffload as usize => Self::VlanOffload, + d if d == Self::Aql as usize => Self::Aql, + d if d == Self::BeaconProtection as usize => Self::BeaconProtection, + d if d == Self::ControlPortNoPreauth as usize => { + Self::ControlPortNoPreauth + } + d if d == Self::ProtectedTwt as usize => Self::ProtectedTwt, + d if d == Self::DelIbssSta as usize => Self::DelIbssSta, + d if d == Self::MulticastRegistrations as usize => { + Self::MulticastRegistrations + } + d if d == Self::BeaconProtectionClient as usize => { + Self::BeaconProtectionClient + } + d if d == Self::ScanFreqKhz as usize => Self::ScanFreqKhz, + d if d == Self::ControlPortOverNl80211TxStatus as usize => { + Self::ControlPortOverNl80211TxStatus + } + d if d == Self::OperatingChannelValidation as usize => { + Self::OperatingChannelValidation + } + d if d == Self::FourWayHandshakeApPsk as usize => { + Self::FourWayHandshakeApPsk + } + d if d == Self::SaeOffloadAp as usize => Self::SaeOffloadAp, + d if d == Self::FilsDiscovery as usize => Self::FilsDiscovery, + d if d == Self::UnsolBcastProbeResp as usize => { + Self::UnsolBcastProbeResp + } + d if d == Self::BeaconRateHe as usize => Self::BeaconRateHe, + d if d == Self::SecureLtf as usize => Self::SecureLtf, + d if d == Self::SecureRtt as usize => Self::SecureRtt, + d if d == Self::ProtRangeNegoAndMeasure as usize => { + Self::ProtRangeNegoAndMeasure + } + d if d == Self::BssColor as usize => Self::BssColor, + d if d == Self::FilsCryptoOffload as usize => { + Self::FilsCryptoOffload + } + d if d == Self::RadarBackground as usize => Self::RadarBackground, + d if d == Self::PoweredAddrChange as usize => { + Self::PoweredAddrChange + } + d if d == Self::Punct as usize => Self::Punct, + d if d == Self::SecureNan as usize => Self::SecureNan, + d if d == Self::AuthAndDeauthRandomTa as usize => { + Self::AuthAndDeauthRandomTa + } + d if d == Self::OweOffload as usize => Self::OweOffload, + d if d == Self::OweOffloadAp as usize => Self::OweOffloadAp, + d if d == Self::DfsConcurrent as usize => Self::DfsConcurrent, + d if d == Self::SppAmsduSupport as usize => Self::SppAmsduSupport, + _ => { + log::info!("Unsupported feature {d}"); + Self::Unknown + } + } + } +} diff --git a/src/frame_type.rs b/src/frame_type.rs new file mode 100644 index 0000000..ccf5bd0 --- /dev/null +++ b/src/frame_type.rs @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{Nla, NlaBuffer, NlasIterator}, + parsers::parse_u16, + DecodeError, Emitable, Parseable, +}; + +use crate::{bytes::write_u16, Nl80211InterfaceType}; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub struct Nl80211IfaceFrameType { + pub iface_type: Nl80211InterfaceType, + pub attributes: Vec, +} + +impl Nla for Nl80211IfaceFrameType { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + u32::from(self.iface_type) as u16 + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer) + } +} + +const NL80211_ATTR_FRAME_TYPE: u16 = 101; + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfaceFrameType +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + let iface_type = Nl80211InterfaceType::from(buf.kind() as u32); + let err_msg = format!("Invalid NL80211_IFACE_COMB_LIMITS {payload:?}"); + let mut attributes = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + // We are discarding other kind of NLA, but linux kernel + // most likely will not add new NLA type for + // NL80211_ATTR_TX_FRAME_TYPES. + if nla.kind() == NL80211_ATTR_FRAME_TYPE { + attributes.push(Nl80211FrameType::from( + parse_u16(nla.value()).context(format!( + "Invalid NL80211_ATTR_FRAME_TYPE {:?}", + nla.value() + ))?, + )); + } + } + Ok(Self { + iface_type, + attributes, + }) + } +} + +const IEEE80211_FTYPE_MGMT: u8 = 0x00; +const IEEE80211_FTYPE_CTL: u8 = 0x04; +const IEEE80211_FTYPE_DATA: u8 = 0x08; +const IEEE80211_FTYPE_EXT: u8 = 0x0c; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211FrameType { + Management(Nl80211FrameTypeMgmt), + Control(Nl80211FrameTypeCtl), + Data(Nl80211FrameTypeData), + Extension(Nl80211FrameTypeExt), + Other(u16), +} + +impl Nla for Nl80211FrameType { + fn value_len(&self) -> usize { + 2 + } + + fn kind(&self) -> u16 { + NL80211_ATTR_FRAME_TYPE + } + + fn emit_value(&self, buffer: &mut [u8]) { + write_u16(buffer, u16::from(*self)) + } +} + +impl From for Nl80211FrameType { + fn from(d: u16) -> Self { + let frame_type = (d & 0xf) as u8; + let sub_type = d - frame_type as u16; + match frame_type { + IEEE80211_FTYPE_MGMT => { + Self::Management(Nl80211FrameTypeMgmt::from(sub_type)) + } + IEEE80211_FTYPE_CTL => { + Self::Control(Nl80211FrameTypeCtl::from(sub_type)) + } + IEEE80211_FTYPE_DATA => { + Self::Data(Nl80211FrameTypeData::from(sub_type)) + } + IEEE80211_FTYPE_EXT => { + Self::Extension(Nl80211FrameTypeExt::from(sub_type)) + } + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211FrameType) -> u16 { + match v { + Nl80211FrameType::Management(s) => { + IEEE80211_FTYPE_MGMT as u16 | u16::from(s) + } + Nl80211FrameType::Control(s) => { + IEEE80211_FTYPE_CTL as u16 | u16::from(s) + } + Nl80211FrameType::Data(s) => { + IEEE80211_FTYPE_DATA as u16 | u16::from(s) + } + Nl80211FrameType::Extension(s) => { + IEEE80211_FTYPE_EXT as u16 | u16::from(s) + } + Nl80211FrameType::Other(d) => d, + } + } +} + +const IEEE80211_STYPE_ASSOC_REQ: u16 = 0x0000; +const IEEE80211_STYPE_ASSOC_RESP: u16 = 0x0010; +const IEEE80211_STYPE_REASSOC_REQ: u16 = 0x0020; +const IEEE80211_STYPE_REASSOC_RESP: u16 = 0x0030; +const IEEE80211_STYPE_PROBE_REQ: u16 = 0x0040; +const IEEE80211_STYPE_PROBE_RESP: u16 = 0x0050; +const IEEE80211_STYPE_BEACON: u16 = 0x0080; +const IEEE80211_STYPE_ATIM: u16 = 0x0090; +const IEEE80211_STYPE_DISASSOC: u16 = 0x00A0; +const IEEE80211_STYPE_AUTH: u16 = 0x00B0; +const IEEE80211_STYPE_DEAUTH: u16 = 0x00C0; +const IEEE80211_STYPE_ACTION: u16 = 0x00D0; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211FrameTypeMgmt { + AssocReq, + AssocResp, + ReassocReq, + ReassocResp, + ProbeReq, + ProbeResp, + Beacon, + Atim, + Disassoc, + Auth, + Deauth, + Action, + Other(u16), +} + +impl From for Nl80211FrameTypeMgmt { + fn from(d: u16) -> Self { + match d { + IEEE80211_STYPE_ASSOC_REQ => Self::AssocReq, + IEEE80211_STYPE_ASSOC_RESP => Self::AssocResp, + IEEE80211_STYPE_REASSOC_REQ => Self::ReassocReq, + IEEE80211_STYPE_REASSOC_RESP => Self::ReassocResp, + IEEE80211_STYPE_PROBE_REQ => Self::ProbeReq, + IEEE80211_STYPE_PROBE_RESP => Self::ProbeResp, + IEEE80211_STYPE_BEACON => Self::Beacon, + IEEE80211_STYPE_ATIM => Self::Atim, + IEEE80211_STYPE_DISASSOC => Self::Disassoc, + IEEE80211_STYPE_AUTH => Self::Auth, + IEEE80211_STYPE_DEAUTH => Self::Deauth, + IEEE80211_STYPE_ACTION => Self::Action, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211FrameTypeMgmt) -> u16 { + match v { + Nl80211FrameTypeMgmt::AssocReq => IEEE80211_STYPE_ASSOC_REQ, + Nl80211FrameTypeMgmt::AssocResp => IEEE80211_STYPE_ASSOC_RESP, + Nl80211FrameTypeMgmt::ReassocReq => IEEE80211_STYPE_REASSOC_REQ, + Nl80211FrameTypeMgmt::ReassocResp => IEEE80211_STYPE_REASSOC_RESP, + Nl80211FrameTypeMgmt::ProbeReq => IEEE80211_STYPE_PROBE_REQ, + Nl80211FrameTypeMgmt::ProbeResp => IEEE80211_STYPE_PROBE_RESP, + Nl80211FrameTypeMgmt::Beacon => IEEE80211_STYPE_BEACON, + Nl80211FrameTypeMgmt::Atim => IEEE80211_STYPE_ATIM, + Nl80211FrameTypeMgmt::Disassoc => IEEE80211_STYPE_DISASSOC, + Nl80211FrameTypeMgmt::Auth => IEEE80211_STYPE_AUTH, + Nl80211FrameTypeMgmt::Deauth => IEEE80211_STYPE_DEAUTH, + Nl80211FrameTypeMgmt::Action => IEEE80211_STYPE_ACTION, + Nl80211FrameTypeMgmt::Other(d) => d, + } + } +} + +const IEEE80211_STYPE_TRIGGER: u16 = 0x0020; +const IEEE80211_STYPE_CTL_EXT: u16 = 0x0060; +const IEEE80211_STYPE_BACK_REQ: u16 = 0x0080; +const IEEE80211_STYPE_BACK: u16 = 0x0090; +const IEEE80211_STYPE_PSPOLL: u16 = 0x00A0; +const IEEE80211_STYPE_RTS: u16 = 0x00B0; +const IEEE80211_STYPE_CTS: u16 = 0x00C0; +const IEEE80211_STYPE_ACK: u16 = 0x00D0; +const IEEE80211_STYPE_CFEND: u16 = 0x00E0; +const IEEE80211_STYPE_CFENDACK: u16 = 0x00F0; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211FrameTypeCtl { + Trigger, + CtlExt, + BackReq, + Back, + Pspoll, + Rts, + Cts, + Ack, + Cfend, + Cfendack, + Other(u16), +} + +impl From for Nl80211FrameTypeCtl { + fn from(d: u16) -> Self { + match d { + IEEE80211_STYPE_TRIGGER => Self::Trigger, + IEEE80211_STYPE_CTL_EXT => Self::CtlExt, + IEEE80211_STYPE_BACK_REQ => Self::BackReq, + IEEE80211_STYPE_BACK => Self::Back, + IEEE80211_STYPE_PSPOLL => Self::Pspoll, + IEEE80211_STYPE_RTS => Self::Rts, + IEEE80211_STYPE_CTS => Self::Cts, + IEEE80211_STYPE_ACK => Self::Ack, + IEEE80211_STYPE_CFEND => Self::Cfend, + IEEE80211_STYPE_CFENDACK => Self::Cfendack, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211FrameTypeCtl) -> u16 { + match v { + Nl80211FrameTypeCtl::Trigger => IEEE80211_STYPE_TRIGGER, + Nl80211FrameTypeCtl::CtlExt => IEEE80211_STYPE_CTL_EXT, + Nl80211FrameTypeCtl::BackReq => IEEE80211_STYPE_BACK_REQ, + Nl80211FrameTypeCtl::Back => IEEE80211_STYPE_BACK, + Nl80211FrameTypeCtl::Pspoll => IEEE80211_STYPE_PSPOLL, + Nl80211FrameTypeCtl::Rts => IEEE80211_STYPE_RTS, + Nl80211FrameTypeCtl::Cts => IEEE80211_STYPE_CTS, + Nl80211FrameTypeCtl::Ack => IEEE80211_STYPE_ACK, + Nl80211FrameTypeCtl::Cfend => IEEE80211_STYPE_CFEND, + Nl80211FrameTypeCtl::Cfendack => IEEE80211_STYPE_CFENDACK, + Nl80211FrameTypeCtl::Other(d) => d, + } + } +} + +const IEEE80211_STYPE_DATA: u16 = 0x0000; +const IEEE80211_STYPE_DATA_CFACK: u16 = 0x0010; +const IEEE80211_STYPE_DATA_CFPOLL: u16 = 0x0020; +const IEEE80211_STYPE_DATA_CFACKPOLL: u16 = 0x0030; +const IEEE80211_STYPE_NULLFUNC: u16 = 0x0040; +const IEEE80211_STYPE_CFACK: u16 = 0x0050; +const IEEE80211_STYPE_CFPOLL: u16 = 0x0060; +const IEEE80211_STYPE_CFACKPOLL: u16 = 0x0070; +const IEEE80211_STYPE_QOS_DATA: u16 = 0x0080; +const IEEE80211_STYPE_QOS_DATA_CFACK: u16 = 0x0090; +const IEEE80211_STYPE_QOS_DATA_CFPOLL: u16 = 0x00A0; +const IEEE80211_STYPE_QOS_DATA_CFACKPOLL: u16 = 0x00B0; +const IEEE80211_STYPE_QOS_NULLFUNC: u16 = 0x00C0; +const IEEE80211_STYPE_QOS_CFACK: u16 = 0x00D0; +const IEEE80211_STYPE_QOS_CFPOLL: u16 = 0x00E0; +const IEEE80211_STYPE_QOS_CFACKPOLL: u16 = 0x00F0; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211FrameTypeData { + Data, + DataCfack, + DataCfpoll, + DataCfackpoll, + Nullfunc, + Cfack, + Cfpoll, + Cfackpoll, + QosData, + QosDataCfack, + QosDataCfpoll, + QosDataCfackpoll, + QosNullfunc, + QosCfack, + QosCfpoll, + QosCfackpoll, + Other(u16), +} + +impl From for Nl80211FrameTypeData { + fn from(d: u16) -> Self { + match d { + IEEE80211_STYPE_DATA => Self::Data, + IEEE80211_STYPE_DATA_CFACK => Self::DataCfack, + IEEE80211_STYPE_DATA_CFPOLL => Self::DataCfpoll, + IEEE80211_STYPE_DATA_CFACKPOLL => Self::DataCfackpoll, + IEEE80211_STYPE_NULLFUNC => Self::Nullfunc, + IEEE80211_STYPE_CFACK => Self::Cfack, + IEEE80211_STYPE_CFPOLL => Self::Cfpoll, + IEEE80211_STYPE_CFACKPOLL => Self::Cfackpoll, + IEEE80211_STYPE_QOS_DATA => Self::QosData, + IEEE80211_STYPE_QOS_DATA_CFACK => Self::QosDataCfack, + IEEE80211_STYPE_QOS_DATA_CFPOLL => Self::QosDataCfpoll, + IEEE80211_STYPE_QOS_DATA_CFACKPOLL => Self::QosDataCfackpoll, + IEEE80211_STYPE_QOS_NULLFUNC => Self::QosNullfunc, + IEEE80211_STYPE_QOS_CFACK => Self::QosCfack, + IEEE80211_STYPE_QOS_CFPOLL => Self::QosCfpoll, + IEEE80211_STYPE_QOS_CFACKPOLL => Self::QosCfackpoll, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211FrameTypeData) -> u16 { + match v { + Nl80211FrameTypeData::Data => IEEE80211_STYPE_DATA, + Nl80211FrameTypeData::DataCfack => IEEE80211_STYPE_DATA_CFACK, + Nl80211FrameTypeData::DataCfpoll => IEEE80211_STYPE_DATA_CFPOLL, + Nl80211FrameTypeData::DataCfackpoll => { + IEEE80211_STYPE_DATA_CFACKPOLL + } + Nl80211FrameTypeData::Nullfunc => IEEE80211_STYPE_NULLFUNC, + Nl80211FrameTypeData::Cfack => IEEE80211_STYPE_CFACK, + Nl80211FrameTypeData::Cfpoll => IEEE80211_STYPE_CFPOLL, + Nl80211FrameTypeData::Cfackpoll => IEEE80211_STYPE_CFACKPOLL, + Nl80211FrameTypeData::QosData => IEEE80211_STYPE_QOS_DATA, + Nl80211FrameTypeData::QosDataCfack => { + IEEE80211_STYPE_QOS_DATA_CFACK + } + Nl80211FrameTypeData::QosDataCfpoll => { + IEEE80211_STYPE_QOS_DATA_CFPOLL + } + Nl80211FrameTypeData::QosDataCfackpoll => { + IEEE80211_STYPE_QOS_DATA_CFACKPOLL + } + Nl80211FrameTypeData::QosNullfunc => IEEE80211_STYPE_QOS_NULLFUNC, + Nl80211FrameTypeData::QosCfack => IEEE80211_STYPE_QOS_CFACK, + Nl80211FrameTypeData::QosCfpoll => IEEE80211_STYPE_QOS_CFPOLL, + Nl80211FrameTypeData::QosCfackpoll => IEEE80211_STYPE_QOS_CFACKPOLL, + Nl80211FrameTypeData::Other(d) => d, + } + } +} + +const IEEE80211_STYPE_DMG_BEACON: u16 = 0x0000; +const IEEE80211_STYPE_S1G_BEACON: u16 = 0x0010; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211FrameTypeExt { + DmgBeacon, + S1gBeacon, + Other(u16), +} + +impl From for Nl80211FrameTypeExt { + fn from(d: u16) -> Self { + match d { + IEEE80211_STYPE_DMG_BEACON => Self::DmgBeacon, + IEEE80211_STYPE_S1G_BEACON => Self::S1gBeacon, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211FrameTypeExt) -> u16 { + match v { + Nl80211FrameTypeExt::DmgBeacon => IEEE80211_STYPE_DMG_BEACON, + Nl80211FrameTypeExt::S1gBeacon => IEEE80211_STYPE_S1G_BEACON, + Nl80211FrameTypeExt::Other(d) => d, + } + } +} diff --git a/src/handle.rs b/src/handle.rs index e143894..07344a0 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -8,7 +8,7 @@ use netlink_packet_utils::DecodeError; use crate::{ try_nl80211, Nl80211Error, Nl80211InterfaceHandle, Nl80211Message, - Nl80211StationHandle, + Nl80211StationHandle, Nl80211WiphyHandle, }; #[derive(Clone, Debug)] @@ -31,6 +31,11 @@ impl Nl80211Handle { Nl80211StationHandle::new(self.clone()) } + // equivalent to `iw phy` command + pub fn wireless_physic(&self) -> Nl80211WiphyHandle { + Nl80211WiphyHandle::new(self.clone()) + } + pub async fn request( &mut self, message: NetlinkMessage>, diff --git a/src/iface/combination.rs b/src/iface/combination.rs new file mode 100644 index 0000000..2de0c3e --- /dev/null +++ b/src/iface/combination.rs @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::parse_u32, + DecodeError, Emitable, Parseable, ParseableParametrized, +}; + +use crate::{bytes::write_u32, Nl80211InterfaceType, Nl80211InterfaceTypes}; + +const NL80211_IFACE_COMB_LIMITS: u16 = 1; +const NL80211_IFACE_COMB_MAXNUM: u16 = 2; +const NL80211_IFACE_COMB_STA_AP_BI_MATCH: u16 = 3; +const NL80211_IFACE_COMB_NUM_CHANNELS: u16 = 4; +const NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u16 = 5; +const NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u16 = 6; +const NL80211_IFACE_COMB_BI_MIN_GCD: u16 = 7; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub struct Nl80211IfaceComb { + pub index: u16, + pub attributes: Vec, +} + +impl Nla for Nl80211IfaceComb { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.index + 1 + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer) + } +} + +impl<'a, T> ParseableParametrized, u16> for Nl80211IfaceComb +where + T: AsRef<[u8]> + ?Sized, +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + index: u16, + ) -> Result { + let payload = buf.value(); + let err_msg = format!( + "Invalid NL80211_IFACE_COMB_LIMITS {payload:?} index {index}" + ); + let mut attributes = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + attributes.push(Nl80211IfaceCombAttribute::parse(nla)?); + } + Ok(Self { index, attributes }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum Nl80211IfaceCombAttribute { + Limits(Vec), + Maxnum(u32), + StaApiBiMatch, + NumChannels(u32), + RadarDetectWidths(u32), + RadarDetectRegins(u32), + BiMinGcd(u32), + Other(DefaultNla), +} + +impl Nla for Nl80211IfaceCombAttribute { + fn value_len(&self) -> usize { + match self { + Self::Limits(v) => v.as_slice().buffer_len(), + Self::StaApiBiMatch => 0, + Self::Maxnum(_) + | Self::NumChannels(_) + | Self::RadarDetectWidths(_) + | Self::RadarDetectRegins(_) + | Self::BiMinGcd(_) => 4, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Limits(_) => NL80211_IFACE_COMB_LIMITS, + Self::Maxnum(_) => NL80211_IFACE_COMB_MAXNUM, + Self::StaApiBiMatch => NL80211_IFACE_COMB_STA_AP_BI_MATCH, + Self::NumChannels(_) => NL80211_IFACE_COMB_NUM_CHANNELS, + Self::RadarDetectWidths(_) => { + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS + } + Self::RadarDetectRegins(_) => { + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS + } + Self::BiMinGcd(_) => NL80211_IFACE_COMB_BI_MIN_GCD, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Maxnum(d) + | Self::NumChannels(d) + | Self::RadarDetectWidths(d) + | Self::RadarDetectRegins(d) + | Self::BiMinGcd(d) => write_u32(buffer, *d), + Self::StaApiBiMatch => (), + Self::Limits(v) => v.as_slice().emit(buffer), + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfaceCombAttribute +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_IFACE_COMB_LIMITS => { + let err_msg = + format!("Invalid NL80211_IFACE_COMB_LIMITS {payload:?}"); + let mut nlas = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211IfaceCombLimit::parse_with_param( + nla, + index as u16, + )?); + } + Self::Limits(nlas) + } + NL80211_IFACE_COMB_MAXNUM => { + Self::Maxnum(parse_u32(payload).context(format!( + "Invalid NL80211_IFACE_COMB_MAXNUM {payload:?}" + ))?) + } + NL80211_IFACE_COMB_STA_AP_BI_MATCH => Self::StaApiBiMatch, + NL80211_IFACE_COMB_NUM_CHANNELS => { + Self::NumChannels(parse_u32(payload).context(format!( + "Invalid NL80211_IFACE_COMB_NUM_CHANNELS {payload:?}" + ))?) + } + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS => { + Self::RadarDetectWidths(parse_u32(payload).context(format!( + "Invalid NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS \ + {payload:?}" + ))?) + } + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS => { + Self::RadarDetectRegins(parse_u32(payload).context(format!( + "Invalid NL80211_IFACE_COMB_RADAR_DETECT_REGIONS \ + {payload:?}" + ))?) + } + NL80211_IFACE_COMB_BI_MIN_GCD => { + Self::BiMinGcd(parse_u32(payload).context(format!( + "Invalid NL80211_IFACE_COMB_BI_MIN_GCD {payload:?}" + ))?) + } + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub struct Nl80211IfaceCombLimit { + pub index: u16, + pub attributes: Vec, +} + +impl Nla for Nl80211IfaceCombLimit { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.index + 1 + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer) + } +} + +impl<'a, T> ParseableParametrized, u16> + for Nl80211IfaceCombLimit +where + T: AsRef<[u8]> + ?Sized, +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + index: u16, + ) -> Result { + let payload = buf.value(); + let err_msg = + format!("Invalid NL80211_IFACE_COMB_LIMITS {:?}", payload); + let mut attributes = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + attributes.push(Nl80211IfaceCombLimitAttribute::parse(nla)?); + } + Ok(Self { index, attributes }) + } +} + +const NL80211_IFACE_LIMIT_MAX: u16 = 1; +const NL80211_IFACE_LIMIT_TYPES: u16 = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum Nl80211IfaceCombLimitAttribute { + Max(u32), + Iftypes(Vec), + Other(DefaultNla), +} + +impl Nla for Nl80211IfaceCombLimitAttribute { + fn value_len(&self) -> usize { + match self { + Self::Max(_) => 4, + Self::Iftypes(v) => { + Nl80211InterfaceTypes::from(v).as_slice().buffer_len() + } + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Max(_) => NL80211_IFACE_LIMIT_MAX, + Self::Iftypes(_) => NL80211_IFACE_LIMIT_TYPES, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Max(d) => write_u32(buffer, *d), + Self::Iftypes(v) => { + Nl80211InterfaceTypes::from(v).as_slice().emit(buffer) + } + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfaceCombLimitAttribute +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_IFACE_LIMIT_MAX => Self::Max(parse_u32(payload).context( + format!("Invalid NL80211_IFACE_LIMIT_MAX {payload:?}"), + )?), + NL80211_IFACE_LIMIT_TYPES => Self::Iftypes( + Nl80211InterfaceTypes::parse( + payload, + "NL80211_IFACE_LIMIT_TYPES", + )? + .0, + ), + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} diff --git a/src/iface/iface_type.rs b/src/iface/iface_type.rs index 43ad0b8..8d9ef9e 100644 --- a/src/iface/iface_type.rs +++ b/src/iface/iface_type.rs @@ -1,5 +1,49 @@ // SPDX-License-Identifier: MIT +use anyhow::Context; + +use netlink_packet_utils::{ + nla::{Nla, NlasIterator}, + parsers::parse_u32, + DecodeError, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub(crate) struct Nl80211InterfaceTypes(pub(crate) Vec); + +impl std::ops::Deref for Nl80211InterfaceTypes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&Vec> for Nl80211InterfaceTypes { + fn from(iface_types: &Vec) -> Self { + Self(iface_types.to_vec()) + } +} + +impl From for Vec { + fn from(iface_types: Nl80211InterfaceTypes) -> Self { + iface_types.0 + } +} + +impl Nl80211InterfaceTypes { + pub fn parse(payload: &[u8], kind: &str) -> Result { + let mut iface_types: Vec = Vec::new(); + for nla in NlasIterator::new(payload) { + let error_msg = format!("Invalid {kind}: {nla:?}"); + let nla = &nla.context(error_msg.clone())?; + iface_types.push(Nl80211InterfaceType::from(nla.kind() as u32)); + } + Ok(Self(iface_types)) + } +} + +const NL80211_IFTYPE_UNSPECIFIED: u32 = 0; const NL80211_IFTYPE_ADHOC: u32 = 1; const NL80211_IFTYPE_STATION: u32 = 2; const NL80211_IFTYPE_AP: u32 = 3; @@ -13,11 +57,15 @@ const NL80211_IFTYPE_P2P_DEVICE: u32 = 10; const NL80211_IFTYPE_OCB: u32 = 11; const NL80211_IFTYPE_NAN: u32 = 12; +// Linux kernel data type `enum nl80211_iftype` +/// (virtual) interface types #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Nl80211InterfaceType { - /// Independent BSS member + /// unspecified type, driver decides + Unspecified, + /// Independent BSS member, also known as IBSS Adhoc, - /// Managed BSS member + /// Managed BSS member, also known as managed Station, /// Access point Ap, @@ -44,9 +92,32 @@ pub enum Nl80211InterfaceType { Other(u32), } +impl Nl80211InterfaceType { + pub const LENGTH: usize = 4; + + pub fn parse(payload: &[u8]) -> Result { + Ok(parse_u32(payload) + .context(format!("Invalid Nl80211InterfaceType data {payload:?}"))? + .into()) + } +} + +impl Nla for Nl80211InterfaceType { + fn value_len(&self) -> usize { + Self::LENGTH + } + + fn emit_value(&self, _buffer: &mut [u8]) {} + + fn kind(&self) -> u16 { + u32::from(*self) as u16 + } +} + impl From for Nl80211InterfaceType { fn from(d: u32) -> Self { match d { + NL80211_IFTYPE_UNSPECIFIED => Self::Unspecified, NL80211_IFTYPE_ADHOC => Self::Adhoc, NL80211_IFTYPE_STATION => Self::Station, NL80211_IFTYPE_AP => Self::Ap, @@ -67,6 +138,7 @@ impl From for Nl80211InterfaceType { impl From for u32 { fn from(v: Nl80211InterfaceType) -> u32 { match v { + Nl80211InterfaceType::Unspecified => NL80211_IFTYPE_UNSPECIFIED, Nl80211InterfaceType::Adhoc => NL80211_IFTYPE_ADHOC, Nl80211InterfaceType::Station => NL80211_IFTYPE_STATION, Nl80211InterfaceType::Ap => NL80211_IFTYPE_AP, diff --git a/src/iface/mod.rs b/src/iface/mod.rs index ca4f0a8..887b129 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -1,9 +1,16 @@ // SPDX-License-Identifier: MIT +mod combination; mod get; mod handle; mod iface_type; -pub use get::Nl80211InterfaceGetRequest; -pub use handle::Nl80211InterfaceHandle; -pub use iface_type::Nl80211InterfaceType; +pub use self::combination::{ + Nl80211IfaceComb, Nl80211IfaceCombAttribute, Nl80211IfaceCombLimit, + Nl80211IfaceCombLimitAttribute, +}; +pub use self::get::Nl80211InterfaceGetRequest; +pub use self::handle::Nl80211InterfaceHandle; +pub use self::iface_type::Nl80211InterfaceType; + +pub(crate) use self::iface_type::Nl80211InterfaceTypes; diff --git a/src/lib.rs b/src/lib.rs index 8fb2c32..5ff6f86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,29 +4,73 @@ mod attr; mod channel; mod connection; mod error; +mod ext_cap; +mod feature; +mod frame_type; mod handle; mod iface; mod macros; mod message; +mod mlo; mod station; mod stats; +mod wifi4; +mod wifi5; +mod wifi6; +mod wifi7; +mod wiphy; -pub use attr::Nl80211Attr; -pub use channel::{Nl80211ChannelWidth, Nl80211WiPhyChannelType}; +pub(crate) mod bytes; + +pub use self::attr::Nl80211Attr; +pub use self::channel::Nl80211ChannelWidth; #[cfg(feature = "tokio_socket")] -pub use connection::new_connection; -pub use connection::new_connection_with_socket; -pub use error::Nl80211Error; -pub use handle::Nl80211Handle; -pub use iface::{ - Nl80211InterfaceGetRequest, Nl80211InterfaceHandle, Nl80211InterfaceType, -}; -pub use message::{Nl80211Cmd, Nl80211Message}; -pub use station::{ +pub use self::connection::new_connection; +pub use self::connection::new_connection_with_socket; +pub use self::error::Nl80211Error; +pub use self::ext_cap::{ + Nl80211ExtendedCapability, Nl80211IfTypeExtCapa, Nl80211IfTypeExtCapas, +}; +pub use self::feature::{Nl80211ExtFeature, Nl80211Features}; +pub use self::frame_type::{Nl80211FrameType, Nl80211IfaceFrameType}; +pub use self::handle::Nl80211Handle; +pub use self::iface::{ + Nl80211IfaceComb, Nl80211IfaceCombAttribute, Nl80211IfaceCombLimit, + Nl80211IfaceCombLimitAttribute, Nl80211InterfaceGetRequest, + Nl80211InterfaceHandle, Nl80211InterfaceType, +}; +pub use self::message::{Nl80211Cmd, Nl80211Message}; +pub use self::mlo::Nl80211MloLink; +pub use self::station::{ Nl80211StationGetRequest, Nl80211StationHandle, Nl80211StationInfo, }; -pub use stats::{ +pub use self::stats::{ NestedNl80211TidStats, Nl80211TidStats, Nl80211TransmitQueueStat, }; +pub use self::wifi4::{ + Nl80211HtCapabilityMask, Nl80211HtCaps, Nl80211HtMcsInfo, + Nl80211HtWiphyChannelType, +}; +pub use self::wifi5::{ + Nl80211VhtCapInfo, Nl80211VhtCapability, Nl80211VhtMcsInfo, +}; +pub use self::wifi6::{ + Nl80211He6GhzCapa, Nl80211HeMacCapInfo, Nl80211HeMcsNssSupp, + Nl80211HePhyCapInfo, Nl80211HePpeThreshold, +}; +pub use self::wifi7::{ + Nl80211EhtMacCapInfo, Nl80211EhtMcsNssSupp, + Nl80211EhtMcsNssSuppMoreThan20Mhz, Nl80211EhtMcsNssSuppOnly20Mhz, + Nl80211EhtPhyCapInfo, Nl80211EhtPpeThres, +}; +pub use self::wiphy::{ + Nl80211Band, Nl80211BandInfo, Nl80211BandType, Nl80211BandTypes, + Nl80211CipherSuit, Nl80211Command, Nl80211Frequency, Nl80211FrequencyInfo, + Nl80211IfMode, Nl80211WiphyGetRequest, Nl80211WiphyHandle, + Nl80211WowlanTcpTrigerSupport, Nl80211WowlanTrigerPatternSupport, + Nl80211WowlanTrigersSupport, +}; -pub(crate) use handle::nl80211_execute; +pub(crate) use self::feature::Nl80211ExtFeatures; +pub(crate) use self::handle::nl80211_execute; +pub(crate) use self::iface::Nl80211InterfaceTypes; diff --git a/src/message.rs b/src/message.rs index 1af980a..1634941 100644 --- a/src/message.rs +++ b/src/message.rs @@ -8,6 +8,8 @@ use netlink_packet_utils::{ use crate::attr::Nl80211Attr; +const NL80211_CMD_GET_WIPHY: u8 = 1; +const NL80211_CMD_NEW_WIPHY: u8 = 3; const NL80211_CMD_GET_INTERFACE: u8 = 5; const NL80211_CMD_NEW_INTERFACE: u8 = 7; const NL80211_CMD_GET_STATION: u8 = 17; @@ -19,6 +21,8 @@ pub enum Nl80211Cmd { InterfaceNew, StationGet, StationNew, + WiphyGet, + WiphyNew, } impl From for u8 { @@ -28,6 +32,8 @@ impl From for u8 { Nl80211Cmd::InterfaceNew => NL80211_CMD_NEW_INTERFACE, Nl80211Cmd::StationGet => NL80211_CMD_GET_STATION, Nl80211Cmd::StationNew => NL80211_CMD_NEW_STATION, + Nl80211Cmd::WiphyGet => NL80211_CMD_GET_WIPHY, + Nl80211Cmd::WiphyNew => NL80211_CMD_NEW_WIPHY, } } } @@ -66,6 +72,13 @@ impl Nl80211Message { nlas, } } + + pub fn new_wiphy_get() -> Self { + Nl80211Message { + cmd: Nl80211Cmd::WiphyGet, + nlas: vec![Nl80211Attr::SplitWiphyDump], + } + } } impl Emitable for Nl80211Message { @@ -81,8 +94,7 @@ impl Emitable for Nl80211Message { fn parse_nlas(buffer: &[u8]) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { - let error_msg = - format!("Failed to parse nl80211 message attribute {:?}", nla); + let error_msg = "Failed to parse nl80211 message attribute".to_string(); let nla = &nla.context(error_msg.clone())?; nlas.push(Nl80211Attr::parse(nla).context(error_msg)?); } @@ -107,6 +119,10 @@ impl ParseableParametrized<[u8], GenlHeader> for Nl80211Message { cmd: Nl80211Cmd::StationNew, nlas: parse_nlas(buffer)?, }, + NL80211_CMD_NEW_WIPHY => Self { + cmd: Nl80211Cmd::WiphyNew, + nlas: parse_nlas(buffer)?, + }, cmd => { return Err(DecodeError::from(format!( "Unsupported nl80211 reply command: {}", diff --git a/src/mlo.rs b/src/mlo.rs new file mode 100644 index 0000000..f32c0b9 --- /dev/null +++ b/src/mlo.rs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::parse_u8, + DecodeError, Emitable, Parseable, +}; + +const ETH_ALEN: usize = 6; +const NL80211_ATTR_MAC: u16 = 6; +const NL80211_ATTR_MLO_LINK_ID: u16 = 313; + +#[derive(Debug, PartialEq, Eq, Clone)] +enum Nl80211MloLinkNla { + Id(u8), + Mac([u8; ETH_ALEN]), + Other(DefaultNla), +} + +impl Nla for Nl80211MloLinkNla { + fn value_len(&self) -> usize { + match self { + Self::Id(_) => 1, + Self::Mac(_) => ETH_ALEN, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Id(_) => NL80211_ATTR_MLO_LINK_ID, + Self::Mac(_) => NL80211_ATTR_MAC, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Id(d) => buffer[0] = *d, + Self::Mac(s) => buffer.copy_from_slice(s), + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211MloLinkNla +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_ATTR_MLO_LINK_ID => { + let err_msg = format!( + "Invalid NL80211_ATTR_MLO_LINK_ID value {:?}", + payload + ); + Self::Id(parse_u8(payload).context(err_msg)?) + } + NL80211_ATTR_MAC => Self::Mac(if payload.len() == ETH_ALEN { + let mut ret = [0u8; ETH_ALEN]; + ret.copy_from_slice(&payload[..ETH_ALEN]); + ret + } else { + return Err(format!( + "Invalid length of NL80211_ATTR_MAC, expected length {} got {:?}", + ETH_ALEN, payload + ) + .into()); + }), + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +/// Multi-Link Operation +#[derive(Debug, PartialEq, Eq, Clone, Default)] +#[non_exhaustive] +pub struct Nl80211MloLink { + pub id: u8, + pub mac: [u8; ETH_ALEN], +} + +impl Nla for Nl80211MloLink { + fn value_len(&self) -> usize { + Vec::::from(self).as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.id as u16 + 1 + } + + fn emit_value(&self, buffer: &mut [u8]) { + Vec::::from(self).as_slice().emit(buffer) + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211MloLink +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut ret = Self::default(); + let payload = buf.value(); + let err_msg = + format!("Invalid NL80211_ATTR_MLO_LINKS value {:?}", payload); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + match Nl80211MloLinkNla::parse(nla).context(err_msg.clone())? { + Nl80211MloLinkNla::Id(d) => ret.id = d, + Nl80211MloLinkNla::Mac(s) => ret.mac = s, + Nl80211MloLinkNla::Other(attr) => { + log::warn!( + "Got unsupported NL80211_ATTR_MLO_LINKS value {:?}", + attr + ) + } + } + } + Ok(ret) + } +} + +impl From<&Nl80211MloLink> for Vec { + fn from(link: &Nl80211MloLink) -> Self { + vec![ + Nl80211MloLinkNla::Id(link.id), + Nl80211MloLinkNla::Mac(link.mac), + ] + } +} diff --git a/src/station/rate_info.rs b/src/station/rate_info.rs index 55ee1ea..34ccf4e 100644 --- a/src/station/rate_info.rs +++ b/src/station/rate_info.rs @@ -103,7 +103,7 @@ impl Nla for Nl80211RateInfo { | Self::EhtRuAlloc(_) => 1, Self::Bitrate(_) => 2, Self::Bitrate32(_) => 4, - Self::Other(ref nlas) => nlas.value_len(), + Self::Other(nlas) => nlas.value_len(), } } @@ -167,7 +167,7 @@ impl Nla for Nl80211RateInfo { Self::HeRuAlloc(d) => buffer[0] = (*d).into(), Self::EhtGi(d) => buffer[0] = (*d).into(), Self::EhtRuAlloc(d) => buffer[0] = (*d).into(), - Self::Other(ref nlas) => nlas.emit(buffer), + Self::Other(nlas) => nlas.emit(buffer), } } } diff --git a/src/station/station_info.rs b/src/station/station_info.rs index c4be36b..efbc67f 100644 --- a/src/station/station_info.rs +++ b/src/station/station_info.rs @@ -202,12 +202,12 @@ impl Nla for Nl80211StationInfo { | Self::TxDuration(_) | Self::RxDuration(_) | Self::AssociationAtBoottime(_) => 8, - Self::TxBitrate(ref nlas) | Self::RxBitrate(ref nlas) => { + Self::TxBitrate(nlas) | Self::RxBitrate(nlas) => { nlas.as_slice().buffer_len() } - Self::BssParam(ref nlas) => nlas.as_slice().buffer_len(), + Self::BssParam(nlas) => nlas.as_slice().buffer_len(), Self::ChainSignal(d) | Self::ChainSignalAvg(d) => d.len(), - Self::TidStats(ref nlas) => nlas.as_slice().buffer_len(), + Self::TidStats(nlas) => nlas.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } @@ -351,7 +351,7 @@ impl Nla for Nl80211StationInfo { | Nl80211StationInfo::ConnectedToAuthServer(d) => { buffer[0] = (*d).into() } - Self::Other(ref attr) => attr.emit(buffer), + Self::Other(attr) => attr.emit(buffer), } } } diff --git a/src/stats.rs b/src/stats.rs index 23533b8..ac2ed98 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -71,7 +71,7 @@ impl Nla for Nl80211TidStats { | Self::TxMsdu(_) | Self::TxMsduRetries(_) | Self::TxMsduFailed(_) => 4, - Self::TransmitQueueStats(ref nlas) => nlas.as_slice().buffer_len(), + Self::TransmitQueueStats(nlas) => nlas.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } @@ -93,8 +93,8 @@ impl Nla for Nl80211TidStats { | Self::TxMsdu(d) | Self::TxMsduRetries(d) | Self::TxMsduFailed(d) => NativeEndian::write_u64(buffer, *d), - Self::TransmitQueueStats(ref nlas) => nlas.as_slice().emit(buffer), - Self::Other(ref attr) => attr.emit(buffer), + Self::TransmitQueueStats(nlas) => nlas.as_slice().emit(buffer), + Self::Other(attr) => attr.emit(buffer), } } } @@ -229,7 +229,7 @@ impl Nla for Nl80211TransmitQueueStat { | Self::TxBytes(d) | Self::TxPackets(d) | Self::MaxFlows(d) => NativeEndian::write_u32(buffer, *d), - Self::Other(ref attr) => attr.emit(buffer), + Self::Other(attr) => attr.emit(buffer), } } } diff --git a/src/wifi4.rs b/src/wifi4.rs new file mode 100644 index 0000000..ee1ec0e --- /dev/null +++ b/src/wifi4.rs @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: MIT + +// Hold WIFI 4(802.11n) specific data types + +use netlink_packet_utils::{DecodeError, Emitable, Parseable}; + +use crate::bytes::write_u16_le; + +const NL80211_CHAN_NO_HT: u32 = 0; +const NL80211_CHAN_HT20: u32 = 1; +const NL80211_CHAN_HT40MINUS: u32 = 2; +const NL80211_CHAN_HT40PLUS: u32 = 3; + +// kernel data type: `enum nl80211_channel_type` +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum Nl80211HtWiphyChannelType { + NoHt, + Ht20, + Ht40Minus, + Ht40Plus, + Other(u32), +} + +impl From for Nl80211HtWiphyChannelType { + fn from(d: u32) -> Self { + match d { + NL80211_CHAN_NO_HT => Self::NoHt, + NL80211_CHAN_HT20 => Self::Ht20, + NL80211_CHAN_HT40MINUS => Self::Ht40Minus, + NL80211_CHAN_HT40PLUS => Self::Ht40Plus, + _ => Self::Other(d), + } + } +} + +impl From for u32 { + fn from(v: Nl80211HtWiphyChannelType) -> u32 { + match v { + Nl80211HtWiphyChannelType::NoHt => NL80211_CHAN_NO_HT, + Nl80211HtWiphyChannelType::Ht20 => NL80211_CHAN_HT20, + Nl80211HtWiphyChannelType::Ht40Minus => NL80211_CHAN_HT40MINUS, + Nl80211HtWiphyChannelType::Ht40Plus => NL80211_CHAN_HT40PLUS, + Nl80211HtWiphyChannelType::Other(d) => d, + } + } +} + +const IEEE80211_HT_CAP_LDPC_CODING: u16 = 0x0001; +const IEEE80211_HT_CAP_SUP_WIDTH_20_40: u16 = 0x0002; +const IEEE80211_HT_CAP_SM_PS: u16 = 0x000C; +const IEEE80211_HT_CAP_GRN_FLD: u16 = 0x0010; +const IEEE80211_HT_CAP_SGI_20: u16 = 0x0020; +const IEEE80211_HT_CAP_SGI_40: u16 = 0x0040; +const IEEE80211_HT_CAP_TX_STBC: u16 = 0x0080; +const IEEE80211_HT_CAP_RX_STBC: u16 = 0x0300; +const IEEE80211_HT_CAP_DELAY_BA: u16 = 0x0400; +const IEEE80211_HT_CAP_MAX_AMSDU: u16 = 0x0800; +const IEEE80211_HT_CAP_DSSSCCK40: u16 = 0x1000; +const IEEE80211_HT_CAP_40MHZ_INTOLERANT: u16 = 0x4000; +const IEEE80211_HT_CAP_LSIG_TXOP_PROT: u16 = 0x8000; + +// For linux kernel `struct ieee80211_ht_cap.cap_info` +bitflags::bitflags! { + #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] + #[non_exhaustive] + pub struct Nl80211HtCaps: u16 { + const LdpcCoding = IEEE80211_HT_CAP_LDPC_CODING; + const SupWidth2040 = IEEE80211_HT_CAP_SUP_WIDTH_20_40; + const SmPs = IEEE80211_HT_CAP_SM_PS; + const GrnFld = IEEE80211_HT_CAP_GRN_FLD; + const Sgi20 = IEEE80211_HT_CAP_SGI_20; + const Sgi40 = IEEE80211_HT_CAP_SGI_40; + const TxStbc = IEEE80211_HT_CAP_TX_STBC; + const RxStbc = IEEE80211_HT_CAP_RX_STBC; + const DelayBa = IEEE80211_HT_CAP_DELAY_BA; + const MaxAmsdu = IEEE80211_HT_CAP_MAX_AMSDU; + const Dssscck40 = IEEE80211_HT_CAP_DSSSCCK40; + const Intolerant40Mhz = IEEE80211_HT_CAP_40MHZ_INTOLERANT; + const LsigTxopProt = IEEE80211_HT_CAP_LSIG_TXOP_PROT; + const _ = !0; + } +} + +const IEEE80211_HT_MCS_MASK_LEN: usize = 10; +const NL80211_BAND_MCS_INFO_LEN: usize = 16; + +// kernel data type: `struct ieee80211_mcs_info` +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Nl80211HtMcsInfo { + pub rx_mask: [u8; IEEE80211_HT_MCS_MASK_LEN], + pub rx_highest: u16, + pub tx_params: u8, +} + +impl Emitable for Nl80211HtMcsInfo { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < NL80211_BAND_MCS_INFO_LEN { + log::error!( + "Buffer size is smaller than NL80211_BAND_MCS_INFO_LEN \ + {NL80211_BAND_MCS_INFO_LEN}" + ); + return; + } + buffer.iter_mut().for_each(|m| *m = 0); + buffer[..IEEE80211_HT_MCS_MASK_LEN].copy_from_slice(&self.rx_mask); + write_u16_le( + &mut buffer + [IEEE80211_HT_MCS_MASK_LEN..IEEE80211_HT_MCS_MASK_LEN + 2], + self.rx_highest, + ); + buffer[IEEE80211_HT_MCS_MASK_LEN + 2] = self.tx_params; + } +} + +impl Nl80211HtMcsInfo { + // `struct ieee80211_mcs_info`. + // Kernel document confirmed this is 16 bytes + pub const LENGTH: usize = NL80211_BAND_MCS_INFO_LEN; +} + +impl + ?Sized> Parseable for Nl80211HtMcsInfo { + fn parse(buf: &T) -> Result { + let buf: &[u8] = buf.as_ref(); + if buf.len() < NL80211_BAND_MCS_INFO_LEN { + return Err(format!( + "Expecting `struct ieee80211_ht_mcs_info` u8 array with \ + size {NL80211_BAND_MCS_INFO_LEN}, but got length {}", + buf.len() + ) + .into()); + } + let mut rx_mask = [0u8; IEEE80211_HT_MCS_MASK_LEN]; + rx_mask.copy_from_slice(&buf[..IEEE80211_HT_MCS_MASK_LEN]); + + Ok(Self { + rx_mask, + rx_highest: u16::from_le_bytes([ + buf[IEEE80211_HT_MCS_MASK_LEN], + buf[IEEE80211_HT_MCS_MASK_LEN + 1], + ]), + tx_params: buf[IEEE80211_HT_MCS_MASK_LEN + 2], + }) + } +} + +const NL80211_HT_CAPABILITY_LEN: usize = 26; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Nl80211HtCapabilityMask(pub [u8; NL80211_HT_CAPABILITY_LEN]); + +impl Nl80211HtCapabilityMask { + pub const LENGTH: usize = NL80211_HT_CAPABILITY_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } +} + +impl Emitable for Nl80211HtCapabilityMask { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Nl80211HtCapabilityMask buffer size is smaller than \ + required size {}", + Self::LENGTH + ); + return; + } + buffer[..Self::LENGTH].copy_from_slice(&self.0) + } +} diff --git a/src/wifi5.rs b/src/wifi5.rs new file mode 100644 index 0000000..14dadb9 --- /dev/null +++ b/src/wifi5.rs @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: MIT + +// Hold WIFI 5(802.11ac) specific data types + +use anyhow::Context; +use netlink_packet_utils::{ + parsers::parse_u32, DecodeError, Emitable, Parseable, +}; + +use crate::bytes::write_u16_le; + +const NL80211_BAND_VHT_MCS_INFO_LEN: usize = 8; + +// We cannot use buffer! macro here as these u16 are all little endian while +// The `buffer!` does not support little endian yet. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Nl80211VhtMcsInfo { + pub rx_mcs_map: u16, + pub rx_highest: u16, + pub tx_mcs_map: u16, + pub tx_highest: u16, +} + +impl Nl80211VhtMcsInfo { + // `struct ieee80211_vht_mcs_info` + // Kernel document confirmed this is 32 bytes + pub const LENGTH: usize = NL80211_BAND_VHT_MCS_INFO_LEN; +} + +impl Emitable for Nl80211VhtMcsInfo { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < NL80211_BAND_VHT_MCS_INFO_LEN { + log::error!( + "Buffer size is smaller than NL80211_BAND_VHT_MCS_INFO_LEN \ + {NL80211_BAND_VHT_MCS_INFO_LEN}" + ); + return; + } + write_u16_le(&mut buffer[0..2], self.rx_mcs_map); + write_u16_le(&mut buffer[2..4], self.rx_highest); + write_u16_le(&mut buffer[4..6], self.tx_mcs_map); + write_u16_le(&mut buffer[6..8], self.tx_highest); + } +} + +impl + ?Sized> Parseable for Nl80211VhtMcsInfo { + fn parse(buf: &T) -> Result { + let buf: &[u8] = buf.as_ref(); + if buf.len() < NL80211_BAND_VHT_MCS_INFO_LEN { + return Err(format!( + "Expecting `struct ieee80211_vht_mcs_info` u8 array with \ + size {NL80211_BAND_VHT_MCS_INFO_LEN}, but got length {}", + buf.len() + ) + .into()); + } + + Ok(Self { + rx_mcs_map: u16::from_le_bytes([buf[0], buf[1]]), + rx_highest: u16::from_le_bytes([buf[2], buf[3]]), + tx_mcs_map: u16::from_le_bytes([buf[4], buf[5]]), + tx_highest: u16::from_le_bytes([buf[6], buf[7]]), + }) + } +} + +const IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: u32 = 0x00000000; +const IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: u32 = 0x00000001; +const IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: u32 = 0x00000002; +const IEEE80211_VHT_CAP_MAX_MPDU_MASK: u32 = 0x00000003; +const IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: u32 = 0x00000004; +const IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: u32 = 0x00000008; +const IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK: u32 = 0x0000000C; +const IEEE80211_VHT_CAP_RXLDPC: u32 = 0x00000010; +const IEEE80211_VHT_CAP_SHORT_GI_80: u32 = 0x00000020; +const IEEE80211_VHT_CAP_SHORT_GI_160: u32 = 0x00000040; +const IEEE80211_VHT_CAP_TXSTBC: u32 = 0x00000080; +const IEEE80211_VHT_CAP_RXSTBC_1: u32 = 0x00000100; +const IEEE80211_VHT_CAP_RXSTBC_2: u32 = 0x00000200; +const IEEE80211_VHT_CAP_RXSTBC_3: u32 = 0x00000300; +const IEEE80211_VHT_CAP_RXSTBC_4: u32 = 0x00000400; +const IEEE80211_VHT_CAP_RXSTBC_MASK: u32 = 0x00000700; +const IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE: u32 = 0x00000800; +const IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE: u32 = 0x00001000; +const IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT: u32 = 13; +const IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK: u32 = + 7 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; +const IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT: u32 = 16; +const IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK: u32 = + 7 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; +const IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE: u32 = 0x00080000; +const IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE: u32 = 0x00100000; +const IEEE80211_VHT_CAP_VHT_TXOP_PS: u32 = 0x00200000; +const IEEE80211_VHT_CAP_HTC_VHT: u32 = 0x00400000; +const IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT: u32 = 23; +const IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK: u32 = + 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +const IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB: u32 = 0x08000000; +const IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB: u32 = 0x0c000000; +const IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN: u32 = 0x10000000; +const IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN: u32 = 0x20000000; +const IEEE80211_VHT_CAP_EXT_NSS_BW_MASK: u32 = 0xc0000000; + +bitflags::bitflags! { + #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] + #[non_exhaustive] + pub struct Nl80211VhtCapInfo: u32 { + const MaxMpduLength3895 = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + const MaxMpduLength7991 = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + const MaxMpduLength11454 = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + const MaxMpduMask = IEEE80211_VHT_CAP_MAX_MPDU_MASK; + const SuppChanWidth160mhz = IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + const SuppChanWidth160With80plus80mhz = + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + const SuppChanWidthMask = IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + const Rxldpc = IEEE80211_VHT_CAP_RXLDPC; + const ShortGi80 = IEEE80211_VHT_CAP_SHORT_GI_80; + const ShortGi160 = IEEE80211_VHT_CAP_SHORT_GI_160; + const TxStbc = IEEE80211_VHT_CAP_TXSTBC; + const Rxstbc1 = IEEE80211_VHT_CAP_RXSTBC_1; + const Rxstbc2 = IEEE80211_VHT_CAP_RXSTBC_2; + const Rxstbc3 = IEEE80211_VHT_CAP_RXSTBC_3; + const Rxstbc4 = IEEE80211_VHT_CAP_RXSTBC_4; + const RxstbcMask = IEEE80211_VHT_CAP_RXSTBC_MASK; + const SuBeamformerCapable = IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + const SuBeamformeeCapable = IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + const BeamformeeStsMask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + const SoundingDimensionsMask = IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + const MuBeamformerCapable = IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + const MuBeamformeeCapable = IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + const VhtTxopPs = IEEE80211_VHT_CAP_VHT_TXOP_PS; + const HtcVht = IEEE80211_VHT_CAP_HTC_VHT; + const MaxAMpduLengthExponentMask = + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + const VhtLinkAdaptationVhtUnsolMfb = + IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB; + const VhtLinkAdaptationVhtMrqMfb = + IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; + const RxAntennaPattern = IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; + const TxAntennaPattern = IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + const ExtNssBwMask = IEEE80211_VHT_CAP_EXT_NSS_BW_MASK; + const _ = !0; + } +} + +impl + ?Sized> Parseable for Nl80211VhtCapInfo { + fn parse(buf: &T) -> Result { + let buf: &[u8] = buf.as_ref(); + Ok(Self::from_bits_retain(parse_u32(buf).context(format!( + "Invalid Nl80211VhtCapInfo payload {buf:?}" + ))?)) + } +} + +impl Nl80211VhtCapInfo { + pub const LENGTH: usize = 4; +} + +impl Emitable for Nl80211VhtCapInfo { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + buffer.copy_from_slice(&self.bits().to_ne_bytes()) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Nl80211VhtCapability { + pub cap_info: Nl80211VhtCapInfo, + pub mcs_info: Nl80211VhtMcsInfo, +} + +// TODO: Please add getter and setter function according to +// 802.11ac-2013 section '8.4.2.160 VHT Capabilities element' +impl Nl80211VhtCapability { + pub const LENGTH: usize = 12; +} + +impl Emitable for Nl80211VhtCapability { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Buffer size {} is smaller than desired size {}", + buffer.len(), + Self::LENGTH, + ); + return; + } + self.cap_info.emit(&mut buffer[..Nl80211VhtCapInfo::LENGTH]); + self.mcs_info.emit(&mut buffer[Nl80211VhtCapInfo::LENGTH..]); + } +} + +impl + ?Sized> Parseable for Nl80211VhtCapability { + fn parse(buf: &T) -> Result { + let buf: &[u8] = buf.as_ref(); + if buf.len() < Self::LENGTH { + Err(format!( + "Invalid length of payload for Nl80211VhtCapability, \ + expecting {}, but got {}", + Self::LENGTH, + buf.len() + ) + .into()) + } else { + Ok(Self { + cap_info: Nl80211VhtCapInfo::parse( + &buf[..Nl80211VhtCapInfo::LENGTH], + )?, + mcs_info: Nl80211VhtMcsInfo::parse( + &buf[Nl80211VhtCapInfo::LENGTH..], + )?, + }) + } + } +} diff --git a/src/wifi6.rs b/src/wifi6.rs new file mode 100644 index 0000000..efc1cd3 --- /dev/null +++ b/src/wifi6.rs @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: MIT + +// Hold WIFI6 (802.11ax) specific data types. + +use netlink_packet_utils::{ + traits::{Emitable, Parseable}, + DecodeError, +}; + +use crate::bytes::{get_bit, get_bits_as_u8, write_u16_le}; + +const HE_MAC_CAP_INFO_LEN: usize = 6; + +/// "HE MAC Capabilities Information field" +/// +/// IEEE 802.11ax-2021 section 9.4.2.248.2 +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211HeMacCapInfo(pub [u8; HE_MAC_CAP_INFO_LEN]); + +impl Nl80211HeMacCapInfo { + pub const LENGTH: usize = HE_MAC_CAP_INFO_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } + + pub fn tc_he_support(&self) -> bool { + get_bit(&self.0, 0) + } + pub fn wt_requester_support(&self) -> bool { + get_bit(&self.0, 1) + } + pub fn wt_responder_support(&self) -> bool { + get_bit(&self.0, 2) + } + pub fn ynamic_fragmentation_support(&self) -> u8 { + get_bits_as_u8(&self.0, 3, 4) + } + pub fn ax_fragmented_msdu_amsdu(&self) -> u8 { + get_bits_as_u8(&self.0, 5, 7) + } + pub fn in_fragment_size(&self) -> u8 { + get_bits_as_u8(&self.0, 8, 9) + } + pub fn rigger_frame_mac_pending_duration(&self) -> u8 { + get_bits_as_u8(&self.0, 10, 11) + } + pub fn ulti_tid_aggregation_rx_support(&self) -> u8 { + get_bits_as_u8(&self.0, 12, 14) + } + pub fn e_link_adapttion_support(&self) -> u8 { + get_bits_as_u8(&self.0, 15, 16) + } + pub fn ll_ack_support(&self) -> bool { + get_bit(&self.0, 17) + } + pub fn ts_support(&self) -> bool { + get_bit(&self.0, 18) + } + pub fn sr_support(&self) -> bool { + get_bit(&self.0, 19) + } + pub fn roadcast_twt_support(&self) -> bool { + get_bit(&self.0, 20) + } + pub fn upport_32_bit_ba_bitmap(&self) -> bool { + get_bit(&self.0, 21) + } + pub fn u_cascading_support(&self) -> bool { + get_bit(&self.0, 22) + } + pub fn ck_enabled_aggregation_support(&self) -> bool { + get_bit(&self.0, 23) + } + // bit 24 is reserved + pub fn m_control_support(&self) -> bool { + get_bit(&self.0, 25) + } + pub fn fdma_ra_support(&self) -> bool { + get_bit(&self.0, 26) + } + pub fn ax_a_mpdu_length_exponent_extentsion(&self) -> u8 { + get_bits_as_u8(&self.0, 27, 28) + } + pub fn _msdu_fragmentation_support(&self) -> bool { + get_bit(&self.0, 29) + } + pub fn lexible_twt_scheduler_support(&self) -> bool { + get_bit(&self.0, 30) + } + pub fn x_control_frame_to_multibss(&self) -> bool { + get_bit(&self.0, 31) + } + pub fn srp_bqrp_a_mpdu_aggregation(&self) -> bool { + get_bit(&self.0, 32) + } + pub fn tp_support(&self) -> bool { + get_bit(&self.0, 33) + } + pub fn qr_support(&self) -> bool { + get_bit(&self.0, 34) + } + pub fn sr_responder(&self) -> bool { + get_bit(&self.0, 35) + } + pub fn dp_feedback_report_support(&self) -> bool { + get_bit(&self.0, 36) + } + pub fn ps_support(&self) -> bool { + get_bit(&self.0, 37) + } + pub fn _msdu_not_under_ba_in_ack_enabled_a_mpdu_support(&self) -> bool { + get_bit(&self.0, 38) + } + pub fn ulti_tid_aggregation_tx_support(&self) -> u8 { + get_bits_as_u8(&self.0, 39, 41) + } + pub fn e_subchannel_selective_ransmission_support(&self) -> bool { + get_bit(&self.0, 42) + } + pub fn l_2x_996_tone_ru_support(&self) -> bool { + get_bit(&self.0, 43) + } + pub fn m_control_ul_mu_data_disable_rx_support(&self) -> bool { + get_bit(&self.0, 44) + } + pub fn e_dyanmic_sm_power_save(&self) -> bool { + get_bit(&self.0, 45) + } + pub fn unctured_sounding_support(&self) -> bool { + get_bit(&self.0, 46) + } + pub fn t_and_vht_trigger_frame_rx_support(&self) -> bool { + get_bit(&self.0, 47) + } +} + +impl Emitable for Nl80211HeMacCapInfo { + fn buffer_len(&self) -> usize { + HE_MAC_CAP_INFO_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < HE_MAC_CAP_INFO_LEN { + log::error!( + "Buffer size is smaller than HE_MAC_CAP_INFO_LEN \ + {HE_MAC_CAP_INFO_LEN}" + ); + return; + } + buffer[..Self::LENGTH].copy_from_slice(&self.0) + } +} + +const HE_PHY_CAP_INFO_LEN: usize = 11; + +/// "HE PHY Capabilities Information field" +/// +/// IEEE 802.11ax-2021 section 9.4.2.248.3 +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211HePhyCapInfo(pub [u8; HE_PHY_CAP_INFO_LEN]); + +impl Nl80211HePhyCapInfo { + pub const LENGTH: usize = HE_PHY_CAP_INFO_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } + + pub fn supported_channel_width_set(&self) -> u8 { + get_bits_as_u8(&self.0, 1, 7) + } + + // TODO: Add all fields as functions by checking 802.11ax-2021 +} + +impl Emitable for Nl80211HePhyCapInfo { + fn buffer_len(&self) -> usize { + HE_PHY_CAP_INFO_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < HE_PHY_CAP_INFO_LEN { + log::error!( + "Buffer size is smaller than HE_PHY_CAP_INFO_LEN \ + {HE_PHY_CAP_INFO_LEN}" + ); + return; + } + buffer[..HE_PHY_CAP_INFO_LEN].copy_from_slice(&self.0) + } +} + +const NL80211_HE_MCS_NSS_SUPP_LEN: usize = 12; + +/// Tx/Rx HE MCS NSS Support Field +/// +/// The released 802.11ax-2021 has no `Tx/Rx HE MCS NSS Support` section, this +/// struct is merely copy of linux kernel `struct ieee80211_he_mcs_nss_supp`. +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub struct Nl80211HeMcsNssSupp { + /// Rx MCS map 2 bits for each stream, total 8 streams, for channel widths + /// less than 80MHz. + pub rx_mcs_80: u16, + /// Tx MCS map 2 bits for each stream, total 8 streams, for channel widths + /// less than 80MHz. + pub tx_mcs_80: u16, + /// Rx MCS map 2 bits for each stream, total 8 streams, for channel width + /// 160MHz. + pub rx_mcs_160: u16, + /// Tx MCS map 2 bits for each stream, total 8 streams, for channel width + /// 160MHz. + pub tx_mcs_160: u16, + /// Rx MCS map 2 bits for each stream, total 8 streams, for channel width + /// 80p80MHz. + pub rx_mcs_80p80: u16, + /// Tx MCS map 2 bits for each stream, total 8 streams, for channel width + /// 80p80MHz. + pub tx_mcs_80p80: u16, +} + +impl Nl80211HeMcsNssSupp { + pub const LENGTH: usize = NL80211_HE_MCS_NSS_SUPP_LEN; +} + +impl Emitable for Nl80211HeMcsNssSupp { + fn buffer_len(&self) -> usize { + NL80211_HE_MCS_NSS_SUPP_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < NL80211_HE_MCS_NSS_SUPP_LEN { + log::error!( + "Buffer size is smaller than NL80211_HE_MCS_NSS_SUPP_LEN \ + {NL80211_HE_MCS_NSS_SUPP_LEN}" + ); + return; + } + write_u16_le(&mut buffer[0..2], self.rx_mcs_80); + write_u16_le(&mut buffer[2..4], self.tx_mcs_80); + write_u16_le(&mut buffer[4..6], self.rx_mcs_160); + write_u16_le(&mut buffer[6..8], self.tx_mcs_160); + write_u16_le(&mut buffer[8..10], self.rx_mcs_80p80); + write_u16_le(&mut buffer[10..12], self.tx_mcs_80p80); + } +} + +impl + ?Sized> Parseable for Nl80211HeMcsNssSupp { + fn parse(buf: &T) -> Result { + let buf: &[u8] = buf.as_ref(); + if buf.len() < Self::LENGTH { + Err(format!( + "Expecting `struct ieee80211_he_mcs_nss_supp` u8 array \ + with size {}, but got length {}", + Self::LENGTH, + buf.len() + ) + .into()) + } else { + Ok(Self { + rx_mcs_80: u16::from_le_bytes([buf[0], buf[1]]), + tx_mcs_80: u16::from_le_bytes([buf[2], buf[3]]), + rx_mcs_160: u16::from_le_bytes([buf[4], buf[5]]), + tx_mcs_160: u16::from_le_bytes([buf[6], buf[7]]), + rx_mcs_80p80: u16::from_le_bytes([buf[8], buf[9]]), + tx_mcs_80p80: u16::from_le_bytes([buf[10], buf[11]]), + }) + } + } +} + +// Kernel says the maximum is 25, but my(Gris Ge) understanding of IEEE +// 802.11ax-2021 indicate the maximum size is: +// ((NSTS + 1) * 6 * 4 + 4 + 3 ) / 8 + 1 == 28 bytes +// | | | | +// | | | +---- NSTS +// | | +------ RU index bitmask(4 bits) +// | +--- RU allocation index, 4 combination +// | +// +---- PPET 16 (3 bits) and PPET 8 (3 bits) +// +// The 25 bytes could be using `NSTS` instead of `NSTS +1`. +// +const IEEE80211_HE_PPE_THRES_MAX_LEN: usize = 25; + +/// IEEE 802.11ax-2021 section 9.4.2.248.5 +/// "PPE Thresholds field" +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211HePpeThreshold(pub [u8; IEEE80211_HE_PPE_THRES_MAX_LEN]); + +impl Nl80211HePpeThreshold { + pub const LENGTH: usize = IEEE80211_HE_PPE_THRES_MAX_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } + + pub fn nsts(&self) -> u8 { + get_bits_as_u8(&self.0, 0, 2) + } + + pub fn ru_index_bitmask(&self) -> u8 { + get_bits_as_u8(&self.0, 3, 6) + } + + // TODO, add iterator to access thresholds +} + +impl Emitable for Nl80211HePpeThreshold { + fn buffer_len(&self) -> usize { + IEEE80211_HE_PPE_THRES_MAX_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < IEEE80211_HE_PPE_THRES_MAX_LEN { + log::error!( + "Buffer size is smaller than IEEE80211_HE_PPE_THRES_MAX_LEN \ + {IEEE80211_HE_PPE_THRES_MAX_LEN}" + ); + return; + } + buffer[..IEEE80211_HE_PPE_THRES_MAX_LEN].copy_from_slice(&self.0) + } +} + +/* HE 6 GHz band capabilities */ +// const IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START: u16 = 0x0007; +// const IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP: u16 = 0x0038; +// const IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN: u16 = 0x00c0; +// const IEEE80211_HE_6GHZ_CAP_SM_PS: u16 = 0x0600; +// const IEEE80211_HE_6GHZ_CAP_RD_RESPONDER: u16 = 0x0800; +// const IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS: u16 = 0x1000; +// const IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS: u16 = 0x2000; + +const IEEE80211_HE_6GHZ_CAP_LEN: usize = 2; + +/// "HE 6 GHz Band Capabilities element" +/// +/// IEEE 802.11ax-2021 section 9.4.2.263 +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211He6GhzCapa(pub [u8; IEEE80211_HE_6GHZ_CAP_LEN]); + +impl Nl80211He6GhzCapa { + pub const LENGTH: usize = IEEE80211_HE_6GHZ_CAP_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } + + pub fn minimum_mpdu_start_spacing(&self) -> u8 { + get_bits_as_u8(&self.0, 0, 2) + } + + pub fn maximum_a_mpdu_length_exponent(&self) -> u8 { + get_bits_as_u8(&self.0, 3, 5) + } + + pub fn maximum_mpdu_length(&self) -> u8 { + get_bits_as_u8(&self.0, 6, 7) + } + + pub fn sm_power_save(&self) -> u8 { + get_bits_as_u8(&self.0, 9, 10) + } + + pub fn rd_responder(&self) -> bool { + get_bit(&self.0, 11) + } + + pub fn rx_antenna_pattern_consistency(&self) -> bool { + get_bit(&self.0, 12) + } + + pub fn tx_antenna_pattern_consistency(&self) -> bool { + get_bit(&self.0, 12) + } +} + +impl Emitable for Nl80211He6GhzCapa { + fn buffer_len(&self) -> usize { + IEEE80211_HE_6GHZ_CAP_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < IEEE80211_HE_6GHZ_CAP_LEN { + log::error!( + "Buffer size is smaller than IEEE80211_HE_6GHZ_CAP_LEN \ + {IEEE80211_HE_6GHZ_CAP_LEN}" + ); + return; + } + buffer[..IEEE80211_HE_6GHZ_CAP_LEN].copy_from_slice(&self.0) + } +} diff --git a/src/wifi7.rs b/src/wifi7.rs new file mode 100644 index 0000000..fc172a0 --- /dev/null +++ b/src/wifi7.rs @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: MIT + +// WIFI 7(802.11be) specific data types + +use netlink_packet_utils::{DecodeError, Emitable}; + +const EHT_MAC_CAP_INFO_LEN: usize = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtMacCapInfo(pub [u8; EHT_MAC_CAP_INFO_LEN]); + +// TODO: Failed to get WIFI7(802.11be) SPEC PDF, hence no parsing functions +impl Nl80211EhtMacCapInfo { + pub const LENGTH: usize = EHT_MAC_CAP_INFO_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } +} + +impl Emitable for Nl80211EhtMacCapInfo { + fn buffer_len(&self) -> usize { + EHT_MAC_CAP_INFO_LEN + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < EHT_MAC_CAP_INFO_LEN { + log::error!( + "Buffer size is smaller than EHT_MAC_CAP_INFO_LEN \ + {EHT_MAC_CAP_INFO_LEN}" + ); + return; + } + buffer[..EHT_MAC_CAP_INFO_LEN].copy_from_slice(&self.0) + } +} + +const EHT_PHY_CAP_INFO_LEN: usize = 9; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtPhyCapInfo(pub [u8; EHT_PHY_CAP_INFO_LEN]); + +impl Nl80211EhtPhyCapInfo { + pub const LENGTH: usize = EHT_PHY_CAP_INFO_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } +} + +impl Emitable for Nl80211EhtPhyCapInfo { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < EHT_PHY_CAP_INFO_LEN { + log::error!( + "Buffer size is smaller than EHT_PHY_CAP_INFO_LEN \ + {EHT_PHY_CAP_INFO_LEN}" + ); + return; + } + buffer[..EHT_PHY_CAP_INFO_LEN].copy_from_slice(&self.0) + } +} + +/// MCS/NSS support for 20 MHz-only STA. +// Kernel data type: `struct ieee80211_eht_mcs_nss_supp_20mhz_only` +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtMcsNssSuppOnly20Mhz { + /// MCS 0 - 7 + pub rx_tx_mcs7_max_nss: u8, + /// MCS 8 - 9 + pub rx_tx_mcs9_max_nss: u8, + /// MCS 10 - 11 + pub rx_tx_mcs11_max_nss: u8, + /// MCS 12 - 13 + pub rx_tx_mcs13_max_nss: u8, +} + +impl Nl80211EhtMcsNssSuppOnly20Mhz { + pub const LENGTH: usize = 4; + + pub fn parse(buf: &[u8]) -> Self { + Self { + rx_tx_mcs7_max_nss: *buf.first().unwrap_or(&0), + rx_tx_mcs9_max_nss: *buf.get(1).unwrap_or(&0), + rx_tx_mcs11_max_nss: *buf.get(2).unwrap_or(&0), + rx_tx_mcs13_max_nss: *buf.get(3).unwrap_or(&0), + } + } +} + +impl Emitable for Nl80211EhtMcsNssSuppOnly20Mhz { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Buffer size is smaller than required length {}", + Self::LENGTH + ); + return; + } + buffer[0] = self.rx_tx_mcs7_max_nss; + buffer[1] = self.rx_tx_mcs9_max_nss; + buffer[2] = self.rx_tx_mcs11_max_nss; + buffer[3] = self.rx_tx_mcs13_max_nss; + } +} + +// Kernel data type: `struct ieee80211_eht_mcs_nss_supp_bw` +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtMcsNssSuppBw { + /// MCS 8 - 9 + pub rx_tx_mcs9_max_nss: u8, + /// MCS 10 - 11 + pub rx_tx_mcs11_max_nss: u8, + /// MCS 12 - 13 + pub rx_tx_mcs13_max_nss: u8, +} + +impl Nl80211EhtMcsNssSuppBw { + pub const LENGTH: usize = 3; + + pub fn parse(buf: &[u8]) -> Self { + Self { + rx_tx_mcs9_max_nss: *buf.first().unwrap_or(&0), + rx_tx_mcs11_max_nss: *buf.get(1).unwrap_or(&0), + rx_tx_mcs13_max_nss: *buf.get(2).unwrap_or(&0), + } + } +} + +impl Emitable for Nl80211EhtMcsNssSuppBw { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Buffer size is smaller than required length {}", + Self::LENGTH + ); + return; + } + buffer[0] = self.rx_tx_mcs9_max_nss; + buffer[1] = self.rx_tx_mcs11_max_nss; + buffer[2] = self.rx_tx_mcs13_max_nss; + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtMcsNssSuppMoreThan20Mhz { + pub mhz_80: Nl80211EhtMcsNssSuppBw, + pub mhz_160: Nl80211EhtMcsNssSuppBw, + pub mhz_320: Nl80211EhtMcsNssSuppBw, +} + +impl Nl80211EhtMcsNssSuppMoreThan20Mhz { + pub const LENGTH: usize = Nl80211EhtMcsNssSuppBw::LENGTH * 3; + + pub fn parse(buf: &[u8]) -> Self { + Self { + mhz_80: Nl80211EhtMcsNssSuppBw::parse(buf), + mhz_160: Nl80211EhtMcsNssSuppBw::parse( + &buf[Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH..], + ), + mhz_320: Nl80211EhtMcsNssSuppBw::parse( + &buf[Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH * 2..], + ), + } + } +} + +impl Emitable for Nl80211EhtMcsNssSuppMoreThan20Mhz { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Buffer size is smaller than required length {}", + Self::LENGTH + ); + return; + } + self.mhz_80.emit(buffer); + self.mhz_160 + .emit(&mut buffer[Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH..]); + self.mhz_320 + .emit(&mut buffer[Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH * 2..]); + } +} + +// Kernel data type: `struct ieee80211_eht_mcs_nss_supp` +/// EHT max supported NSS per MCS +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211EhtMcsNssSupp { + Only20Mhz(Nl80211EhtMcsNssSuppOnly20Mhz), + MoreThan20Mhz(Nl80211EhtMcsNssSuppMoreThan20Mhz), + /// This other might be removed once 802.11be standard published + Other(Vec), +} + +impl Emitable for Nl80211EhtMcsNssSupp { + fn buffer_len(&self) -> usize { + match self { + Self::Only20Mhz(_) => Nl80211EhtMcsNssSuppOnly20Mhz::LENGTH, + Self::MoreThan20Mhz(_) => Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH, + Self::Other(v) => v.len(), + } + } + + fn emit(&self, buffer: &mut [u8]) { + match self { + Self::Only20Mhz(v) => v.emit(buffer), + Self::MoreThan20Mhz(v) => v.emit(buffer), + Self::Other(v) => buffer.copy_from_slice(&v[..buffer.len()]), + } + } +} + +impl Nl80211EhtMcsNssSupp { + pub fn parse(buf: &[u8]) -> Result { + if buf.len() > Nl80211EhtMcsNssSuppOnly20Mhz::LENGTH { + if buf.len() >= Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH { + Ok(Self::MoreThan20Mhz( + Nl80211EhtMcsNssSuppMoreThan20Mhz::parse(buf), + )) + } else { + Err(format!( + "Invalid NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET \ + data, expecting u8 array with size {} or {}, but got {}", + Nl80211EhtMcsNssSuppOnly20Mhz::LENGTH, + Nl80211EhtMcsNssSuppMoreThan20Mhz::LENGTH, + buf.len() + ) + .into()) + } + } else { + Ok(Self::Only20Mhz(Nl80211EhtMcsNssSuppOnly20Mhz::parse(buf))) + } + } +} + +const IEEE80211_EHT_PPE_THRES_MAX_LEN: usize = 32; + +/// PPE thresholds +// TODO: write passing function +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211EhtPpeThres(pub [u8; IEEE80211_EHT_PPE_THRES_MAX_LEN]); + +impl Nl80211EhtPpeThres { + pub const LENGTH: usize = IEEE80211_EHT_PPE_THRES_MAX_LEN; + + pub fn new(value: &[u8]) -> Self { + let mut data = [0u8; Self::LENGTH]; + if value.len() > Self::LENGTH { + data.copy_from_slice(&value[..Self::LENGTH]); + } else { + data[..value.len()].copy_from_slice(value) + } + Self(data) + } +} + +impl Emitable for Nl80211EhtPpeThres { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + if buffer.len() < Self::LENGTH { + log::error!( + "Buffer size is smaller than required length {}", + Self::LENGTH + ); + return; + } + buffer[..Self::LENGTH].copy_from_slice(&self.0) + } +} diff --git a/src/wiphy/band.rs b/src/wiphy/band.rs new file mode 100644 index 0000000..3e10e81 --- /dev/null +++ b/src/wiphy/band.rs @@ -0,0 +1,1400 @@ +// SPDX-License-Identifier: MIT + +// Most documentation comments are copied and modified from linux kernel +// include/uapi/linux/nl80211.h which is holding these license disclaimer: +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2024 Intel Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::{parse_u16, parse_u32, parse_u8}, + DecodeError, Emitable, Parseable, ParseableParametrized, +}; + +use crate::{ + bytes::{write_u16, write_u32}, + Nl80211EhtMacCapInfo, Nl80211EhtMcsNssSupp, Nl80211EhtPhyCapInfo, + Nl80211EhtPpeThres, Nl80211He6GhzCapa, Nl80211HeMacCapInfo, + Nl80211HeMcsNssSupp, Nl80211HePhyCapInfo, Nl80211HePpeThreshold, + Nl80211HtCaps, Nl80211HtMcsInfo, Nl80211VhtCapInfo, Nl80211VhtMcsInfo, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211Band { + pub kind: Nl80211BandType, + pub info: Vec, +} + +impl Nla for Nl80211Band { + fn value_len(&self) -> usize { + self.info.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.kind.into() + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.info.as_slice().emit(buffer) + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Band { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let band_type = Nl80211BandType::from(buf.kind()); + let payload = buf.value(); + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = + format!("Invalid NL80211_ATTR_WIPHY_BANDS value {:?}", nla); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211BandInfo::parse(nla)?); + } + Ok(Self { + kind: band_type, + info: nlas, + }) + } +} + +const NL80211_BAND_2GHZ: u16 = 0; +const NL80211_BAND_5GHZ: u16 = 1; +const NL80211_BAND_60GHZ: u16 = 2; +const NL80211_BAND_6GHZ: u16 = 3; +const NL80211_BAND_S1GHZ: u16 = 4; +const NL80211_BAND_LC: u16 = 5; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211BandType { + Band2GHz, + Band5GHz, + Band60GHz, + Band6GHz, + BandS1GHz, + /// light communication band + BandLc, + Other(u16), +} + +impl From for Nl80211BandType { + fn from(d: u16) -> Self { + match d { + NL80211_BAND_2GHZ => Self::Band2GHz, + NL80211_BAND_5GHZ => Self::Band5GHz, + NL80211_BAND_60GHZ => Self::Band60GHz, + NL80211_BAND_6GHZ => Self::Band6GHz, + NL80211_BAND_S1GHZ => Self::BandS1GHz, + NL80211_BAND_LC => Self::BandLc, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211BandType) -> u16 { + match v { + Nl80211BandType::Band2GHz => NL80211_BAND_2GHZ, + Nl80211BandType::Band5GHz => NL80211_BAND_5GHZ, + Nl80211BandType::Band60GHz => NL80211_BAND_60GHZ, + Nl80211BandType::Band6GHz => NL80211_BAND_6GHZ, + Nl80211BandType::BandS1GHz => NL80211_BAND_S1GHZ, + Nl80211BandType::BandLc => NL80211_BAND_LC, + Nl80211BandType::Other(d) => d, + } + } +} + +bitflags::bitflags! { + /// If not bands are set, it means don't care and the device will decide + /// what to use + #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] + #[non_exhaustive] + pub struct Nl80211BandTypes: u32 { + const Band2GHz = 1 << NL80211_BAND_2GHZ; + const Band5GHz = 1 << NL80211_BAND_5GHZ; + const Band60GHz = 1 << NL80211_BAND_60GHZ; + const Band6GHz = 1 << NL80211_BAND_6GHZ; + const BandS1GHz = 1<< NL80211_BAND_S1GHZ; + /// light communication band (placeholder) + const BandLc = 1 << NL80211_BAND_LC; + } +} + +impl Nl80211BandTypes { + pub const LENGTH: usize = 4; + + pub fn parse(raw: &[u8]) -> Result { + Ok(Self::from_bits_retain(parse_u32(raw).context(format!( + "Invalid Nl80211BandTypes payload {raw:?}" + ))?)) + } +} + +impl Emitable for Nl80211BandTypes { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + buffer.copy_from_slice(&self.bits().to_ne_bytes()) + } +} + +const NL80211_BAND_ATTR_FREQS: u16 = 1; +const NL80211_BAND_ATTR_RATES: u16 = 2; +const NL80211_BAND_ATTR_HT_MCS_SET: u16 = 3; +const NL80211_BAND_ATTR_HT_CAPA: u16 = 4; +const NL80211_BAND_ATTR_HT_AMPDU_FACTOR: u16 = 5; +const NL80211_BAND_ATTR_HT_AMPDU_DENSITY: u16 = 6; +const NL80211_BAND_ATTR_VHT_MCS_SET: u16 = 7; +const NL80211_BAND_ATTR_VHT_CAPA: u16 = 8; +const NL80211_BAND_ATTR_IFTYPE_DATA: u16 = 9; +const NL80211_BAND_ATTR_EDMG_CHANNELS: u16 = 10; +const NL80211_BAND_ATTR_EDMG_BW_CONFIG: u16 = 11; +// TODO: Kernel has no properly defined struct for 802.11ah sub-1G MCS and CAPA, +// postpone the deserialization. +// const NL80211_BAND_ATTR_S1G_MCS_NSS_SET: u16 = 12; +// const NL80211_BAND_ATTR_S1G_CAPA: u16 = 13; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211BandInfo { + /// Supported frequencies in this band. + Freqs(Vec), + /// Supported bitrates in this band. + Rates(Vec>), + /// The MCS set as defined in 802.11n(WIFI 4). + HtMcsSet(Nl80211HtMcsInfo), + /// HT capabilities, as in the HT information IE. + HtCapa(Nl80211HtCaps), + /// Maximum A-MPDU length factor, as in 802.11n(WIFI 4). + HtAmpduFactor(u8), + /// Minimum A-MPDU spacing, as in 802.11n(WIFI 4). + HtAmpduDensity(u8), + /// The MCS set as defined in 802.11ac(WIFI 5). + VhtMcsSet(Nl80211VhtMcsInfo), + /// VHT capabilities, as in the HT information IE + VhtCap(Nl80211VhtCapInfo), + /// Interface type data + IftypeData(Vec), + /// Bitmap that indicates the 2.16 GHz channel(s) that are allowed to be + /// used for EDMG transmissions. Defined by IEEE P802.11ay/D4.0 section + /// 9.4.2.251. + EdmgChannels(u8), + /// Channel BW Configuration subfield encodes the allowed channel bandwidth + /// configurations. + EdmgBwConfig(u8), + Other(DefaultNla), +} + +impl Nla for Nl80211BandInfo { + fn value_len(&self) -> usize { + match self { + Self::Freqs(s) => s.as_slice().buffer_len(), + Self::Rates(s) => { + Nl80211RateAttrsList::from(s).as_slice().buffer_len() + } + Self::HtMcsSet(_) => Nl80211HtMcsInfo::LENGTH, + Self::HtCapa(_) => 2, + Self::HtAmpduFactor(_) => 1, + Self::HtAmpduDensity(_) => 1, + Self::VhtMcsSet(_) => Nl80211VhtMcsInfo::LENGTH, + Self::VhtCap(_) => Nl80211VhtCapInfo::LENGTH, + Self::IftypeData(s) => s.as_slice().buffer_len(), + Self::EdmgChannels(_) => 1, + Self::EdmgBwConfig(_) => 1, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Freqs(_) => NL80211_BAND_ATTR_FREQS, + Self::Rates(_) => NL80211_BAND_ATTR_RATES, + Self::HtMcsSet(_) => NL80211_BAND_ATTR_HT_MCS_SET, + Self::HtCapa(_) => NL80211_BAND_ATTR_HT_CAPA, + Self::HtAmpduFactor(_) => NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + Self::HtAmpduDensity(_) => NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + Self::VhtMcsSet(_) => NL80211_BAND_ATTR_VHT_MCS_SET, + Self::VhtCap(_) => NL80211_BAND_ATTR_VHT_CAPA, + Self::IftypeData(_) => NL80211_BAND_ATTR_IFTYPE_DATA, + Self::EdmgChannels(_) => NL80211_BAND_ATTR_EDMG_CHANNELS, + Self::EdmgBwConfig(_) => NL80211_BAND_ATTR_EDMG_BW_CONFIG, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Freqs(d) => d.as_slice().emit(buffer), + Self::Rates(s) => { + Nl80211RateAttrsList::from(s).as_slice().emit(buffer) + } + Self::HtMcsSet(d) => d.emit(buffer), + Self::HtCapa(d) => buffer.copy_from_slice(&d.bits().to_ne_bytes()), + Self::HtAmpduFactor(d) => buffer[0] = *d, + Self::HtAmpduDensity(d) => buffer[0] = *d, + Self::VhtMcsSet(d) => d.emit(buffer), + Self::VhtCap(d) => d.emit(buffer), + Self::IftypeData(d) => d.as_slice().emit(buffer), + Self::EdmgChannels(d) => buffer[0] = *d, + Self::EdmgBwConfig(d) => buffer[0] = *d, + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211BandInfo +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_BAND_ATTR_FREQS => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_FREQS value {:?}", + payload + ); + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211Frequency::parse(nla)?); + } + Self::Freqs(nlas) + } + NL80211_BAND_ATTR_RATES => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_RATES value {:?}", + payload + ); + let mut nlas = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let nla = &nla.context(err_msg.clone())?; + nlas.push( + Nl80211RateAttrs::parse_with_param(nla, index as u16) + .context(err_msg.clone())? + .attributes, + ); + } + Self::Rates(nlas) + } + NL80211_BAND_ATTR_HT_MCS_SET => { + Self::HtMcsSet(Nl80211HtMcsInfo::parse(payload)?) + } + NL80211_BAND_ATTR_HT_CAPA => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_HT_CAPA value {:?}", + payload + ); + Self::HtCapa(Nl80211HtCaps::from_bits_retain( + parse_u16(payload).context(err_msg)?, + )) + } + NL80211_BAND_ATTR_HT_AMPDU_FACTOR => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_HT_AMPDU_FACTOR value {:?}", + payload + ); + Self::HtAmpduFactor(parse_u8(payload).context(err_msg)?) + } + NL80211_BAND_ATTR_HT_AMPDU_DENSITY => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_HT_AMPDU_DENSITY value {:?}", + payload + ); + Self::HtAmpduDensity(parse_u8(payload).context(err_msg)?) + } + NL80211_BAND_ATTR_VHT_MCS_SET => { + Self::VhtMcsSet(Nl80211VhtMcsInfo::parse(payload)?) + } + NL80211_BAND_ATTR_VHT_CAPA => { + Self::VhtCap(Nl80211VhtCapInfo::parse(payload)?) + } + NL80211_BAND_ATTR_IFTYPE_DATA => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_IFTYPE_DATA value {:?}", + payload + ); + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + nlas.push( + Nl80211BandIftypeData::parse(nla) + .context(err_msg.clone())?, + ); + } + Self::IftypeData(nlas) + } + NL80211_BAND_ATTR_EDMG_CHANNELS => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_EDMG_CHANNELS value {:?}", + payload + ); + Self::EdmgChannels(parse_u8(payload).context(err_msg)?) + } + NL80211_BAND_ATTR_EDMG_BW_CONFIG => { + let err_msg = format!( + "Invalid NL80211_BAND_ATTR_EDMG_BW_CONFIG value {:?}", + payload + ); + Self::EdmgBwConfig(parse_u8(payload).context(err_msg)?) + } + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +const NL80211_BAND_IFTYPE_ATTR_IFTYPES: u16 = 1; +const NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: u16 = 2; +const NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: u16 = 3; +const NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: u16 = 4; +const NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: u16 = 5; +const NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: u16 = 6; +const NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: u16 = 7; +const NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: u16 = 8; +const NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: u16 = 9; +const NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: u16 = 10; +const NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: u16 = 11; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum Nl80211BandIftypeData { + IfTypes(Vec), + HeCapMac(Nl80211HeMacCapInfo), + HeCapPhy(Nl80211HePhyCapInfo), + HeCapMcsSet(Nl80211HeMcsNssSupp), + HeCapPpeThreshold(Nl80211HePpeThreshold), + He6ghzCapa(Nl80211He6GhzCapa), + /// Vendor specific data + VendorElems(Vec), + EhtCapMac(Nl80211EhtMacCapInfo), + EhtCapPhy(Nl80211EhtPhyCapInfo), + EhtCapMcsSet(Nl80211EhtMcsNssSupp), + EhtCapPpe(Nl80211EhtPpeThres), + Other(DefaultNla), +} + +impl Nla for Nl80211BandIftypeData { + fn value_len(&self) -> usize { + match self { + Self::IfTypes(s) => Nl80211IfTypeList(s.clone()).value_len(), + Self::HeCapMac(s) => s.buffer_len(), + Self::HeCapPhy(s) => s.buffer_len(), + Self::HeCapMcsSet(s) => s.buffer_len(), + Self::HeCapPpeThreshold(s) => s.buffer_len(), + Self::He6ghzCapa(s) => s.buffer_len(), + Self::VendorElems(s) => s.len(), + Self::EhtCapMac(s) => s.buffer_len(), + Self::EhtCapPhy(s) => s.buffer_len(), + Self::EhtCapMcsSet(s) => s.buffer_len(), + Self::EhtCapPpe(s) => s.buffer_len(), + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::IfTypes(_) => NL80211_BAND_IFTYPE_ATTR_IFTYPES, + Self::HeCapMac(_) => NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, + Self::HeCapPhy(_) => NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, + Self::HeCapMcsSet(_) => NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, + Self::HeCapPpeThreshold(_) => NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, + Self::He6ghzCapa(_) => NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, + Self::VendorElems(_) => NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, + Self::EhtCapMac(_) => NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, + Self::EhtCapPhy(_) => NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, + Self::EhtCapMcsSet(_) => NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, + Self::EhtCapPpe(_) => NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::IfTypes(d) => { + Nl80211IfTypeList(d.clone()).0.as_slice().emit(buffer) + } + Self::HeCapMac(d) => d.emit(buffer), + Self::HeCapPhy(d) => d.emit(buffer), + Self::HeCapMcsSet(d) => d.emit(buffer), + Self::HeCapPpeThreshold(d) => d.emit(buffer), + Self::He6ghzCapa(d) => d.emit(buffer), + Self::VendorElems(d) => buffer[..d.len()].copy_from_slice(d), + Self::EhtCapMac(d) => d.emit(buffer), + Self::EhtCapPhy(d) => d.emit(buffer), + Self::EhtCapMcsSet(d) => d.emit(buffer), + Self::EhtCapPpe(d) => d.emit(buffer), + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211BandIftypeData +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_BAND_IFTYPE_ATTR_IFTYPES => Self::IfTypes( + Nl80211IfTypeList::parse(buf) + .context( + "Invalid NLA for NL80211_BAND_IFTYPE_ATTR_IFTYPES", + )? + .0, + ), + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC => { + if payload.len() < Nl80211HeMacCapInfo::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC value \ + length is less than expected {}: {:?}", + Nl80211HeMacCapInfo::LENGTH, + payload + ) + .into()); + } + Self::HeCapMac(Nl80211HeMacCapInfo::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY => { + if payload.len() < Nl80211HePhyCapInfo::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY value \ + length is less than expected {}: {:?}", + Nl80211HePhyCapInfo::LENGTH, + payload + ) + .into()); + } + Self::HeCapPhy(Nl80211HePhyCapInfo::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET => { + if payload.len() < Nl80211HeMcsNssSupp::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET value \ + length is less than expected {}: {:?}", + Nl80211HeMcsNssSupp::LENGTH, + payload + ) + .into()); + } + Self::HeCapMcsSet(Nl80211HeMcsNssSupp::parse(payload)?) + } + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE => { + if payload.len() < Nl80211HePpeThreshold::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE value \ + length is less than expected {}: {:?}", + Nl80211HePpeThreshold::LENGTH, + payload + ) + .into()); + } + Self::HeCapPpeThreshold(Nl80211HePpeThreshold::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA => { + if payload.len() < Nl80211He6GhzCapa::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA value \ + length is less than expected {}: {:?}", + Nl80211He6GhzCapa::LENGTH, + payload + ) + .into()); + } + Self::He6ghzCapa(Nl80211He6GhzCapa::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS => { + Self::VendorElems(payload.to_vec()) + } + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC => { + if payload.len() < Nl80211EhtMacCapInfo::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC value \ + length is less than expected {}: {:?}", + Nl80211EhtMacCapInfo::LENGTH, + payload + ) + .into()); + } + Self::EhtCapMac(Nl80211EhtMacCapInfo::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY => { + if payload.len() < Nl80211EhtPhyCapInfo::LENGTH { + return Err(format!( + "NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY value \ + length is less than expected {}: {:?}", + Nl80211EhtPhyCapInfo::LENGTH, + payload + ) + .into()); + } + Self::EhtCapPhy(Nl80211EhtPhyCapInfo::new(payload)) + } + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET => { + Self::EhtCapMcsSet(Nl80211EhtMcsNssSupp::parse(payload)?) + } + NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE => { + Self::EhtCapPpe(Nl80211EhtPpeThres::new(payload)) + } + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +const NL80211_IFTYPE_ADHOC: u16 = 1; +const NL80211_IFTYPE_STATION: u16 = 2; +const NL80211_IFTYPE_AP: u16 = 3; +const NL80211_IFTYPE_AP_VLAN: u16 = 4; +const NL80211_IFTYPE_WDS: u16 = 5; +const NL80211_IFTYPE_MONITOR: u16 = 6; +const NL80211_IFTYPE_MESH_POINT: u16 = 7; +const NL80211_IFTYPE_P2P_CLIENT: u16 = 8; +const NL80211_IFTYPE_P2P_GO: u16 = 9; +const NL80211_IFTYPE_P2P_DEVICE: u16 = 10; +const NL80211_IFTYPE_OCB: u16 = 11; +const NL80211_IFTYPE_NAN: u16 = 12; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211IfType { + Adhoc, + /// aka: managed or client + Station, + Ap, + ApVlan, + Wds, + Monitor, + MeshPoint, + P2pClient, + P2pGo, + P2pDevice, + Ocb, + Nan, + Other(u16), +} + +impl From for Nl80211IfType { + fn from(d: u16) -> Self { + match d { + NL80211_IFTYPE_ADHOC => Self::Adhoc, + NL80211_IFTYPE_STATION => Self::Station, + NL80211_IFTYPE_AP => Self::Ap, + NL80211_IFTYPE_AP_VLAN => Self::ApVlan, + NL80211_IFTYPE_WDS => Self::Wds, + NL80211_IFTYPE_MONITOR => Self::Monitor, + NL80211_IFTYPE_MESH_POINT => Self::MeshPoint, + NL80211_IFTYPE_P2P_CLIENT => Self::P2pClient, + NL80211_IFTYPE_P2P_GO => Self::P2pGo, + NL80211_IFTYPE_P2P_DEVICE => Self::P2pDevice, + NL80211_IFTYPE_OCB => Self::Ocb, + NL80211_IFTYPE_NAN => Self::Nan, + _ => Self::Other(d), + } + } +} + +impl From<&Nl80211IfType> for u16 { + fn from(v: &Nl80211IfType) -> Self { + match v { + Nl80211IfType::Adhoc => NL80211_IFTYPE_ADHOC, + Nl80211IfType::Station => NL80211_IFTYPE_STATION, + Nl80211IfType::Ap => NL80211_IFTYPE_AP, + Nl80211IfType::ApVlan => NL80211_IFTYPE_AP_VLAN, + Nl80211IfType::Wds => NL80211_IFTYPE_WDS, + Nl80211IfType::Monitor => NL80211_IFTYPE_MONITOR, + Nl80211IfType::MeshPoint => NL80211_IFTYPE_MESH_POINT, + Nl80211IfType::P2pClient => NL80211_IFTYPE_P2P_CLIENT, + Nl80211IfType::P2pGo => NL80211_IFTYPE_P2P_GO, + Nl80211IfType::P2pDevice => NL80211_IFTYPE_P2P_DEVICE, + Nl80211IfType::Ocb => NL80211_IFTYPE_OCB, + Nl80211IfType::Nan => NL80211_IFTYPE_NAN, + Nl80211IfType::Other(d) => *d, + } + } +} + +// The kernel function `nl80211_put_iftypes()` is using mode number as NLA kind +struct Nl80211IfTypeList(Vec); + +impl std::ops::Deref for Nl80211IfTypeList { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Nla for Nl80211IfType { + fn value_len(&self) -> usize { + 0 + } + + fn emit_value(&self, _buffer: &mut [u8]) {} + + fn kind(&self) -> u16 { + self.into() + } +} + +impl Nla for Nl80211IfTypeList { + fn value_len(&self) -> usize { + self.0.as_slice().buffer_len() + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.0.as_slice().emit(buffer) + } + + fn kind(&self) -> u16 { + NL80211_BAND_IFTYPE_ATTR_IFTYPES + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfTypeList +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + let mut if_types: Vec = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = + &nla.context("invalid NL80211_BAND_IFTYPE_ATTR_IFTYPES value")?; + if_types.push(nla.kind().into()); + } + Ok(Self(if_types)) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211Frequency { + pub index: u16, + pub info: Vec, +} + +impl Nla for Nl80211Frequency { + fn value_len(&self) -> usize { + self.info.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.index + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.info.as_slice().emit(buffer) + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211Frequency +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let index = buf.kind(); + let payload = buf.value(); + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = + format!("Invalid NL80211_BAND_ATTR_FREQS value {:?}", nla); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211FrequencyInfo::parse(nla)?); + } + Ok(Self { index, info: nlas }) + } +} + +const NL80211_FREQUENCY_ATTR_FREQ: u16 = 1; +const NL80211_FREQUENCY_ATTR_DISABLED: u16 = 2; +const NL80211_FREQUENCY_ATTR_NO_IR: u16 = 3; +// Obsoleted, same as NL80211_FREQUENCY_ATTR_NO_IR +const __NL80211_FREQUENCY_ATTR_NO_IBSS: u16 = 4; +const NL80211_FREQUENCY_ATTR_RADAR: u16 = 5; +const NL80211_FREQUENCY_ATTR_MAX_TX_POWER: u16 = 6; +const NL80211_FREQUENCY_ATTR_DFS_STATE: u16 = 7; +const NL80211_FREQUENCY_ATTR_DFS_TIME: u16 = 8; +const NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: u16 = 9; +const NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: u16 = 10; +const NL80211_FREQUENCY_ATTR_NO_80MHZ: u16 = 11; +const NL80211_FREQUENCY_ATTR_NO_160MHZ: u16 = 12; +const NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: u16 = 13; +const NL80211_FREQUENCY_ATTR_INDOOR_ONLY: u16 = 14; +const NL80211_FREQUENCY_ATTR_IR_CONCURRENT: u16 = 15; +const NL80211_FREQUENCY_ATTR_NO_20MHZ: u16 = 16; +const NL80211_FREQUENCY_ATTR_NO_10MHZ: u16 = 17; +const NL80211_FREQUENCY_ATTR_WMM: u16 = 18; +const NL80211_FREQUENCY_ATTR_NO_HE: u16 = 19; +const NL80211_FREQUENCY_ATTR_OFFSET: u16 = 20; +const NL80211_FREQUENCY_ATTR_1MHZ: u16 = 21; +const NL80211_FREQUENCY_ATTR_2MHZ: u16 = 22; +const NL80211_FREQUENCY_ATTR_4MHZ: u16 = 23; +const NL80211_FREQUENCY_ATTR_8MHZ: u16 = 24; +const NL80211_FREQUENCY_ATTR_16MHZ: u16 = 25; +const NL80211_FREQUENCY_ATTR_NO_320MHZ: u16 = 26; +const NL80211_FREQUENCY_ATTR_NO_EHT: u16 = 27; +const NL80211_FREQUENCY_ATTR_PSD: u16 = 28; +const NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: u16 = 29; +const NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: u16 = 30; +const NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: u16 = 31; +const NL80211_FREQUENCY_ATTR_CAN_MONITOR: u16 = 32; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211FrequencyInfo { + /// Frequency in MHz + Freq(u32), + /// Channel is disabled in current regulatory domain + Disabled, + /// No mechanisms that initiate radiation are permitted on this channel, + /// this includes sending probe requests, or modes of operation that + /// require beaconing. + NoIr, + /// Obsoleted, same as [Nl80211FrequencyInfo::NoIr] + NoIbss, + /// Radar detection is mandatory on this channel in current regulatory + /// domain. + Radar, + /// Maximum transmission power in mBm (100 * dBm) + MaxTxPower(u32), + /// Current state for DFS + DfsState(Nl80211DfsState), + /// time in milliseconds for how long this channel is in this DFS state + DfsTime(u32), + /// HT40- isn't possible with this channel as the control channel + NoHt40Minus, + /// HT40+ isn't possible with this channel as the control channel + NoHt40Plus, + /// Any 80 MHz channel using this channel as the primary or any of the + /// secondary channels isn't possible, this includes 80+80 channels + No80Mhz, + /// Any 160 MHz (but not 80+80) channel using this channel as the primary + /// or any of the secondary channels isn't possible + No160Mhz, + /// DFS CAC time in milliseconds. + DfsCacTime(u32), + /// Only indoor use is permitted on this channel. A channel that has the + /// INDOOR_ONLY attribute can only be used when there is a clear assessment + /// that the device is operating in an indoor surroundings, i.e., it is + /// connected to AC power (and not through portable DC inverters) or is + /// under the control of a master that is acting as an AP and is connected + /// to AC power. + IndoorOnly, + /// IR operation is allowed on this channel if it's connected concurrently + /// to a BSS on the same channel on the 2 GHz band or to a channel in + /// the same UNII band (on the 5 GHz band), and IEEE80211_CHAN_RADAR is + /// not set. Instantiating a GO or TDLS off-channel on a channel that + /// has the IR_CONCURRENT attribute set can be done when there is a + /// clear assessment that the device is operating under the guidance of + /// an authorized master, i.e., setting up a GO or TDLS off-channel + /// while the device is also connected to an AP with DFS and radar + /// detection on the UNII band (it is up to user-space, i.e., + /// wpa_supplicant to perform the required verifications). Using this + /// attribute for IR is disallowed for master interfaces (IBSS, AP). + IrConcurrent, + /// 20 MHz operation is not allowed on this channel in current regulatory + /// domain. + No20Mhz, + /// 10 MHz operation is not allowed on this channel in current regulatory + /// domain. + No10Mhz, + /// this channel has WMM limitations. + Wmm(Vec>), + /// HE operation is not allowed on this channel in current regulatory + /// domain. + NoHe, + /// frequency offset in KHz + Offset(u32), + /// 1 MHz operation is allowed + Allow1Mhz, + /// 2 MHz operation is allowed + Allow2Mhz, + /// 4 MHz operation is allowed + Allow4Mhz, + /// 8 MHz operation is allowed + Allow8Mhz, + /// 16 MHz operation is allowed + Allow16Mhz, + /// any 320 MHz channel using this channel + /// as the primary or any of the secondary channels isn't possible + No320Mhz, + /// EHT operation is not allowed on this channel in current regulatory + /// domain. + NoEht, + /// Power spectral density (in dBm) that is allowed on this channel in + /// current regulatory domain. + Psd(i8), + /// Operation on this channel is allowed for peer-to-peer or adhoc + /// communication under the control of a DFS master which operates on the + /// same channel (FCC-594280 D01 Section B.3). Should be used together with + /// `NL80211_RRF_DFS` only. + DfsConcurrent, + /// Client connection to VLP AP not allowed using this channel + No6GhzVlpClient, + /// Client connection to AFC AP not allowed using this channel + No6GhzAfcclient, + /// This channel can be used in monitor mode despite other (regulatory) + /// restrictions, even if the channel is otherwise completely disabled. + CanMonitor, + /// Place holder for new attribute of `NL80211_BAND_ATTR_FREQS` + Other(DefaultNla), +} + +impl Nla for Nl80211FrequencyInfo { + fn value_len(&self) -> usize { + match self { + Self::Freq(_) => 4, + Self::Disabled => 0, + Self::NoIr => 0, + Self::Radar => 0, + Self::NoIbss => 0, + Self::MaxTxPower(_) => 4, + Self::DfsState(_) => 4, + Self::DfsTime(_) => 4, + Self::NoHt40Minus => 0, + Self::NoHt40Plus => 0, + Self::No80Mhz => 0, + Self::No160Mhz => 0, + Self::DfsCacTime(_) => 4, + Self::IndoorOnly => 0, + Self::IrConcurrent => 0, + Self::No20Mhz => 0, + Self::No10Mhz => 0, + Self::Wmm(ref v) => { + Nl80211WmmRuleAttrsList::from(v).as_slice().buffer_len() + } + Self::NoHe => 0, + Self::Offset(_) => 4, + Self::Allow1Mhz => 0, + Self::Allow2Mhz => 0, + Self::Allow4Mhz => 0, + Self::Allow8Mhz => 0, + Self::Allow16Mhz => 0, + Self::No320Mhz => 0, + Self::NoEht => 0, + Self::Psd(_) => 1, + Self::DfsConcurrent => 0, + Self::No6GhzVlpClient => 0, + Self::No6GhzAfcclient => 0, + Self::CanMonitor => 0, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Freq(_) => NL80211_FREQUENCY_ATTR_FREQ, + Self::Disabled => NL80211_FREQUENCY_ATTR_DISABLED, + Self::NoIr => NL80211_FREQUENCY_ATTR_NO_IR, + Self::NoIbss => __NL80211_FREQUENCY_ATTR_NO_IBSS, + Self::Radar => NL80211_FREQUENCY_ATTR_RADAR, + Self::MaxTxPower(_) => NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + Self::DfsState(_) => NL80211_FREQUENCY_ATTR_DFS_STATE, + Self::DfsTime(_) => NL80211_FREQUENCY_ATTR_DFS_TIME, + Self::NoHt40Minus => NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + Self::NoHt40Plus => NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + Self::No80Mhz => NL80211_FREQUENCY_ATTR_NO_80MHZ, + Self::No160Mhz => NL80211_FREQUENCY_ATTR_NO_160MHZ, + Self::DfsCacTime(_) => NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + Self::IndoorOnly => NL80211_FREQUENCY_ATTR_INDOOR_ONLY, + Self::IrConcurrent => NL80211_FREQUENCY_ATTR_IR_CONCURRENT, + Self::No20Mhz => NL80211_FREQUENCY_ATTR_NO_20MHZ, + Self::No10Mhz => NL80211_FREQUENCY_ATTR_NO_10MHZ, + Self::Wmm(_) => NL80211_FREQUENCY_ATTR_WMM, + Self::NoHe => NL80211_FREQUENCY_ATTR_NO_HE, + Self::Offset(_) => NL80211_FREQUENCY_ATTR_OFFSET, + Self::Allow1Mhz => NL80211_FREQUENCY_ATTR_1MHZ, + Self::Allow2Mhz => NL80211_FREQUENCY_ATTR_2MHZ, + Self::Allow4Mhz => NL80211_FREQUENCY_ATTR_4MHZ, + Self::Allow8Mhz => NL80211_FREQUENCY_ATTR_8MHZ, + Self::Allow16Mhz => NL80211_FREQUENCY_ATTR_16MHZ, + Self::No320Mhz => NL80211_FREQUENCY_ATTR_NO_320MHZ, + Self::NoEht => NL80211_FREQUENCY_ATTR_NO_EHT, + Self::Psd(_) => NL80211_FREQUENCY_ATTR_PSD, + Self::DfsConcurrent => NL80211_FREQUENCY_ATTR_DFS_CONCURRENT, + Self::No6GhzVlpClient => NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, + Self::No6GhzAfcclient => NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, + Self::CanMonitor => NL80211_FREQUENCY_ATTR_CAN_MONITOR, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Freq(d) + | Self::MaxTxPower(d) + | Self::DfsTime(d) + | Self::DfsCacTime(d) => write_u32(buffer, *d), + Self::DfsState(d) => write_u32(buffer, u32::from(d)), + Self::Disabled + | Self::NoIr + | Self::NoIbss + | Self::Radar + | Self::NoHt40Minus + | Self::NoHt40Plus + | Self::No80Mhz + | Self::No160Mhz + | Self::IndoorOnly + | Self::IrConcurrent + | Self::No20Mhz + | Self::No10Mhz + | Self::NoHe + | Self::Allow1Mhz + | Self::Allow2Mhz + | Self::Allow4Mhz + | Self::Allow8Mhz + | Self::Allow16Mhz + | Self::No320Mhz + | Self::NoEht + | Self::DfsConcurrent + | Self::No6GhzVlpClient + | Self::No6GhzAfcclient + | Self::CanMonitor => (), + Self::Psd(d) => buffer[0] = *d as u8, + Self::Offset(d) => write_u32(buffer, *d), + Self::Wmm(ref v) => { + Nl80211WmmRuleAttrsList::from(v).as_slice().emit(buffer) + } + Self::Other(ref attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211FrequencyInfo +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_FREQUENCY_ATTR_FREQ => { + Self::Freq(parse_u32(payload).context(format!( + "Invalid NL80211_FREQUENCY_ATTR_FREQ value: {:?}", + payload + ))?) + } + NL80211_FREQUENCY_ATTR_DISABLED => Self::Disabled, + NL80211_FREQUENCY_ATTR_NO_IR => Self::NoIr, + __NL80211_FREQUENCY_ATTR_NO_IBSS => Self::NoIbss, + NL80211_FREQUENCY_ATTR_RADAR => Self::Radar, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER => { + Self::MaxTxPower(parse_u32(payload).context(format!( + "Invalid NL80211_FREQUENCY_ATTR_MAX_TX_POWER value: {:?}", + payload + ))?) + } + NL80211_FREQUENCY_ATTR_DFS_STATE => Self::DfsState( + parse_u32(payload) + .context(format!( + "Invalid NL80211_FREQUENCY_ATTR_MAX_TX_POWER value: {:?}", + payload + ))? + .into(), + ), + + NL80211_FREQUENCY_ATTR_DFS_TIME => { + Self::DfsTime(parse_u32(payload).context(format!( + "Invalid NL80211_FREQUENCY_ATTR_DFS_TIME value: {:?}", + payload + ))?) + } + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS => Self::NoHt40Minus, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS => Self::NoHt40Plus, + NL80211_FREQUENCY_ATTR_NO_80MHZ => Self::No80Mhz, + NL80211_FREQUENCY_ATTR_NO_160MHZ => Self::No160Mhz, + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME => { + Self::DfsCacTime(parse_u32(payload).context(format!( + "Invalid NL80211_FREQUENCY_ATTR_DFS_CAC_TIME value: {:?}", + payload + ))?) + } + NL80211_FREQUENCY_ATTR_INDOOR_ONLY => Self::IndoorOnly, + NL80211_FREQUENCY_ATTR_IR_CONCURRENT => Self::IrConcurrent, + NL80211_FREQUENCY_ATTR_NO_20MHZ => Self::No20Mhz, + NL80211_FREQUENCY_ATTR_NO_10MHZ => Self::No10Mhz, + NL80211_FREQUENCY_ATTR_WMM => { + let err_msg = format!( + "Invalid NL80211_FREQUENCY_ATTR_WMM value {:?}", + payload + ); + let mut nlas = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let nla = &nla.context(err_msg.clone())?; + nlas.push( + Nl80211WmmRuleAttrs::parse_with_param( + nla, + index as u16, + ) + .context(err_msg.clone())? + .attributes, + ); + } + Self::Wmm(nlas) + } + NL80211_FREQUENCY_ATTR_NO_HE => Self::NoHe, + NL80211_FREQUENCY_ATTR_OFFSET => { + Self::Offset(parse_u32(payload).context(format!( + "Invalid NL80211_FREQUENCY_ATTR_OFFSET value {:?}", + payload + ))?) + } + NL80211_FREQUENCY_ATTR_1MHZ => Self::Allow1Mhz, + NL80211_FREQUENCY_ATTR_2MHZ => Self::Allow2Mhz, + NL80211_FREQUENCY_ATTR_4MHZ => Self::Allow4Mhz, + NL80211_FREQUENCY_ATTR_8MHZ => Self::Allow8Mhz, + NL80211_FREQUENCY_ATTR_16MHZ => Self::Allow16Mhz, + NL80211_FREQUENCY_ATTR_NO_320MHZ => Self::No320Mhz, + NL80211_FREQUENCY_ATTR_NO_EHT => Self::NoEht, + NL80211_FREQUENCY_ATTR_PSD => { + if payload.is_empty() { + return Err( + "Got empty NL80211_FREQUENCY_ATTR_PSD payload".into() + ); + } + Self::Psd(payload[0] as i8) + } + NL80211_FREQUENCY_ATTR_DFS_CONCURRENT => Self::DfsConcurrent, + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT => Self::No6GhzVlpClient, + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT => Self::No6GhzAfcclient, + NL80211_FREQUENCY_ATTR_CAN_MONITOR => Self::CanMonitor, + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct Nl80211RateAttrsList(Vec); + +impl std::ops::Deref for Nl80211RateAttrsList { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&Vec>> for Nl80211RateAttrsList { + fn from(attributes: &Vec>) -> Self { + Self( + attributes + .iter() + .cloned() + .enumerate() + .map(|(index, attributes)| Nl80211RateAttrs { + index: index as u16, + attributes, + }) + .collect(), + ) + } +} + +// `NL80211_BAND_ATTR_RATES` is a two levels array. +// The second level is using index as NLA kind. +#[derive(Debug, PartialEq, Eq, Clone)] +struct Nl80211RateAttrs { + index: u16, + attributes: Vec, +} + +impl Nla for Nl80211RateAttrs { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.index + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer); + } +} + +impl<'a, T> ParseableParametrized, u16> for Nl80211RateAttrs +where + T: AsRef<[u8]> + ?Sized, +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + index: u16, + ) -> Result { + let payload = buf.value(); + let err_msg = + format!("Invalid NL80211_BAND_ATTR_RATES value {:?}", payload); + let mut attributes = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + attributes.push(Nl80211Rate::parse(nla)?); + } + Ok(Self { index, attributes }) + } +} + +const NL80211_BITRATE_ATTR_RATE: u16 = 1; +const NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: u16 = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211Rate { + /// Bitrate in units of 100 kbps. + Rate(u32), + /// Short preamble supported in 2.4 GHz band. + Support2GhzShortpreamble, + Other(DefaultNla), +} + +impl Nla for Nl80211Rate { + fn value_len(&self) -> usize { + match self { + Self::Rate(_) => 4, + Self::Support2GhzShortpreamble => 0, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Rate(_) => NL80211_BITRATE_ATTR_RATE, + Self::Support2GhzShortpreamble => { + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE + } + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Rate(d) => write_u32(buffer, *d), + Self::Support2GhzShortpreamble => (), + Self::Other(ref attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nl80211Rate { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_BITRATE_ATTR_RATE => { + Self::Rate(parse_u32(payload).context(format!( + "Invalid NL80211_BITRATE_ATTR_RATE value {:?}", + payload + ))?) + } + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE => { + Self::Support2GhzShortpreamble + } + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +const NL80211_DFS_USABLE: u32 = 0; +const NL80211_DFS_UNAVAILABLE: u32 = 1; +const NL80211_DFS_AVAILABLE: u32 = 2; + +/// DFS states for channels +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211DfsState { + /// The channel can be used, but channel availability check (CAC) must be + /// performed before using it for AP or IBSS. + Usable, + /// A radar has been detected on this channel, it is therefore marked as + /// not available. + Unavailable, + /// The channel has been CAC checked and is available. + Available, + /// Place holder for new state + Other(u32), +} + +impl From for Nl80211DfsState { + fn from(d: u32) -> Self { + match d { + NL80211_DFS_USABLE => Self::Usable, + NL80211_DFS_UNAVAILABLE => Self::Unavailable, + NL80211_DFS_AVAILABLE => Self::Available, + _ => Self::Other(d), + } + } +} + +impl From<&Nl80211DfsState> for u32 { + fn from(v: &Nl80211DfsState) -> Self { + match v { + Nl80211DfsState::Usable => NL80211_DFS_USABLE, + Nl80211DfsState::Unavailable => NL80211_DFS_UNAVAILABLE, + Nl80211DfsState::Available => NL80211_DFS_AVAILABLE, + Nl80211DfsState::Other(d) => *d, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct Nl80211WmmRuleAttrsList(Vec); + +impl std::ops::Deref for Nl80211WmmRuleAttrsList { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&Vec>> for Nl80211WmmRuleAttrsList { + fn from(attributes: &Vec>) -> Self { + Self( + attributes + .iter() + .cloned() + .enumerate() + .map(|(index, attributes)| Nl80211WmmRuleAttrs { + index: index as u16, + attributes, + }) + .collect(), + ) + } +} + +// `NL80211_FREQUENCY_ATTR_WMM` is a two levels array. +// The second level is using index as NLA kind. +#[derive(Debug, PartialEq, Eq, Clone)] +struct Nl80211WmmRuleAttrs { + index: u16, + attributes: Vec, +} + +impl Nla for Nl80211WmmRuleAttrs { + fn value_len(&self) -> usize { + self.attributes.as_slice().buffer_len() + } + + fn kind(&self) -> u16 { + self.index + } + + fn emit_value(&self, buffer: &mut [u8]) { + self.attributes.as_slice().emit(buffer); + } +} + +impl<'a, T> ParseableParametrized, u16> for Nl80211WmmRuleAttrs +where + T: AsRef<[u8]> + ?Sized, +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + index: u16, + ) -> Result { + let payload = buf.value(); + let err_msg = + format!("Invalid NL80211_FREQUENCY_ATTR_WMM value {:?}", payload); + let mut attributes = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err_msg.clone())?; + attributes.push(Nl80211WmmRule::parse(nla)?); + } + Ok(Self { index, attributes }) + } +} + +const NL80211_WMMR_CW_MIN: u16 = 1; +const NL80211_WMMR_CW_MAX: u16 = 2; +const NL80211_WMMR_AIFSN: u16 = 3; +const NL80211_WMMR_TXOP: u16 = 4; + +/// DFS states for channels +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211WmmRule { + /// Minimum contention window slot + CwMin(u16), + /// Maximum contention window slot + CwMax(u16), + /// Arbitration Inter Frame Space + Aifsn(u8), + /// Maximum allowed tx operation time + Txop(u16), + /// Place holder for new entry of `enum nl80211_wmm_rule` + Other(DefaultNla), +} + +impl Nla for Nl80211WmmRule { + fn value_len(&self) -> usize { + match self { + Self::CwMin(_) | Self::CwMax(_) | Self::Txop(_) => 2, + Self::Aifsn(_) => 1, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::CwMin(_) => NL80211_WMMR_CW_MIN, + Self::CwMax(_) => NL80211_WMMR_CW_MAX, + Self::Aifsn(_) => NL80211_WMMR_AIFSN, + Self::Txop(_) => NL80211_WMMR_TXOP, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::CwMin(d) | Self::CwMax(d) | Self::Txop(d) => { + write_u16(buffer, *d) + } + Self::Aifsn(d) => buffer[0] = *d, + Self::Other(ref attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211WmmRule +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_WMMR_CW_MIN => Self::CwMin(parse_u16(payload).context( + format!("Invalid NL80211_WMMR_CW_MIN value {:?}", payload), + )?), + NL80211_WMMR_CW_MAX => Self::CwMax(parse_u16(payload).context( + format!("Invalid NL80211_WMMR_CW_MAX value {:?}", payload), + )?), + NL80211_WMMR_AIFSN => Self::Aifsn(parse_u8(payload).context( + format!("Invalid NL80211_WMMR_AIFSN value {:?}", payload), + )?), + NL80211_WMMR_TXOP => Self::Txop(parse_u16(payload).context( + format!("Invalid NL80211_WMMR_CW_MAX value {:?}", payload), + )?), + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} diff --git a/src/wiphy/cipher.rs b/src/wiphy/cipher.rs new file mode 100644 index 0000000..11ee9a8 --- /dev/null +++ b/src/wiphy/cipher.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT + +const WLAN_CIPHER_SUITE_USE_GROUP: u32 = 0x000FAC << 8; +const WLAN_CIPHER_SUITE_WEP40: u32 = 0x000FAC << 8 | 1; +const WLAN_CIPHER_SUITE_TKIP: u32 = 0x000FAC << 8 | 2; +const WLAN_CIPHER_SUITE_CCMP: u32 = 0x000FAC << 8 | 4; +const WLAN_CIPHER_SUITE_WEP104: u32 = 0x000FAC << 8 | 5; +const WLAN_CIPHER_SUITE_AES_CMAC: u32 = 0x000FAC << 8 | 6; +const WLAN_CIPHER_SUITE_GCMP: u32 = 0x000FAC << 8 | 8; +const WLAN_CIPHER_SUITE_GCMP_256: u32 = 0x000FAC << 8 | 9; +const WLAN_CIPHER_SUITE_CCMP_256: u32 = 0x000FAC << 8 | 10; +const WLAN_CIPHER_SUITE_BIP_GMAC_128: u32 = 0x000FAC << 8 | 11; +const WLAN_CIPHER_SUITE_BIP_GMAC_256: u32 = 0x000FAC << 8 | 12; +const WLAN_CIPHER_SUITE_BIP_CMAC_256: u32 = 0x000FAC << 8 | 13; +const WLAN_CIPHER_SUITE_SMS4: u32 = 0x001472 << 8 | 1; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211CipherSuit { + UseGroup, + Wep40, + Tkip, + Ccmp, + Wep104, + AesCmac, + Gcmp, + Gcmp256, + Ccmp256, + BipGmac128, + BipGmac256, + BipCmac256, + Sms4, + Other(u32), +} + +impl From for Nl80211CipherSuit { + fn from(d: u32) -> Self { + match d { + WLAN_CIPHER_SUITE_USE_GROUP => Self::UseGroup, + WLAN_CIPHER_SUITE_WEP40 => Self::Wep40, + WLAN_CIPHER_SUITE_TKIP => Self::Tkip, + WLAN_CIPHER_SUITE_CCMP => Self::Ccmp, + WLAN_CIPHER_SUITE_WEP104 => Self::Wep104, + WLAN_CIPHER_SUITE_AES_CMAC => Self::AesCmac, + WLAN_CIPHER_SUITE_GCMP => Self::Gcmp, + WLAN_CIPHER_SUITE_GCMP_256 => Self::Gcmp256, + WLAN_CIPHER_SUITE_CCMP_256 => Self::Ccmp256, + WLAN_CIPHER_SUITE_BIP_GMAC_128 => Self::BipGmac128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 => Self::BipGmac256, + WLAN_CIPHER_SUITE_BIP_CMAC_256 => Self::BipCmac256, + WLAN_CIPHER_SUITE_SMS4 => Self::Sms4, + _ => Self::Other(d), + } + } +} + +impl From for u32 { + fn from(v: Nl80211CipherSuit) -> u32 { + match v { + Nl80211CipherSuit::UseGroup => WLAN_CIPHER_SUITE_USE_GROUP, + Nl80211CipherSuit::Wep40 => WLAN_CIPHER_SUITE_WEP40, + Nl80211CipherSuit::Tkip => WLAN_CIPHER_SUITE_TKIP, + Nl80211CipherSuit::Ccmp => WLAN_CIPHER_SUITE_CCMP, + Nl80211CipherSuit::Wep104 => WLAN_CIPHER_SUITE_WEP104, + Nl80211CipherSuit::AesCmac => WLAN_CIPHER_SUITE_AES_CMAC, + Nl80211CipherSuit::Gcmp => WLAN_CIPHER_SUITE_GCMP, + Nl80211CipherSuit::Gcmp256 => WLAN_CIPHER_SUITE_GCMP_256, + Nl80211CipherSuit::Ccmp256 => WLAN_CIPHER_SUITE_CCMP_256, + Nl80211CipherSuit::BipGmac128 => WLAN_CIPHER_SUITE_BIP_GMAC_128, + Nl80211CipherSuit::BipGmac256 => WLAN_CIPHER_SUITE_BIP_GMAC_256, + Nl80211CipherSuit::BipCmac256 => WLAN_CIPHER_SUITE_BIP_CMAC_256, + Nl80211CipherSuit::Sms4 => WLAN_CIPHER_SUITE_SMS4, + Nl80211CipherSuit::Other(d) => d, + } + } +} diff --git a/src/wiphy/command.rs b/src/wiphy/command.rs new file mode 100644 index 0000000..2b64f92 --- /dev/null +++ b/src/wiphy/command.rs @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; + +use netlink_packet_utils::{ + nla::{Nla, NlasIterator}, + parsers::parse_u32, + DecodeError, +}; + +use crate::bytes::write_u32; + +pub(crate) struct Nl80211CommandNla { + index: u16, + cmd: Nl80211Command, +} + +impl Nla for Nl80211CommandNla { + fn value_len(&self) -> usize { + 4 + } + + fn emit_value(&self, buffer: &mut [u8]) { + write_u32(buffer, self.cmd.into()) + } + + fn kind(&self) -> u16 { + self.index + } +} + +// NL80211_ATTR_SUPPORTED_COMMANDS is using index as NLA kind. +pub(crate) struct Nl80211Commands(Vec); + +impl std::ops::Deref for Nl80211Commands { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&Vec> for Nl80211Commands { + fn from(cmds: &Vec) -> Self { + let mut nlas = Vec::new(); + for (i, cmd) in cmds.iter().enumerate() { + let nla = Nl80211CommandNla { + index: i as u16, + cmd: *cmd, + }; + nlas.push(nla); + } + Nl80211Commands(nlas) + } +} + +impl From for Vec { + fn from(cmds: Nl80211Commands) -> Self { + let mut cmds = cmds; + cmds.0.drain(..).map(|c| c.cmd).collect() + } +} + +impl Nl80211Commands { + pub fn parse(payload: &[u8]) -> Result { + let mut cmds: Vec = Vec::new(); + for (index, nla) in NlasIterator::new(payload).enumerate() { + let error_msg = + format!("Invalid NL80211_ATTR_SUPPORTED_COMMANDS: {nla:?}"); + let nla = &nla.context(error_msg.clone())?; + let cmd = Nl80211Command::from(parse_u32(nla.value()).context( + format!("Invalid NL80211_ATTR_SUPPORTED_COMMANDS: {nla:?}"), + )?); + cmds.push(Nl80211CommandNla { + index: index as u16, + cmd, + }); + } + Ok(Self(cmds)) + } +} + +const NL80211_CMD_GET_WIPHY: u32 = 1; +const NL80211_CMD_SET_WIPHY: u32 = 2; +const NL80211_CMD_NEW_WIPHY: u32 = 3; +const NL80211_CMD_DEL_WIPHY: u32 = 4; +const NL80211_CMD_GET_INTERFACE: u32 = 5; +const NL80211_CMD_SET_INTERFACE: u32 = 6; +const NL80211_CMD_NEW_INTERFACE: u32 = 7; +const NL80211_CMD_DEL_INTERFACE: u32 = 8; +const NL80211_CMD_GET_KEY: u32 = 9; +const NL80211_CMD_SET_KEY: u32 = 10; +const NL80211_CMD_NEW_KEY: u32 = 11; +const NL80211_CMD_DEL_KEY: u32 = 12; +const NL80211_CMD_GET_BEACON: u32 = 13; +const NL80211_CMD_SET_BEACON: u32 = 14; +const NL80211_CMD_START_AP: u32 = 15; +const NL80211_CMD_STOP_AP: u32 = 16; +const NL80211_CMD_GET_STATION: u32 = 17; +const NL80211_CMD_SET_STATION: u32 = 18; +const NL80211_CMD_NEW_STATION: u32 = 19; +const NL80211_CMD_DEL_STATION: u32 = 20; +const NL80211_CMD_GET_MPATH: u32 = 21; +const NL80211_CMD_SET_MPATH: u32 = 22; +const NL80211_CMD_NEW_MPATH: u32 = 23; +const NL80211_CMD_DEL_MPATH: u32 = 24; +const NL80211_CMD_SET_BSS: u32 = 25; +const NL80211_CMD_SET_REG: u32 = 26; +const NL80211_CMD_REQ_SET_REG: u32 = 27; +const NL80211_CMD_GET_MESH_CONFIG: u32 = 28; +const NL80211_CMD_SET_MESH_CONFIG: u32 = 29; +const NL80211_CMD_SET_MGMT_EXTRA_IE: u32 = 30; +const NL80211_CMD_GET_REG: u32 = 31; +const NL80211_CMD_GET_SCAN: u32 = 32; +const NL80211_CMD_TRIGGER_SCAN: u32 = 33; +const NL80211_CMD_NEW_SCAN_RESULTS: u32 = 34; +const NL80211_CMD_SCAN_ABORTED: u32 = 35; +const NL80211_CMD_REG_CHANGE: u32 = 36; +const NL80211_CMD_AUTHENTICATE: u32 = 37; +const NL80211_CMD_ASSOCIATE: u32 = 38; +const NL80211_CMD_DEAUTHENTICATE: u32 = 39; +const NL80211_CMD_DISASSOCIATE: u32 = 40; +const NL80211_CMD_MICHAEL_MIC_FAILURE: u32 = 41; +const NL80211_CMD_REG_BEACON_HINT: u32 = 42; +const NL80211_CMD_JOIN_IBSS: u32 = 43; +const NL80211_CMD_LEAVE_IBSS: u32 = 44; +const NL80211_CMD_TESTMODE: u32 = 45; +const NL80211_CMD_CONNECT: u32 = 46; +const NL80211_CMD_ROAM: u32 = 47; +const NL80211_CMD_DISCONNECT: u32 = 48; +const NL80211_CMD_SET_WIPHY_NETNS: u32 = 49; +const NL80211_CMD_GET_SURVEY: u32 = 50; +const NL80211_CMD_NEW_SURVEY_RESULTS: u32 = 51; +const NL80211_CMD_SET_PMKSA: u32 = 52; +const NL80211_CMD_DEL_PMKSA: u32 = 53; +const NL80211_CMD_FLUSH_PMKSA: u32 = 54; +const NL80211_CMD_REMAIN_ON_CHANNEL: u32 = 55; +const NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: u32 = 56; +const NL80211_CMD_SET_TX_BITRATE_MASK: u32 = 57; +const NL80211_CMD_REGISTER_FRAME: u32 = 58; +const NL80211_CMD_FRAME: u32 = 59; +const NL80211_CMD_FRAME_TX_STATUS: u32 = 60; +const NL80211_CMD_SET_POWER_SAVE: u32 = 61; +const NL80211_CMD_GET_POWER_SAVE: u32 = 62; +const NL80211_CMD_SET_CQM: u32 = 63; +const NL80211_CMD_NOTIFY_CQM: u32 = 64; +const NL80211_CMD_SET_CHANNEL: u32 = 65; +const NL80211_CMD_SET_WDS_PEER: u32 = 66; +const NL80211_CMD_FRAME_WAIT_CANCEL: u32 = 67; +const NL80211_CMD_JOIN_MESH: u32 = 68; +const NL80211_CMD_LEAVE_MESH: u32 = 69; +const NL80211_CMD_UNPROT_DEAUTHENTICATE: u32 = 70; +const NL80211_CMD_UNPROT_DISASSOCIATE: u32 = 71; +const NL80211_CMD_NEW_PEER_CANDIDATE: u32 = 72; +const NL80211_CMD_GET_WOWLAN: u32 = 73; +const NL80211_CMD_SET_WOWLAN: u32 = 74; +const NL80211_CMD_START_SCHED_SCAN: u32 = 75; +const NL80211_CMD_STOP_SCHED_SCAN: u32 = 76; +const NL80211_CMD_SCHED_SCAN_RESULTS: u32 = 77; +const NL80211_CMD_SCHED_SCAN_STOPPED: u32 = 78; +const NL80211_CMD_SET_REKEY_OFFLOAD: u32 = 79; +const NL80211_CMD_PMKSA_CANDIDATE: u32 = 80; +const NL80211_CMD_TDLS_OPER: u32 = 81; +const NL80211_CMD_TDLS_MGMT: u32 = 82; +const NL80211_CMD_UNEXPECTED_FRAME: u32 = 83; +const NL80211_CMD_PROBE_CLIENT: u32 = 84; +const NL80211_CMD_REGISTER_BEACONS: u32 = 85; +const NL80211_CMD_UNEXPECTED_4ADDR_FRAME: u32 = 86; +const NL80211_CMD_SET_NOACK_MAP: u32 = 87; +const NL80211_CMD_CH_SWITCH_NOTIFY: u32 = 88; +const NL80211_CMD_START_P2P_DEVICE: u32 = 89; +const NL80211_CMD_STOP_P2P_DEVICE: u32 = 90; +const NL80211_CMD_CONN_FAILED: u32 = 91; +const NL80211_CMD_SET_MCAST_RATE: u32 = 92; +const NL80211_CMD_SET_MAC_ACL: u32 = 93; +const NL80211_CMD_RADAR_DETECT: u32 = 94; +const NL80211_CMD_GET_PROTOCOL_FEATURES: u32 = 95; +const NL80211_CMD_UPDATE_FT_IES: u32 = 96; +const NL80211_CMD_FT_EVENT: u32 = 97; +const NL80211_CMD_CRIT_PROTOCOL_START: u32 = 98; +const NL80211_CMD_CRIT_PROTOCOL_STOP: u32 = 99; +const NL80211_CMD_GET_COALESCE: u32 = 100; +const NL80211_CMD_SET_COALESCE: u32 = 101; +const NL80211_CMD_CHANNEL_SWITCH: u32 = 102; +const NL80211_CMD_VENDOR: u32 = 103; +const NL80211_CMD_SET_QOS_MAP: u32 = 104; +const NL80211_CMD_ADD_TX_TS: u32 = 105; +const NL80211_CMD_DEL_TX_TS: u32 = 106; +const NL80211_CMD_GET_MPP: u32 = 107; +const NL80211_CMD_JOIN_OCB: u32 = 108; +const NL80211_CMD_LEAVE_OCB: u32 = 109; +const NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: u32 = 110; +const NL80211_CMD_TDLS_CHANNEL_SWITCH: u32 = 111; +const NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: u32 = 112; +const NL80211_CMD_WIPHY_REG_CHANGE: u32 = 113; +const NL80211_CMD_ABORT_SCAN: u32 = 114; +const NL80211_CMD_START_NAN: u32 = 115; +const NL80211_CMD_STOP_NAN: u32 = 116; +const NL80211_CMD_ADD_NAN_FUNCTION: u32 = 117; +const NL80211_CMD_DEL_NAN_FUNCTION: u32 = 118; +const NL80211_CMD_CHANGE_NAN_CONFIG: u32 = 119; +const NL80211_CMD_NAN_MATCH: u32 = 120; +const NL80211_CMD_SET_MULTICAST_TO_UNICAST: u32 = 121; +const NL80211_CMD_UPDATE_CONNECT_PARAMS: u32 = 122; +const NL80211_CMD_SET_PMK: u32 = 123; +const NL80211_CMD_DEL_PMK: u32 = 124; +const NL80211_CMD_PORT_AUTHORIZED: u32 = 125; +const NL80211_CMD_RELOAD_REGDB: u32 = 126; +const NL80211_CMD_EXTERNAL_AUTH: u32 = 127; +const NL80211_CMD_STA_OPMODE_CHANGED: u32 = 128; +const NL80211_CMD_CONTROL_PORT_FRAME: u32 = 129; +const NL80211_CMD_GET_FTM_RESPONDER_STATS: u32 = 130; +const NL80211_CMD_PEER_MEASUREMENT_START: u32 = 131; +const NL80211_CMD_PEER_MEASUREMENT_RESULT: u32 = 132; +const NL80211_CMD_PEER_MEASUREMENT_COMPLETE: u32 = 133; +const NL80211_CMD_NOTIFY_RADAR: u32 = 134; +const NL80211_CMD_UPDATE_OWE_INFO: u32 = 135; +const NL80211_CMD_PROBE_MESH_LINK: u32 = 136; +const NL80211_CMD_SET_TID_CONFIG: u32 = 137; +const NL80211_CMD_UNPROT_BEACON: u32 = 138; +const NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: u32 = 139; +const NL80211_CMD_SET_SAR_SPECS: u32 = 140; +const NL80211_CMD_OBSS_COLOR_COLLISION: u32 = 141; +const NL80211_CMD_COLOR_CHANGE_REQUEST: u32 = 142; +const NL80211_CMD_COLOR_CHANGE_STARTED: u32 = 143; +const NL80211_CMD_COLOR_CHANGE_ABORTED: u32 = 144; +const NL80211_CMD_COLOR_CHANGE_COMPLETED: u32 = 145; +const NL80211_CMD_SET_FILS_AAD: u32 = 146; +const NL80211_CMD_ASSOC_COMEBACK: u32 = 147; +const NL80211_CMD_ADD_LINK: u32 = 148; +const NL80211_CMD_REMOVE_LINK: u32 = 149; +const NL80211_CMD_ADD_LINK_STA: u32 = 150; +const NL80211_CMD_MODIFY_LINK_STA: u32 = 151; +const NL80211_CMD_REMOVE_LINK_STA: u32 = 152; +const NL80211_CMD_SET_HW_TIMESTAMP: u32 = 153; +const NL80211_CMD_LINKS_REMOVED: u32 = 154; +const NL80211_CMD_SET_TID_TO_LINK_MAPPING: u32 = 155; + +const NL80211_CMD_NEW_BEACON: u32 = NL80211_CMD_START_AP; +const NL80211_CMD_DEL_BEACON: u32 = NL80211_CMD_STOP_AP; +const NL80211_CMD_REGISTER_ACTION: u32 = NL80211_CMD_REGISTER_FRAME; +const NL80211_CMD_ACTION: u32 = NL80211_CMD_FRAME; +const NL80211_CMD_ACTION_TX_STATUS: u32 = NL80211_CMD_FRAME_TX_STATUS; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211Command { + GetWiphy, + SetWiphy, + NewWiphy, + DelWiphy, + GetInterface, + SetInterface, + NewInterface, + DelInterface, + GetKey, + SetKey, + NewKey, + DelKey, + GetBeacon, + SetBeacon, + StartAp, + StopAp, + GetStation, + SetStation, + NewStation, + DelStation, + GetMpath, + SetMpath, + NewMpath, + DelMpath, + SetBss, + SetReg, + ReqSetReg, + GetMeshConfig, + SetMeshConfig, + SetMgmtExtraIe, + GetReg, + GetScan, + TriggerScan, + NewScanResults, + ScanAborted, + RegChange, + Authenticate, + Associate, + Deauthenticate, + Disassociate, + MichaelMicFailure, + RegBeaconHint, + JoinIbss, + LeaveIbss, + Testmode, + Connect, + Roam, + Disconnect, + SetWiphyNetns, + GetSurvey, + NewSurveyResults, + SetPmksa, + DelPmksa, + FlushPmksa, + RemainOnChannel, + CancelRemainOnChannel, + SetTxBitrateMask, + RegisterFrame, + Frame, + FrameTxStatus, + SetPowerSave, + GetPowerSave, + SetCqm, + NotifyCqm, + SetChannel, + SetWdsPeer, + FrameWaitCancel, + JoinMesh, + LeaveMesh, + UnprotDeauthenticate, + UnprotDisassociate, + NewPeerCandidate, + GetWowlan, + SetWowlan, + StartSchedScan, + StopSchedScan, + SchedScanResults, + SchedScanStopped, + SetRekeyOffload, + PmksaCandidate, + TdlsOper, + TdlsMgmt, + UnexpectedFrame, + ProbeClient, + RegisterBeacons, + Unexpected4addrFrame, + SetNoackMap, + ChSwitchNotify, + StartP2PDevice, + StopP2PDevice, + ConnFailed, + SetMcastRate, + SetMacAcl, + RadarDetect, + GetProtocolFeatures, + UpdateFtIes, + FtEvent, + CritProtocolStart, + CritProtocolStop, + GetCoalesce, + SetCoalesce, + ChannelSwitch, + Vendor, + SetQosMap, + AddTxTs, + DelTxTs, + GetMpp, + JoinOcb, + LeaveOcb, + ChSwitchStartedNotify, + TdlsChannelSwitch, + TdlsCancelChannelSwitch, + WiphyRegChange, + AbortScan, + StartNan, + StopNan, + AddNanFunction, + DelNanFunction, + ChangeNanConfig, + NanMatch, + SetMulticastToUnicast, + UpdateConnectParams, + SetPmk, + DelPmk, + PortAuthorized, + ReloadRegdb, + ExternalAuth, + StaOpmodeChanged, + ControlPortFrame, + GetFtmResponderStats, + PeerMeasurementStart, + PeerMeasurementResult, + PeerMeasurementComplete, + NotifyRadar, + UpdateOweInfo, + ProbeMeshLink, + SetTidConfig, + UnprotBeacon, + ControlPortFrameTxStatus, + SetSarSpecs, + ObssColorCollision, + ColorChangeRequest, + ColorChangeStarted, + ColorChangeAborted, + ColorChangeCompleted, + SetFilsAad, + AssocComeback, + AddLink, + RemoveLink, + AddLinkSta, + ModifyLinkSta, + RemoveLinkSta, + SetHwTimestamp, + LinksRemoved, + SetTidToLinkMapping, + Other(u32), + + // Below are aliases + NewBeacon, + DelBeacon, + RegisterAction, + Action, + ActionTxStatus, +} + +impl From for Nl80211Command { + fn from(d: u32) -> Self { + match d { + NL80211_CMD_GET_WIPHY => Self::GetWiphy, + NL80211_CMD_SET_WIPHY => Self::SetWiphy, + NL80211_CMD_NEW_WIPHY => Self::NewWiphy, + NL80211_CMD_DEL_WIPHY => Self::DelWiphy, + NL80211_CMD_GET_INTERFACE => Self::GetInterface, + NL80211_CMD_SET_INTERFACE => Self::SetInterface, + NL80211_CMD_NEW_INTERFACE => Self::NewInterface, + NL80211_CMD_DEL_INTERFACE => Self::DelInterface, + NL80211_CMD_GET_KEY => Self::GetKey, + NL80211_CMD_SET_KEY => Self::SetKey, + NL80211_CMD_NEW_KEY => Self::NewKey, + NL80211_CMD_DEL_KEY => Self::DelKey, + NL80211_CMD_GET_BEACON => Self::GetBeacon, + NL80211_CMD_SET_BEACON => Self::SetBeacon, + NL80211_CMD_START_AP => Self::StartAp, + NL80211_CMD_STOP_AP => Self::StopAp, + NL80211_CMD_GET_STATION => Self::GetStation, + NL80211_CMD_SET_STATION => Self::SetStation, + NL80211_CMD_NEW_STATION => Self::NewStation, + NL80211_CMD_DEL_STATION => Self::DelStation, + NL80211_CMD_GET_MPATH => Self::GetMpath, + NL80211_CMD_SET_MPATH => Self::SetMpath, + NL80211_CMD_NEW_MPATH => Self::NewMpath, + NL80211_CMD_DEL_MPATH => Self::DelMpath, + NL80211_CMD_SET_BSS => Self::SetBss, + NL80211_CMD_SET_REG => Self::SetReg, + NL80211_CMD_REQ_SET_REG => Self::ReqSetReg, + NL80211_CMD_GET_MESH_CONFIG => Self::GetMeshConfig, + NL80211_CMD_SET_MESH_CONFIG => Self::SetMeshConfig, + NL80211_CMD_SET_MGMT_EXTRA_IE => Self::SetMgmtExtraIe, + NL80211_CMD_GET_REG => Self::GetReg, + NL80211_CMD_GET_SCAN => Self::GetScan, + NL80211_CMD_TRIGGER_SCAN => Self::TriggerScan, + NL80211_CMD_NEW_SCAN_RESULTS => Self::NewScanResults, + NL80211_CMD_SCAN_ABORTED => Self::ScanAborted, + NL80211_CMD_REG_CHANGE => Self::RegChange, + NL80211_CMD_AUTHENTICATE => Self::Authenticate, + NL80211_CMD_ASSOCIATE => Self::Associate, + NL80211_CMD_DEAUTHENTICATE => Self::Deauthenticate, + NL80211_CMD_DISASSOCIATE => Self::Disassociate, + NL80211_CMD_MICHAEL_MIC_FAILURE => Self::MichaelMicFailure, + NL80211_CMD_REG_BEACON_HINT => Self::RegBeaconHint, + NL80211_CMD_JOIN_IBSS => Self::JoinIbss, + NL80211_CMD_LEAVE_IBSS => Self::LeaveIbss, + NL80211_CMD_TESTMODE => Self::Testmode, + NL80211_CMD_CONNECT => Self::Connect, + NL80211_CMD_ROAM => Self::Roam, + NL80211_CMD_DISCONNECT => Self::Disconnect, + NL80211_CMD_SET_WIPHY_NETNS => Self::SetWiphyNetns, + NL80211_CMD_GET_SURVEY => Self::GetSurvey, + NL80211_CMD_NEW_SURVEY_RESULTS => Self::NewSurveyResults, + NL80211_CMD_SET_PMKSA => Self::SetPmksa, + NL80211_CMD_DEL_PMKSA => Self::DelPmksa, + NL80211_CMD_FLUSH_PMKSA => Self::FlushPmksa, + NL80211_CMD_REMAIN_ON_CHANNEL => Self::RemainOnChannel, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL => Self::CancelRemainOnChannel, + NL80211_CMD_SET_TX_BITRATE_MASK => Self::SetTxBitrateMask, + NL80211_CMD_REGISTER_FRAME => Self::RegisterFrame, + NL80211_CMD_FRAME => Self::Frame, + NL80211_CMD_FRAME_TX_STATUS => Self::FrameTxStatus, + NL80211_CMD_SET_POWER_SAVE => Self::SetPowerSave, + NL80211_CMD_GET_POWER_SAVE => Self::GetPowerSave, + NL80211_CMD_SET_CQM => Self::SetCqm, + NL80211_CMD_NOTIFY_CQM => Self::NotifyCqm, + NL80211_CMD_SET_CHANNEL => Self::SetChannel, + NL80211_CMD_SET_WDS_PEER => Self::SetWdsPeer, + NL80211_CMD_FRAME_WAIT_CANCEL => Self::FrameWaitCancel, + NL80211_CMD_JOIN_MESH => Self::JoinMesh, + NL80211_CMD_LEAVE_MESH => Self::LeaveMesh, + NL80211_CMD_UNPROT_DEAUTHENTICATE => Self::UnprotDeauthenticate, + NL80211_CMD_UNPROT_DISASSOCIATE => Self::UnprotDisassociate, + NL80211_CMD_NEW_PEER_CANDIDATE => Self::NewPeerCandidate, + NL80211_CMD_GET_WOWLAN => Self::GetWowlan, + NL80211_CMD_SET_WOWLAN => Self::SetWowlan, + NL80211_CMD_START_SCHED_SCAN => Self::StartSchedScan, + NL80211_CMD_STOP_SCHED_SCAN => Self::StopSchedScan, + NL80211_CMD_SCHED_SCAN_RESULTS => Self::SchedScanResults, + NL80211_CMD_SCHED_SCAN_STOPPED => Self::SchedScanStopped, + NL80211_CMD_SET_REKEY_OFFLOAD => Self::SetRekeyOffload, + NL80211_CMD_PMKSA_CANDIDATE => Self::PmksaCandidate, + NL80211_CMD_TDLS_OPER => Self::TdlsOper, + NL80211_CMD_TDLS_MGMT => Self::TdlsMgmt, + NL80211_CMD_UNEXPECTED_FRAME => Self::UnexpectedFrame, + NL80211_CMD_PROBE_CLIENT => Self::ProbeClient, + NL80211_CMD_REGISTER_BEACONS => Self::RegisterBeacons, + NL80211_CMD_UNEXPECTED_4ADDR_FRAME => Self::Unexpected4addrFrame, + NL80211_CMD_SET_NOACK_MAP => Self::SetNoackMap, + NL80211_CMD_CH_SWITCH_NOTIFY => Self::ChSwitchNotify, + NL80211_CMD_START_P2P_DEVICE => Self::StartP2PDevice, + NL80211_CMD_STOP_P2P_DEVICE => Self::StopP2PDevice, + NL80211_CMD_CONN_FAILED => Self::ConnFailed, + NL80211_CMD_SET_MCAST_RATE => Self::SetMcastRate, + NL80211_CMD_SET_MAC_ACL => Self::SetMacAcl, + NL80211_CMD_RADAR_DETECT => Self::RadarDetect, + NL80211_CMD_GET_PROTOCOL_FEATURES => Self::GetProtocolFeatures, + NL80211_CMD_UPDATE_FT_IES => Self::UpdateFtIes, + NL80211_CMD_FT_EVENT => Self::FtEvent, + NL80211_CMD_CRIT_PROTOCOL_START => Self::CritProtocolStart, + NL80211_CMD_CRIT_PROTOCOL_STOP => Self::CritProtocolStop, + NL80211_CMD_GET_COALESCE => Self::GetCoalesce, + NL80211_CMD_SET_COALESCE => Self::SetCoalesce, + NL80211_CMD_CHANNEL_SWITCH => Self::ChannelSwitch, + NL80211_CMD_VENDOR => Self::Vendor, + NL80211_CMD_SET_QOS_MAP => Self::SetQosMap, + NL80211_CMD_ADD_TX_TS => Self::AddTxTs, + NL80211_CMD_DEL_TX_TS => Self::DelTxTs, + NL80211_CMD_GET_MPP => Self::GetMpp, + NL80211_CMD_JOIN_OCB => Self::JoinOcb, + NL80211_CMD_LEAVE_OCB => Self::LeaveOcb, + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY => Self::ChSwitchStartedNotify, + NL80211_CMD_TDLS_CHANNEL_SWITCH => Self::TdlsChannelSwitch, + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH => { + Self::TdlsCancelChannelSwitch + } + NL80211_CMD_WIPHY_REG_CHANGE => Self::WiphyRegChange, + NL80211_CMD_ABORT_SCAN => Self::AbortScan, + NL80211_CMD_START_NAN => Self::StartNan, + NL80211_CMD_STOP_NAN => Self::StopNan, + NL80211_CMD_ADD_NAN_FUNCTION => Self::AddNanFunction, + NL80211_CMD_DEL_NAN_FUNCTION => Self::DelNanFunction, + NL80211_CMD_CHANGE_NAN_CONFIG => Self::ChangeNanConfig, + NL80211_CMD_NAN_MATCH => Self::NanMatch, + NL80211_CMD_SET_MULTICAST_TO_UNICAST => Self::SetMulticastToUnicast, + NL80211_CMD_UPDATE_CONNECT_PARAMS => Self::UpdateConnectParams, + NL80211_CMD_SET_PMK => Self::SetPmk, + NL80211_CMD_DEL_PMK => Self::DelPmk, + NL80211_CMD_PORT_AUTHORIZED => Self::PortAuthorized, + NL80211_CMD_RELOAD_REGDB => Self::ReloadRegdb, + NL80211_CMD_EXTERNAL_AUTH => Self::ExternalAuth, + NL80211_CMD_STA_OPMODE_CHANGED => Self::StaOpmodeChanged, + NL80211_CMD_CONTROL_PORT_FRAME => Self::ControlPortFrame, + NL80211_CMD_GET_FTM_RESPONDER_STATS => Self::GetFtmResponderStats, + NL80211_CMD_PEER_MEASUREMENT_START => Self::PeerMeasurementStart, + NL80211_CMD_PEER_MEASUREMENT_RESULT => Self::PeerMeasurementResult, + NL80211_CMD_PEER_MEASUREMENT_COMPLETE => { + Self::PeerMeasurementComplete + } + NL80211_CMD_NOTIFY_RADAR => Self::NotifyRadar, + NL80211_CMD_UPDATE_OWE_INFO => Self::UpdateOweInfo, + NL80211_CMD_PROBE_MESH_LINK => Self::ProbeMeshLink, + NL80211_CMD_SET_TID_CONFIG => Self::SetTidConfig, + NL80211_CMD_UNPROT_BEACON => Self::UnprotBeacon, + NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS => { + Self::ControlPortFrameTxStatus + } + NL80211_CMD_SET_SAR_SPECS => Self::SetSarSpecs, + NL80211_CMD_OBSS_COLOR_COLLISION => Self::ObssColorCollision, + NL80211_CMD_COLOR_CHANGE_REQUEST => Self::ColorChangeRequest, + NL80211_CMD_COLOR_CHANGE_STARTED => Self::ColorChangeStarted, + NL80211_CMD_COLOR_CHANGE_ABORTED => Self::ColorChangeAborted, + NL80211_CMD_COLOR_CHANGE_COMPLETED => Self::ColorChangeCompleted, + NL80211_CMD_SET_FILS_AAD => Self::SetFilsAad, + NL80211_CMD_ASSOC_COMEBACK => Self::AssocComeback, + NL80211_CMD_ADD_LINK => Self::AddLink, + NL80211_CMD_REMOVE_LINK => Self::RemoveLink, + NL80211_CMD_ADD_LINK_STA => Self::AddLinkSta, + NL80211_CMD_MODIFY_LINK_STA => Self::ModifyLinkSta, + NL80211_CMD_REMOVE_LINK_STA => Self::RemoveLinkSta, + NL80211_CMD_SET_HW_TIMESTAMP => Self::SetHwTimestamp, + NL80211_CMD_LINKS_REMOVED => Self::LinksRemoved, + NL80211_CMD_SET_TID_TO_LINK_MAPPING => Self::SetTidToLinkMapping, + _ => Self::Other(d), + } + } +} + +impl From for u32 { + fn from(v: Nl80211Command) -> u32 { + match v { + Nl80211Command::GetWiphy => NL80211_CMD_GET_WIPHY, + Nl80211Command::SetWiphy => NL80211_CMD_SET_WIPHY, + Nl80211Command::NewWiphy => NL80211_CMD_NEW_WIPHY, + Nl80211Command::DelWiphy => NL80211_CMD_DEL_WIPHY, + Nl80211Command::GetInterface => NL80211_CMD_GET_INTERFACE, + Nl80211Command::SetInterface => NL80211_CMD_SET_INTERFACE, + Nl80211Command::NewInterface => NL80211_CMD_NEW_INTERFACE, + Nl80211Command::DelInterface => NL80211_CMD_DEL_INTERFACE, + Nl80211Command::GetKey => NL80211_CMD_GET_KEY, + Nl80211Command::SetKey => NL80211_CMD_SET_KEY, + Nl80211Command::NewKey => NL80211_CMD_NEW_KEY, + Nl80211Command::DelKey => NL80211_CMD_DEL_KEY, + Nl80211Command::GetBeacon => NL80211_CMD_GET_BEACON, + Nl80211Command::SetBeacon => NL80211_CMD_SET_BEACON, + Nl80211Command::StartAp => NL80211_CMD_START_AP, + Nl80211Command::StopAp => NL80211_CMD_STOP_AP, + Nl80211Command::GetStation => NL80211_CMD_GET_STATION, + Nl80211Command::SetStation => NL80211_CMD_SET_STATION, + Nl80211Command::NewStation => NL80211_CMD_NEW_STATION, + Nl80211Command::DelStation => NL80211_CMD_DEL_STATION, + Nl80211Command::GetMpath => NL80211_CMD_GET_MPATH, + Nl80211Command::SetMpath => NL80211_CMD_SET_MPATH, + Nl80211Command::NewMpath => NL80211_CMD_NEW_MPATH, + Nl80211Command::DelMpath => NL80211_CMD_DEL_MPATH, + Nl80211Command::SetBss => NL80211_CMD_SET_BSS, + Nl80211Command::SetReg => NL80211_CMD_SET_REG, + Nl80211Command::ReqSetReg => NL80211_CMD_REQ_SET_REG, + Nl80211Command::GetMeshConfig => NL80211_CMD_GET_MESH_CONFIG, + Nl80211Command::SetMeshConfig => NL80211_CMD_SET_MESH_CONFIG, + Nl80211Command::SetMgmtExtraIe => NL80211_CMD_SET_MGMT_EXTRA_IE, + Nl80211Command::GetReg => NL80211_CMD_GET_REG, + Nl80211Command::GetScan => NL80211_CMD_GET_SCAN, + Nl80211Command::TriggerScan => NL80211_CMD_TRIGGER_SCAN, + Nl80211Command::NewScanResults => NL80211_CMD_NEW_SCAN_RESULTS, + Nl80211Command::ScanAborted => NL80211_CMD_SCAN_ABORTED, + Nl80211Command::RegChange => NL80211_CMD_REG_CHANGE, + Nl80211Command::Authenticate => NL80211_CMD_AUTHENTICATE, + Nl80211Command::Associate => NL80211_CMD_ASSOCIATE, + Nl80211Command::Deauthenticate => NL80211_CMD_DEAUTHENTICATE, + Nl80211Command::Disassociate => NL80211_CMD_DISASSOCIATE, + Nl80211Command::MichaelMicFailure => { + NL80211_CMD_MICHAEL_MIC_FAILURE + } + Nl80211Command::RegBeaconHint => NL80211_CMD_REG_BEACON_HINT, + Nl80211Command::JoinIbss => NL80211_CMD_JOIN_IBSS, + Nl80211Command::LeaveIbss => NL80211_CMD_LEAVE_IBSS, + Nl80211Command::Testmode => NL80211_CMD_TESTMODE, + Nl80211Command::Connect => NL80211_CMD_CONNECT, + Nl80211Command::Roam => NL80211_CMD_ROAM, + Nl80211Command::Disconnect => NL80211_CMD_DISCONNECT, + Nl80211Command::SetWiphyNetns => NL80211_CMD_SET_WIPHY_NETNS, + Nl80211Command::GetSurvey => NL80211_CMD_GET_SURVEY, + Nl80211Command::NewSurveyResults => NL80211_CMD_NEW_SURVEY_RESULTS, + Nl80211Command::SetPmksa => NL80211_CMD_SET_PMKSA, + Nl80211Command::DelPmksa => NL80211_CMD_DEL_PMKSA, + Nl80211Command::FlushPmksa => NL80211_CMD_FLUSH_PMKSA, + Nl80211Command::RemainOnChannel => NL80211_CMD_REMAIN_ON_CHANNEL, + Nl80211Command::CancelRemainOnChannel => { + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL + } + Nl80211Command::SetTxBitrateMask => NL80211_CMD_SET_TX_BITRATE_MASK, + Nl80211Command::RegisterFrame => NL80211_CMD_REGISTER_FRAME, + Nl80211Command::Frame => NL80211_CMD_FRAME, + Nl80211Command::FrameTxStatus => NL80211_CMD_FRAME_TX_STATUS, + Nl80211Command::SetPowerSave => NL80211_CMD_SET_POWER_SAVE, + Nl80211Command::GetPowerSave => NL80211_CMD_GET_POWER_SAVE, + Nl80211Command::SetCqm => NL80211_CMD_SET_CQM, + Nl80211Command::NotifyCqm => NL80211_CMD_NOTIFY_CQM, + Nl80211Command::SetChannel => NL80211_CMD_SET_CHANNEL, + Nl80211Command::SetWdsPeer => NL80211_CMD_SET_WDS_PEER, + Nl80211Command::FrameWaitCancel => NL80211_CMD_FRAME_WAIT_CANCEL, + Nl80211Command::JoinMesh => NL80211_CMD_JOIN_MESH, + Nl80211Command::LeaveMesh => NL80211_CMD_LEAVE_MESH, + Nl80211Command::UnprotDeauthenticate => { + NL80211_CMD_UNPROT_DEAUTHENTICATE + } + Nl80211Command::UnprotDisassociate => { + NL80211_CMD_UNPROT_DISASSOCIATE + } + Nl80211Command::NewPeerCandidate => NL80211_CMD_NEW_PEER_CANDIDATE, + Nl80211Command::GetWowlan => NL80211_CMD_GET_WOWLAN, + Nl80211Command::SetWowlan => NL80211_CMD_SET_WOWLAN, + Nl80211Command::StartSchedScan => NL80211_CMD_START_SCHED_SCAN, + Nl80211Command::StopSchedScan => NL80211_CMD_STOP_SCHED_SCAN, + Nl80211Command::SchedScanResults => NL80211_CMD_SCHED_SCAN_RESULTS, + Nl80211Command::SchedScanStopped => NL80211_CMD_SCHED_SCAN_STOPPED, + Nl80211Command::SetRekeyOffload => NL80211_CMD_SET_REKEY_OFFLOAD, + Nl80211Command::PmksaCandidate => NL80211_CMD_PMKSA_CANDIDATE, + Nl80211Command::TdlsOper => NL80211_CMD_TDLS_OPER, + Nl80211Command::TdlsMgmt => NL80211_CMD_TDLS_MGMT, + Nl80211Command::UnexpectedFrame => NL80211_CMD_UNEXPECTED_FRAME, + Nl80211Command::ProbeClient => NL80211_CMD_PROBE_CLIENT, + Nl80211Command::RegisterBeacons => NL80211_CMD_REGISTER_BEACONS, + Nl80211Command::Unexpected4addrFrame => { + NL80211_CMD_UNEXPECTED_4ADDR_FRAME + } + Nl80211Command::SetNoackMap => NL80211_CMD_SET_NOACK_MAP, + Nl80211Command::ChSwitchNotify => NL80211_CMD_CH_SWITCH_NOTIFY, + Nl80211Command::StartP2PDevice => NL80211_CMD_START_P2P_DEVICE, + Nl80211Command::StopP2PDevice => NL80211_CMD_STOP_P2P_DEVICE, + Nl80211Command::ConnFailed => NL80211_CMD_CONN_FAILED, + Nl80211Command::SetMcastRate => NL80211_CMD_SET_MCAST_RATE, + Nl80211Command::SetMacAcl => NL80211_CMD_SET_MAC_ACL, + Nl80211Command::RadarDetect => NL80211_CMD_RADAR_DETECT, + Nl80211Command::GetProtocolFeatures => { + NL80211_CMD_GET_PROTOCOL_FEATURES + } + Nl80211Command::UpdateFtIes => NL80211_CMD_UPDATE_FT_IES, + Nl80211Command::FtEvent => NL80211_CMD_FT_EVENT, + Nl80211Command::CritProtocolStart => { + NL80211_CMD_CRIT_PROTOCOL_START + } + Nl80211Command::CritProtocolStop => NL80211_CMD_CRIT_PROTOCOL_STOP, + Nl80211Command::GetCoalesce => NL80211_CMD_GET_COALESCE, + Nl80211Command::SetCoalesce => NL80211_CMD_SET_COALESCE, + Nl80211Command::ChannelSwitch => NL80211_CMD_CHANNEL_SWITCH, + Nl80211Command::Vendor => NL80211_CMD_VENDOR, + Nl80211Command::SetQosMap => NL80211_CMD_SET_QOS_MAP, + Nl80211Command::AddTxTs => NL80211_CMD_ADD_TX_TS, + Nl80211Command::DelTxTs => NL80211_CMD_DEL_TX_TS, + Nl80211Command::GetMpp => NL80211_CMD_GET_MPP, + Nl80211Command::JoinOcb => NL80211_CMD_JOIN_OCB, + Nl80211Command::LeaveOcb => NL80211_CMD_LEAVE_OCB, + Nl80211Command::ChSwitchStartedNotify => { + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY + } + Nl80211Command::TdlsChannelSwitch => { + NL80211_CMD_TDLS_CHANNEL_SWITCH + } + Nl80211Command::TdlsCancelChannelSwitch => { + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH + } + Nl80211Command::WiphyRegChange => NL80211_CMD_WIPHY_REG_CHANGE, + Nl80211Command::AbortScan => NL80211_CMD_ABORT_SCAN, + Nl80211Command::StartNan => NL80211_CMD_START_NAN, + Nl80211Command::StopNan => NL80211_CMD_STOP_NAN, + Nl80211Command::AddNanFunction => NL80211_CMD_ADD_NAN_FUNCTION, + Nl80211Command::DelNanFunction => NL80211_CMD_DEL_NAN_FUNCTION, + Nl80211Command::ChangeNanConfig => NL80211_CMD_CHANGE_NAN_CONFIG, + Nl80211Command::NanMatch => NL80211_CMD_NAN_MATCH, + Nl80211Command::SetMulticastToUnicast => { + NL80211_CMD_SET_MULTICAST_TO_UNICAST + } + Nl80211Command::UpdateConnectParams => { + NL80211_CMD_UPDATE_CONNECT_PARAMS + } + Nl80211Command::SetPmk => NL80211_CMD_SET_PMK, + Nl80211Command::DelPmk => NL80211_CMD_DEL_PMK, + Nl80211Command::PortAuthorized => NL80211_CMD_PORT_AUTHORIZED, + Nl80211Command::ReloadRegdb => NL80211_CMD_RELOAD_REGDB, + Nl80211Command::ExternalAuth => NL80211_CMD_EXTERNAL_AUTH, + Nl80211Command::StaOpmodeChanged => NL80211_CMD_STA_OPMODE_CHANGED, + Nl80211Command::ControlPortFrame => NL80211_CMD_CONTROL_PORT_FRAME, + Nl80211Command::GetFtmResponderStats => { + NL80211_CMD_GET_FTM_RESPONDER_STATS + } + Nl80211Command::PeerMeasurementStart => { + NL80211_CMD_PEER_MEASUREMENT_START + } + Nl80211Command::PeerMeasurementResult => { + NL80211_CMD_PEER_MEASUREMENT_RESULT + } + Nl80211Command::PeerMeasurementComplete => { + NL80211_CMD_PEER_MEASUREMENT_COMPLETE + } + Nl80211Command::NotifyRadar => NL80211_CMD_NOTIFY_RADAR, + Nl80211Command::UpdateOweInfo => NL80211_CMD_UPDATE_OWE_INFO, + Nl80211Command::ProbeMeshLink => NL80211_CMD_PROBE_MESH_LINK, + Nl80211Command::SetTidConfig => NL80211_CMD_SET_TID_CONFIG, + Nl80211Command::UnprotBeacon => NL80211_CMD_UNPROT_BEACON, + Nl80211Command::ControlPortFrameTxStatus => { + NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS + } + Nl80211Command::SetSarSpecs => NL80211_CMD_SET_SAR_SPECS, + Nl80211Command::ObssColorCollision => { + NL80211_CMD_OBSS_COLOR_COLLISION + } + Nl80211Command::ColorChangeRequest => { + NL80211_CMD_COLOR_CHANGE_REQUEST + } + Nl80211Command::ColorChangeStarted => { + NL80211_CMD_COLOR_CHANGE_STARTED + } + Nl80211Command::ColorChangeAborted => { + NL80211_CMD_COLOR_CHANGE_ABORTED + } + Nl80211Command::ColorChangeCompleted => { + NL80211_CMD_COLOR_CHANGE_COMPLETED + } + Nl80211Command::SetFilsAad => NL80211_CMD_SET_FILS_AAD, + Nl80211Command::AssocComeback => NL80211_CMD_ASSOC_COMEBACK, + Nl80211Command::AddLink => NL80211_CMD_ADD_LINK, + Nl80211Command::RemoveLink => NL80211_CMD_REMOVE_LINK, + Nl80211Command::AddLinkSta => NL80211_CMD_ADD_LINK_STA, + Nl80211Command::ModifyLinkSta => NL80211_CMD_MODIFY_LINK_STA, + Nl80211Command::RemoveLinkSta => NL80211_CMD_REMOVE_LINK_STA, + Nl80211Command::SetHwTimestamp => NL80211_CMD_SET_HW_TIMESTAMP, + Nl80211Command::LinksRemoved => NL80211_CMD_LINKS_REMOVED, + Nl80211Command::SetTidToLinkMapping => { + NL80211_CMD_SET_TID_TO_LINK_MAPPING + } + Nl80211Command::Other(d) => d, + + Nl80211Command::NewBeacon => NL80211_CMD_NEW_BEACON, + Nl80211Command::DelBeacon => NL80211_CMD_DEL_BEACON, + Nl80211Command::RegisterAction => NL80211_CMD_REGISTER_ACTION, + Nl80211Command::Action => NL80211_CMD_ACTION, + Nl80211Command::ActionTxStatus => NL80211_CMD_ACTION_TX_STATUS, + } + } +} diff --git a/src/wiphy/get.rs b/src/wiphy/get.rs new file mode 100644 index 0000000..79fd680 --- /dev/null +++ b/src/wiphy/get.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +use futures::TryStream; +use netlink_packet_generic::GenlMessage; + +use crate::{nl80211_execute, Nl80211Error, Nl80211Handle, Nl80211Message}; + +pub struct Nl80211WiphyGetRequest { + handle: Nl80211Handle, +} + +impl Nl80211WiphyGetRequest { + pub(crate) fn new(handle: Nl80211Handle) -> Self { + Nl80211WiphyGetRequest { handle } + } + + pub async fn execute( + self, + ) -> impl TryStream, Error = Nl80211Error> + { + let Nl80211WiphyGetRequest { mut handle } = self; + + let nl80211_msg = Nl80211Message::new_wiphy_get(); + nl80211_execute(&mut handle, nl80211_msg).await + } +} diff --git a/src/wiphy/handle.rs b/src/wiphy/handle.rs new file mode 100644 index 0000000..5a0eaae --- /dev/null +++ b/src/wiphy/handle.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +use crate::{Nl80211Handle, Nl80211WiphyGetRequest}; + +#[derive(Debug)] +pub struct Nl80211WiphyHandle(Nl80211Handle); + +impl Nl80211WiphyHandle { + pub fn new(handle: Nl80211Handle) -> Self { + Nl80211WiphyHandle(handle) + } + + /// Retrieve the wireless interfaces + /// (equivalent to `iw phy`) + pub fn get(&mut self) -> Nl80211WiphyGetRequest { + Nl80211WiphyGetRequest::new(self.0.clone()) + } +} diff --git a/src/wiphy/ifmode.rs b/src/wiphy/ifmode.rs new file mode 100644 index 0000000..8fbf368 --- /dev/null +++ b/src/wiphy/ifmode.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{ + nla::{Nla, NlaBuffer}, + DecodeError, Parseable, +}; + +const NL80211_IFTYPE_ADHOC: u16 = 1; +const NL80211_IFTYPE_STATION: u16 = 2; +const NL80211_IFTYPE_AP: u16 = 3; +const NL80211_IFTYPE_AP_VLAN: u16 = 4; +const NL80211_IFTYPE_WDS: u16 = 5; +const NL80211_IFTYPE_MONITOR: u16 = 6; +const NL80211_IFTYPE_MESH_POINT: u16 = 7; +const NL80211_IFTYPE_P2P_CLIENT: u16 = 8; +const NL80211_IFTYPE_P2P_GO: u16 = 9; +const NL80211_IFTYPE_P2P_DEVICE: u16 = 10; +const NL80211_IFTYPE_OCB: u16 = 11; +const NL80211_IFTYPE_NAN: u16 = 12; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Nl80211IfMode { + Adhoc, + Station, + Ap, + ApVlan, + Wds, + Monitor, + MeshPoint, + P2pClient, + P2pGo, + P2pDevice, + Ocb, + Nan, + Other(u16), +} + +impl From for Nl80211IfMode { + fn from(d: u16) -> Self { + match d { + NL80211_IFTYPE_ADHOC => Self::Adhoc, + NL80211_IFTYPE_STATION => Self::Station, + NL80211_IFTYPE_AP => Self::Ap, + NL80211_IFTYPE_AP_VLAN => Self::ApVlan, + NL80211_IFTYPE_WDS => Self::Wds, + NL80211_IFTYPE_MONITOR => Self::Monitor, + NL80211_IFTYPE_MESH_POINT => Self::MeshPoint, + NL80211_IFTYPE_P2P_CLIENT => Self::P2pClient, + NL80211_IFTYPE_P2P_GO => Self::P2pGo, + NL80211_IFTYPE_P2P_DEVICE => Self::P2pDevice, + NL80211_IFTYPE_OCB => Self::Ocb, + NL80211_IFTYPE_NAN => Self::Nan, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(v: Nl80211IfMode) -> u16 { + match v { + Nl80211IfMode::Adhoc => NL80211_IFTYPE_ADHOC, + Nl80211IfMode::Station => NL80211_IFTYPE_STATION, + Nl80211IfMode::Ap => NL80211_IFTYPE_AP, + Nl80211IfMode::ApVlan => NL80211_IFTYPE_AP_VLAN, + Nl80211IfMode::Wds => NL80211_IFTYPE_WDS, + Nl80211IfMode::Monitor => NL80211_IFTYPE_MONITOR, + Nl80211IfMode::MeshPoint => NL80211_IFTYPE_MESH_POINT, + Nl80211IfMode::P2pClient => NL80211_IFTYPE_P2P_CLIENT, + Nl80211IfMode::P2pGo => NL80211_IFTYPE_P2P_GO, + Nl80211IfMode::P2pDevice => NL80211_IFTYPE_P2P_DEVICE, + Nl80211IfMode::Ocb => NL80211_IFTYPE_OCB, + Nl80211IfMode::Nan => NL80211_IFTYPE_NAN, + Nl80211IfMode::Other(d) => d, + } + } +} + +impl Nla for Nl80211IfMode { + fn value_len(&self) -> usize { + 0 + } + + fn kind(&self) -> u16 { + match self { + Nl80211IfMode::Adhoc => NL80211_IFTYPE_ADHOC, + Nl80211IfMode::Station => NL80211_IFTYPE_STATION, + Nl80211IfMode::Ap => NL80211_IFTYPE_AP, + Nl80211IfMode::ApVlan => NL80211_IFTYPE_AP_VLAN, + Nl80211IfMode::Wds => NL80211_IFTYPE_WDS, + Nl80211IfMode::Monitor => NL80211_IFTYPE_MONITOR, + Nl80211IfMode::MeshPoint => NL80211_IFTYPE_MESH_POINT, + Nl80211IfMode::P2pClient => NL80211_IFTYPE_P2P_CLIENT, + Nl80211IfMode::P2pGo => NL80211_IFTYPE_P2P_GO, + Nl80211IfMode::P2pDevice => NL80211_IFTYPE_P2P_DEVICE, + Nl80211IfMode::Ocb => NL80211_IFTYPE_OCB, + Nl80211IfMode::Nan => NL80211_IFTYPE_NAN, + Nl80211IfMode::Other(d) => *d, + } + } + + fn emit_value(&self, _buffer: &mut [u8]) {} +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211IfMode +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + Ok(buf.kind().into()) + } +} diff --git a/src/wiphy/mod.rs b/src/wiphy/mod.rs new file mode 100644 index 0000000..0d25ce3 --- /dev/null +++ b/src/wiphy/mod.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +mod band; +mod cipher; +mod command; +mod get; +mod handle; +mod ifmode; +mod wowlan; + +pub use self::band::{ + Nl80211Band, Nl80211BandInfo, Nl80211BandType, Nl80211BandTypes, + Nl80211Frequency, Nl80211FrequencyInfo, +}; +pub use self::cipher::Nl80211CipherSuit; +pub use self::command::Nl80211Command; +pub use self::get::Nl80211WiphyGetRequest; +pub use self::handle::Nl80211WiphyHandle; +pub use self::ifmode::Nl80211IfMode; +pub use self::wowlan::{ + Nl80211WowlanTcpTrigerSupport, Nl80211WowlanTrigerPatternSupport, + Nl80211WowlanTrigersSupport, +}; + +pub(crate) use self::command::Nl80211Commands; diff --git a/src/wiphy/wowlan.rs b/src/wiphy/wowlan.rs new file mode 100644 index 0000000..63e8812 --- /dev/null +++ b/src/wiphy/wowlan.rs @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: MIT + +// Most documentation comments are copied and modified from linux kernel +// include/uapi/linux/nl80211.h which is holding these license disclaimer: +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2024 Intel Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::parse_u32, + DecodeError, Emitable, Parseable, +}; + +use crate::bytes::write_u32; + +const NL80211_WOWLAN_TRIG_ANY: u16 = 1; +const NL80211_WOWLAN_TRIG_DISCONNECT: u16 = 2; +const NL80211_WOWLAN_TRIG_MAGIC_PKT: u16 = 3; +const NL80211_WOWLAN_TRIG_PKT_PATTERN: u16 = 4; +const NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: u16 = 5; +const NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: u16 = 6; +const NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: u16 = 7; +const NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: u16 = 8; +const NL80211_WOWLAN_TRIG_RFKILL_RELEASE: u16 = 9; +// const NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: u16 = 10; +// const NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: u16 = 11; +// const NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: u16 = 12; +// const NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: u16 = 13; +const NL80211_WOWLAN_TRIG_TCP_CONNECTION: u16 = 14; +// const NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: u16 = 15; +// const NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: u16 = 16; +// const NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: u16 = 17; +const NL80211_WOWLAN_TRIG_NET_DETECT: u16 = 18; +// const NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: u16 = 19; +// const NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: u16 = 20; + +/// Supported WoWLAN trigger +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211WowlanTrigersSupport { + /// Wake up on any activity, do not really put the chip into a special + /// state -- works best with chips that have support for low-power + /// operation already. + /// Note that this mode is incompatible with all of the others, if + /// any others are even supported by the device. + Any, + /// Wake up on disconnect, the way disconnect is detected is + /// implementation-specific. + Disconnect, + /// Wake up on magic packet (6x 0xff, followed by 16 repetitions of MAC + /// addr, anywhere in payload). + MagicPkt, + /// Wake up on the specified packet patterns. + /// The matching is done on the MSDU, i.e. as though the packet was an + /// 802.3 packet, so the pattern matching is done after the packet is + /// converted to the MSDU. + PktPattern(Nl80211WowlanTrigerPatternSupport), + /// Not a real trigger, and cannot be used when setting, used only to + /// indicate that GTK rekeying is supported by the device. + GtkRekeySupported, + /// wake up on GTK rekey failure (if done by the device). + GtkRekeyFailure, + /// wake up on EAP Identity Request packet. + EapIdentRequest, + /// wake up on 4-way handshake. + FourWayHandshake, + /// wake up when rfkill is released (on devices that have rfkill in the + /// device). + RfkillRelease, + /// The number of match sets supported by driver for waking up when a + /// configured network is detected. + NetDetect(u32), + /// TCP connection wake. + TcpConnection(Vec), + Other(DefaultNla), +} + +impl Nla for Nl80211WowlanTrigersSupport { + fn value_len(&self) -> usize { + match self { + Self::Any + | Self::Disconnect + | Self::MagicPkt + | Self::GtkRekeySupported + | Self::GtkRekeyFailure + | Self::EapIdentRequest + | Self::FourWayHandshake + | Self::RfkillRelease => 0, + Self::PktPattern(_) => Nl80211WowlanTrigerPatternSupport::LENGTH, + Self::NetDetect(_) => 4, + Self::TcpConnection(s) => s.as_slice().buffer_len(), + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Any => NL80211_WOWLAN_TRIG_ANY, + Self::Disconnect => NL80211_WOWLAN_TRIG_DISCONNECT, + Self::MagicPkt => NL80211_WOWLAN_TRIG_MAGIC_PKT, + Self::GtkRekeySupported => NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + Self::GtkRekeyFailure => NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + Self::EapIdentRequest => NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + Self::FourWayHandshake => NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + Self::RfkillRelease => NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + Self::PktPattern(_) => NL80211_WOWLAN_TRIG_PKT_PATTERN, + Self::NetDetect(_) => NL80211_WOWLAN_TRIG_NET_DETECT, + Self::TcpConnection(_) => NL80211_WOWLAN_TRIG_TCP_CONNECTION, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Any + | Self::Disconnect + | Self::MagicPkt + | Self::GtkRekeySupported + | Self::GtkRekeyFailure + | Self::EapIdentRequest + | Self::FourWayHandshake + | Self::RfkillRelease => (), + Self::PktPattern(s) => s.emit(buffer), + Self::NetDetect(d) => write_u32(buffer, *d), + Self::TcpConnection(s) => s.as_slice().emit(buffer), + Self::Other(attr) => attr.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211WowlanTrigersSupport +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_WOWLAN_TRIG_ANY => Self::Any, + NL80211_WOWLAN_TRIG_DISCONNECT => Self::Disconnect, + NL80211_WOWLAN_TRIG_MAGIC_PKT => Self::MagicPkt, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED => Self::GtkRekeySupported, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE => Self::GtkRekeyFailure, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST => Self::EapIdentRequest, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE => Self::FourWayHandshake, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE => Self::RfkillRelease, + NL80211_WOWLAN_TRIG_PKT_PATTERN => Self::PktPattern( + Nl80211WowlanTrigerPatternSupport::parse(payload)?, + ), + NL80211_WOWLAN_TRIG_NET_DETECT => { + Self::NetDetect(parse_u32(payload).context(format!( + "Invalid NL80211_WOWLAN_TRIG_NET_DETECT \ + {payload:?}" + ))?) + } + NL80211_WOWLAN_TRIG_TCP_CONNECTION => { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(payload) { + let err_msg = format!( + "Invalid NL80211_WOWLAN_TRIG_TCP_CONNECTION value {:?}", + nla + ); + let nla = &nla.context(err_msg.clone())?; + nlas.push(Nl80211WowlanTcpTrigerSupport::parse(nla)?); + } + + Self::TcpConnection(nlas) + } + _ => Self::Other(DefaultNla::parse(buf).context( + "invalid NLA for NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED ", + )?), + }) + } +} + +/// Support status of WoWLAN trigger pattern +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Nl80211WowlanTrigerPatternSupport { + pub max_patterns: u32, + pub min_pattern_len: u32, + pub max_pattern_len: u32, + pub max_pkt_offset: u32, +} + +impl Nl80211WowlanTrigerPatternSupport { + const LENGTH: usize = 16; + + pub fn parse(payload: &[u8]) -> Result { + if payload.len() < Self::LENGTH { + Err(format!( + "Invalid NL80211_WOWLAN_TRIG_PKT_PATTERN \ + for support query, expecting length {} but got {}: \ + {payload:?}", + Self::LENGTH, + payload.len() + ) + .into()) + } else { + Ok(Self { + max_patterns: parse_u32(&payload[..4])?, + min_pattern_len: parse_u32(&payload[4..8])?, + max_pattern_len: parse_u32(&payload[8..12])?, + max_pkt_offset: parse_u32(&payload[12..16])?, + }) + } + } +} + +impl Emitable for Nl80211WowlanTrigerPatternSupport { + fn buffer_len(&self) -> usize { + Self::LENGTH + } + + fn emit(&self, buffer: &mut [u8]) { + write_u32(&mut buffer[0..4], self.max_patterns); + write_u32(&mut buffer[4..8], self.min_pattern_len); + write_u32(&mut buffer[8..12], self.max_pattern_len); + write_u32(&mut buffer[12..16], self.max_pkt_offset); + } +} + +const NL80211_WOWLAN_TCP_SRC_IPV4: u16 = 1; +const NL80211_WOWLAN_TCP_DST_IPV4: u16 = 2; +const NL80211_WOWLAN_TCP_DST_MAC: u16 = 3; +const NL80211_WOWLAN_TCP_SRC_PORT: u16 = 4; +const NL80211_WOWLAN_TCP_DST_PORT: u16 = 5; +const NL80211_WOWLAN_TCP_DATA_PAYLOAD: u16 = 6; +const NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: u16 = 7; +const NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: u16 = 8; +const NL80211_WOWLAN_TCP_DATA_INTERVAL: u16 = 9; +const NL80211_WOWLAN_TCP_WAKE_PAYLOAD: u16 = 10; +const NL80211_WOWLAN_TCP_WAKE_MASK: u16 = 11; + +/// Supported WoWLAN TCP connection trigger +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Nl80211WowlanTcpTrigerSupport { + SrcIpv4, + DstIpv4, + DstMac, + SrcPort, + DstPort, + DataPayload(u32), + DataPayloadSeq, + DataPayloadToken, + DataInterval(u32), + WakePayload(u32), + WakeMask, + Other(DefaultNla), +} + +impl Nla for Nl80211WowlanTcpTrigerSupport { + fn value_len(&self) -> usize { + match self { + Self::SrcIpv4 + | Self::DstIpv4 + | Self::DstMac + | Self::SrcPort + | Self::DstPort + | Self::DataPayloadSeq + | Self::DataPayloadToken + | Self::WakeMask => 0, + Self::DataPayload(_) + | Self::DataInterval(_) + | Self::WakePayload(_) => 4, + Self::Other(v) => v.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::SrcIpv4 => NL80211_WOWLAN_TCP_SRC_IPV4, + Self::DstIpv4 => NL80211_WOWLAN_TCP_DST_IPV4, + Self::DstMac => NL80211_WOWLAN_TCP_DST_MAC, + Self::SrcPort => NL80211_WOWLAN_TCP_SRC_PORT, + Self::DstPort => NL80211_WOWLAN_TCP_DST_PORT, + Self::DataPayload(_) => NL80211_WOWLAN_TCP_DATA_PAYLOAD, + Self::DataPayloadSeq => NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + Self::DataPayloadToken => NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + Self::DataInterval(_) => NL80211_WOWLAN_TCP_DATA_INTERVAL, + Self::WakePayload(_) => NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + Self::WakeMask => NL80211_WOWLAN_TCP_WAKE_MASK, + Self::Other(v) => v.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::SrcIpv4 + | Self::DstIpv4 + | Self::DstMac + | Self::SrcPort + | Self::DstPort + | Self::DataPayloadSeq + | Self::DataPayloadToken + | Self::WakeMask => (), + Self::DataPayload(d) + | Self::DataInterval(d) + | Self::WakePayload(d) => write_u32(buffer, *d), + Self::Other(v) => v.emit(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for Nl80211WowlanTcpTrigerSupport +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + NL80211_WOWLAN_TCP_SRC_IPV4 => Self::SrcIpv4, + NL80211_WOWLAN_TCP_DST_IPV4 => Self::DstIpv4, + NL80211_WOWLAN_TCP_DST_MAC => Self::DstMac, + NL80211_WOWLAN_TCP_SRC_PORT => Self::SrcPort, + NL80211_WOWLAN_TCP_DST_PORT => Self::DstPort, + NL80211_WOWLAN_TCP_DATA_PAYLOAD => { + Self::DataPayload(parse_u32(payload).context(format!( + "Invalid NL80211_WOWLAN_TCP_DATA_PAYLOAD {payload:?}" + ))?) + } + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ => Self::DataPayloadSeq, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN => Self::DataPayloadToken, + NL80211_WOWLAN_TCP_DATA_INTERVAL => { + Self::DataInterval(parse_u32(payload).context(format!( + "Invalid NL80211_WOWLAN_TCP_DATA_INTERVAL {payload:?}" + ))?) + } + NL80211_WOWLAN_TCP_WAKE_PAYLOAD => { + Self::WakePayload(parse_u32(payload).context(format!( + "Invalid NL80211_WOWLAN_TCP_WAKE_PAYLOAD {payload:?}" + ))?) + } + NL80211_WOWLAN_TCP_WAKE_MASK => Self::WakeMask, + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +}