From 59261b530239c076c61011707864133d062a2dcf Mon Sep 17 00:00:00 2001 From: Olli Paakkunainen Date: Wed, 9 Aug 2023 12:48:26 +0300 Subject: [PATCH] Add set channel feature --- src/attributes.rs | 24 +++++++++++++++ src/commands.rs | 8 +++++ src/interface.rs | 78 +++++++++++++++++++++++++---------------------- src/netlink.rs | 42 ++++++++++++++++++++++++- 4 files changed, 115 insertions(+), 37 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index f0a76a1..8e4ec4a 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -905,3 +905,27 @@ pub enum MonitorFlags { } impl NlAttrType for MonitorFlags {} + +/// Channel width definitions. +/// +/// These values are used with the `Attribute.ChannelWidth` attribute. +/// +/// nl80211_chan_width enum from: +/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/nl80211.h +#[neli_enum(serialized_type = "u32")] +pub(crate) enum ChannelWidth { + Width20NoHT = 0, + Width20 = 1, + Width40 = 2, + Width80 = 3, + Width80P80 = 4, + Width160 = 5, + Width5 = 6, + Width10 = 7, + Width1 = 8, + Width2 = 9, + Width4 = 10, + Width8 = 11, + Width16 = 12, + Width320 = 13, +} diff --git a/src/commands.rs b/src/commands.rs index e2ff28d..d622235 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -116,6 +116,14 @@ pub(crate) enum Command { SetCqm = 63, NotifyCqm = 64, + /// Set the channel (using NL80211_ATTR_WIPHY_FREQ and the attributes + /// determining channel width) the given interface (identifed by + /// NL80211_ATTR_IFINDEX) shall operate on. In case multiple channels are + /// supported by the device, the mechanism with which it switches channels + /// is implementation-defined. When a monitor interface is given, it can + /// only switch channel while no other interfaces are operating to avoid + /// disturbing the operation of any other interfaces, and other interfaces + /// will again take precedence when they are used. SetChannel = 65, SetWdsPeer = 66, diff --git a/src/interface.rs b/src/interface.rs index 45a2373..271b6c3 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -6,6 +6,7 @@ use neli::attr::Attribute as NeliAttribute; use neli::err::DeError; use neli::FromBytes; +use super::attributes::ChannelWidth as NlChannelWidth; use super::attributes::InterfaceType as NlInterfaceType; use super::attributes::{Attribute, TxqStats}; use crate::attributes::Attrs; @@ -35,7 +36,7 @@ pub struct WirelessInterface { /// Center frequency of the second part of the channel, used only for 80+80 MHz bandwidth. pub center_frequency2: Option, /// Wireless channel width. - pub channel_width: Option, + pub channel_width: ChannelWidth, /// Transmit power level (s16) in dBm. pub tx_power: Option, /// Wireless device identifier, used for pseudo-devices that don't have a netdev. @@ -83,8 +84,8 @@ impl TryFrom> for WirelessInterface { interface.center_frequency2 = Some(attr.get_payload_as()?); } Attribute::ChannelWidth => { - let attr_channel_width: u32 = attr.get_payload_as()?; - interface.channel_width = Some(attr_channel_width.into()); + let attr_channel_width: NlChannelWidth = attr.get_payload_as()?; + interface.channel_width = attr_channel_width.into(); } Attribute::WiphyTxPowerLevel => { interface.tx_power = Some(attr.get_payload_as()?); @@ -340,7 +341,7 @@ impl fmt::Display for InterfaceType { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] /// Wireless channel width. pub enum ChannelWidth { Width20NoHT, @@ -356,47 +357,51 @@ pub enum ChannelWidth { Width4, Width8, Width16, + Width320, + #[default] Unknown, } -impl From for ChannelWidth { - fn from(attr_channel_width: u32) -> Self { +impl From for NlChannelWidth { + fn from(attr_channel_width: ChannelWidth) -> Self { match attr_channel_width { - 0 => ChannelWidth::Width20NoHT, - 1 => ChannelWidth::Width20, - 2 => ChannelWidth::Width40, - 3 => ChannelWidth::Width80, - 4 => ChannelWidth::Width80P80, - 5 => ChannelWidth::Width160, - 6 => ChannelWidth::Width5, - 7 => ChannelWidth::Width10, - 8 => ChannelWidth::Width1, - 9 => ChannelWidth::Width2, - 10 => ChannelWidth::Width4, - 11 => ChannelWidth::Width8, - 12 => ChannelWidth::Width16, - _ => ChannelWidth::Unknown, + ChannelWidth::Width20NoHT => NlChannelWidth::Width20NoHT, + ChannelWidth::Width20 => NlChannelWidth::Width20, + ChannelWidth::Width40 => NlChannelWidth::Width40, + ChannelWidth::Width80 => NlChannelWidth::Width80, + ChannelWidth::Width80P80 => NlChannelWidth::Width80P80, + ChannelWidth::Width160 => NlChannelWidth::Width160, + ChannelWidth::Width5 => NlChannelWidth::Width5, + ChannelWidth::Width10 => NlChannelWidth::Width10, + ChannelWidth::Width1 => NlChannelWidth::Width1, + ChannelWidth::Width2 => NlChannelWidth::Width2, + ChannelWidth::Width4 => NlChannelWidth::Width4, + ChannelWidth::Width8 => NlChannelWidth::Width8, + ChannelWidth::Width16 => NlChannelWidth::Width16, + ChannelWidth::Width320 => NlChannelWidth::Width320, + ChannelWidth::Unknown => NlChannelWidth::Width20NoHT, } } } -impl From for u32 { - fn from(attr_channel_width: ChannelWidth) -> Self { +impl From for ChannelWidth { + fn from(attr_channel_width: NlChannelWidth) -> Self { match attr_channel_width { - ChannelWidth::Width20NoHT => 20, - ChannelWidth::Width20 => 20, - ChannelWidth::Width40 => 40, - ChannelWidth::Width80 => 80, - ChannelWidth::Width80P80 => 80, - ChannelWidth::Width160 => 160, - ChannelWidth::Width5 => 5, - ChannelWidth::Width10 => 10, - ChannelWidth::Width1 => 1, - ChannelWidth::Width2 => 2, - ChannelWidth::Width4 => 4, - ChannelWidth::Width8 => 8, - ChannelWidth::Width16 => 16, - ChannelWidth::Unknown => 0, + NlChannelWidth::Width20NoHT => ChannelWidth::Width20NoHT, + NlChannelWidth::Width20 => ChannelWidth::Width20, + NlChannelWidth::Width40 => ChannelWidth::Width40, + NlChannelWidth::Width80 => ChannelWidth::Width80, + NlChannelWidth::Width80P80 => ChannelWidth::Width80P80, + NlChannelWidth::Width160 => ChannelWidth::Width160, + NlChannelWidth::Width5 => ChannelWidth::Width5, + NlChannelWidth::Width10 => ChannelWidth::Width10, + NlChannelWidth::Width1 => ChannelWidth::Width1, + NlChannelWidth::Width2 => ChannelWidth::Width2, + NlChannelWidth::Width4 => ChannelWidth::Width4, + NlChannelWidth::Width8 => ChannelWidth::Width8, + NlChannelWidth::Width16 => ChannelWidth::Width16, + NlChannelWidth::Width320 => ChannelWidth::Width320, + NlChannelWidth::UnrecognizedConst(_) => ChannelWidth::Unknown, } } } @@ -417,6 +422,7 @@ impl fmt::Display for ChannelWidth { ChannelWidth::Width4 => "4 MHz OFDM", ChannelWidth::Width8 => "8 MHz OFDM", ChannelWidth::Width16 => "16 MHz OFDM", + ChannelWidth::Width320 => "320 MHz", ChannelWidth::Unknown => "Unknown channel width", }; write!(f, "{channel_width}") diff --git a/src/netlink.rs b/src/netlink.rs index c605a3e..12acb0e 100644 --- a/src/netlink.rs +++ b/src/netlink.rs @@ -16,9 +16,10 @@ use crate::attributes::MonitorFlags; use crate::reg_domain::RegulatoryDomain; use crate::station::WirelessStation; use crate::wiphy::PhysicalDevice; -use crate::InterfaceType; +use crate::{ChannelWidth, InterfaceType}; use super::attributes::Attribute; +use super::attributes::ChannelWidth as NlChannelWidth; use super::attributes::InterfaceType as NlInterfaceType; use super::commands::Command; use super::interface::WirelessInterface; @@ -122,6 +123,45 @@ impl NlSocket { Ok(()) } + pub fn set_channel( + &mut self, + if_index: u32, + freq: u32, + width: ChannelWidth, + ) -> Result<(), NlError> { + let attrs = { + let mut attrs = GenlBuffer::new(); + attrs.push(Nlattr::new(false, false, Attribute::Ifindex, if_index).unwrap()); + attrs.push(Nlattr::new(false, false, Attribute::WiphyFreq, freq).unwrap()); + attrs.push( + Nlattr::new( + false, + false, + Attribute::ChannelWidth, + Into::::into(width), + ) + .unwrap(), + ); + attrs + }; + let nl_payload = + Genlmsghdr::::new(Command::SetChannel, NL80211_VERSION, attrs); + let msg = self.build_header(nl_payload, &[NlmF::Request, NlmF::Ack]); + + self.send(msg)?; + for response in self.socket.iter::(false) { + let response = response.map_err(NlError::new)?; + match response.nl_payload { + NlPayload::Err(e) => { + error!("Error when reading response: {e}"); + break; + } + NlPayload::Payload(_) | NlPayload::Empty | NlPayload::Ack(_) => (), + }; + } + Ok(()) + } + pub fn list_stations(&mut self, if_index: u32) -> Result, NlError> { let attrs = { let mut attrs = GenlBuffer::new();