Skip to content

Commit

Permalink
Add set channel feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ollipa committed Aug 9, 2023
1 parent eb0cb83 commit 59261b5
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 37 deletions.
24 changes: 24 additions & 0 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
8 changes: 8 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
78 changes: 42 additions & 36 deletions src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<u32>,
/// Wireless channel width.
pub channel_width: Option<ChannelWidth>,
pub channel_width: ChannelWidth,
/// Transmit power level (s16) in dBm.
pub tx_power: Option<u32>,
/// Wireless device identifier, used for pseudo-devices that don't have a netdev.
Expand Down Expand Up @@ -83,8 +84,8 @@ impl TryFrom<Attrs<'_, Attribute>> 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()?);
Expand Down Expand Up @@ -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,
Expand All @@ -356,47 +357,51 @@ pub enum ChannelWidth {
Width4,
Width8,
Width16,
Width320,
#[default]
Unknown,
}

impl From<u32> for ChannelWidth {
fn from(attr_channel_width: u32) -> Self {
impl From<ChannelWidth> 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<ChannelWidth> for u32 {
fn from(attr_channel_width: ChannelWidth) -> Self {
impl From<NlChannelWidth> 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,
}
}
}
Expand All @@ -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}")
Expand Down
42 changes: 41 additions & 1 deletion src/netlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<NlChannelWidth>::into(width),
)
.unwrap(),
);
attrs
};
let nl_payload =
Genlmsghdr::<Command, Attribute>::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::<Nlmsg, Neli80211Header>(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<Vec<WirelessStation>, NlError> {
let attrs = {
let mut attrs = GenlBuffer::new();
Expand Down

0 comments on commit 59261b5

Please sign in to comment.