From 9204410b2f946d137977f0d9de5464ebd66acbb8 Mon Sep 17 00:00:00 2001 From: laundrei Date: Wed, 25 Feb 2026 18:07:53 -0500 Subject: [PATCH 1/3] Add support for get_link_state following the example of the other get commands and the documentation here https://docs.kernel.org/networking/ethtool-netlink.html#linkstate-get tested with cargo run --example dump_link_state --- examples/dump_link_state.rs | 33 ++++++++++ src/handle.rs | 8 ++- src/lib.rs | 5 ++ src/link_state/attr.rs | 127 ++++++++++++++++++++++++++++++++++++ src/link_state/get.rs | 39 +++++++++++ src/link_state/handle.rs | 19 ++++++ src/link_state/mod.rs | 11 ++++ src/message.rs | 36 +++++++++- 8 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 examples/dump_link_state.rs create mode 100644 src/link_state/attr.rs create mode 100644 src/link_state/get.rs create mode 100644 src/link_state/handle.rs create mode 100644 src/link_state/mod.rs diff --git a/examples/dump_link_state.rs b/examples/dump_link_state.rs new file mode 100644 index 0000000..6978efa --- /dev/null +++ b/examples/dump_link_state.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +use futures_util::stream::StreamExt; + +fn main() { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); + let iface_name = std::env::args().nth(1); + rt.block_on(get_link_state(iface_name.as_deref())); +} + +async fn get_link_state(iface_name: Option<&str>) { + let (connection, mut handle, _) = ethtool::new_connection().unwrap(); + tokio::spawn(connection); + + let mut link_state_handle = handle + .link_state() + .get(iface_name) + .execute() + .await + .unwrap(); + + let mut msgs = Vec::new(); + while let Some(Ok(msg)) = link_state_handle.next().await { + msgs.push(msg); + } + assert!(!msgs.is_empty()); + for msg in msgs { + println!("{:?}", msg); + } +} diff --git a/src/handle.rs b/src/handle.rs index 95267f9..8c5c6b3 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -11,8 +11,8 @@ use netlink_packet_generic::GenlMessage; use crate::{ try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError, EthtoolFeatureHandle, EthtoolFecHandle, EthtoolLinkModeHandle, - EthtoolMessage, EthtoolModuleEEPROMHandle, EthtoolPauseHandle, - EthtoolRingHandle, EthtoolTsInfoHandle, + EthtoolLinkStateHandle, EthtoolMessage, EthtoolModuleEEPROMHandle, + EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle, }; #[derive(Clone, Debug)] @@ -61,6 +61,10 @@ impl EthtoolHandle { EthtoolModuleEEPROMHandle::new(self.clone()) } + pub fn link_state(&mut self) -> EthtoolLinkStateHandle { + EthtoolLinkStateHandle::new(self.clone()) + } + pub async fn request( &mut self, message: NetlinkMessage>, diff --git a/src/lib.rs b/src/lib.rs index bd03001..01ca157 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ mod fec; mod handle; mod header; mod link_mode; +mod link_state; mod macros; mod message; mod pause; @@ -46,6 +47,10 @@ pub use link_mode::{ EthtoolLinkModeAttr, EthtoolLinkModeDuplex, EthtoolLinkModeGetRequest, EthtoolLinkModeHandle, }; +pub use link_state::{ + EthtoolLinkStateAttr, EthtoolLinkStateGetRequest, + EthtoolLinkStateHandle, +}; pub use message::{EthtoolAttr, EthtoolCmd, EthtoolMessage}; pub use pause::{ EthtoolPauseAttr, EthtoolPauseGetRequest, EthtoolPauseHandle, diff --git a/src/link_state/attr.rs b/src/link_state/attr.rs new file mode 100644 index 0000000..1566dfc --- /dev/null +++ b/src/link_state/attr.rs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_core::{ + parse_u32, parse_u8, DecodeError, DefaultNla, Emitable, ErrorContext, + Nla, NlaBuffer, NlasIterator, Parseable, NLA_F_NESTED, +}; + +use crate::{EthtoolAttr, EthtoolHeader}; + +const ETHTOOL_A_LINKSTATE_HEADER: u16 = 1; +const ETHTOOL_A_LINKSTATE_LINK: u16 = 2; +const ETHTOOL_A_LINKSTATE_SQI: u16 = 3; +const ETHTOOL_A_LINKSTATE_SQI_MAX: u16 = 4; +const ETHTOOL_A_LINKSTATE_EXT_STATE: u16 = 5; +const ETHTOOL_A_LINKSTATE_EXT_SUBSTATE: u16 = 6; +const ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT: u16 = 7; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum EthtoolLinkStateAttr { + Header(Vec), + Link(bool), + Sqi(u32), + SqiMax(u32), + ExtState(u8), + ExtSubstate(u8), + ExtDownCnt(u32), + Other(DefaultNla), +} + +impl Nla for EthtoolLinkStateAttr { + fn value_len(&self) -> usize { + match self { + Self::Header(hdrs) => hdrs.as_slice().buffer_len(), + Self::Link(_) | Self::ExtState(_) | Self::ExtSubstate(_) => 1, + Self::Sqi(_) | Self::SqiMax(_) | Self::ExtDownCnt(_) => 4, + Self::Other(attr) => attr.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Header(_) => ETHTOOL_A_LINKSTATE_HEADER | NLA_F_NESTED, + Self::Link(_) => ETHTOOL_A_LINKSTATE_LINK, + Self::Sqi(_) => ETHTOOL_A_LINKSTATE_SQI, + Self::SqiMax(_) => ETHTOOL_A_LINKSTATE_SQI_MAX, + Self::ExtState(_) => ETHTOOL_A_LINKSTATE_EXT_STATE, + Self::ExtSubstate(_) => ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, + Self::ExtDownCnt(_) => ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT, + Self::Other(attr) => attr.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Header(ref nlas) => nlas.as_slice().emit(buffer), + Self::Other(ref attr) => attr.emit(buffer), + _ => todo!("Does not support changing link state"), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for EthtoolLinkStateAttr +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + ETHTOOL_A_LINKSTATE_HEADER => { + let mut nlas = Vec::new(); + let error_msg = + "failed to parse link state header attributes"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(error_msg)?; + let parsed = + EthtoolHeader::parse(nla).context(error_msg)?; + nlas.push(parsed); + } + Self::Header(nlas) + } + ETHTOOL_A_LINKSTATE_LINK => Self::Link( + parse_u8(payload) + .context("invalid ETHTOOL_A_LINKSTATE_LINK value")? + == 1, + ), + ETHTOOL_A_LINKSTATE_SQI => Self::Sqi( + parse_u32(payload) + .context("invalid ETHTOOL_A_LINKSTATE_SQI value")?, + ), + ETHTOOL_A_LINKSTATE_SQI_MAX => Self::SqiMax( + parse_u32(payload) + .context("invalid ETHTOOL_A_LINKSTATE_SQI_MAX value")?, + ), + ETHTOOL_A_LINKSTATE_EXT_STATE => Self::ExtState( + parse_u8(payload) + .context("invalid ETHTOOL_A_LINKSTATE_EXT_STATE value")?, + ), + ETHTOOL_A_LINKSTATE_EXT_SUBSTATE => Self::ExtSubstate( + parse_u8(payload).context( + "invalid ETHTOOL_A_LINKSTATE_EXT_SUBSTATE value", + )?, + ), + ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT => Self::ExtDownCnt( + parse_u32(payload).context( + "invalid ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT value", + )?, + ), + _ => Self::Other( + DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + ), + }) + } +} + +pub(crate) fn parse_link_state_nlas( + buffer: &[u8], +) -> Result, DecodeError> { + let mut nlas = Vec::new(); + for nla in NlasIterator::new(buffer) { + let error_msg = format!( + "Failed to parse ethtool link state message attribute {nla:?}" + ); + let nla = &nla.context(error_msg.clone())?; + let parsed = EthtoolLinkStateAttr::parse(nla).context(error_msg)?; + nlas.push(EthtoolAttr::LinkState(parsed)); + } + Ok(nlas) +} diff --git a/src/link_state/get.rs b/src/link_state/get.rs new file mode 100644 index 0000000..1338d8a --- /dev/null +++ b/src/link_state/get.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +use futures_util::Stream; +use netlink_packet_generic::GenlMessage; + +use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; + +pub struct EthtoolLinkStateGetRequest { + handle: EthtoolHandle, + iface_name: Option, +} + +impl EthtoolLinkStateGetRequest { + pub(crate) fn new( + handle: EthtoolHandle, + iface_name: Option<&str>, + ) -> Self { + EthtoolLinkStateGetRequest { + handle, + iface_name: iface_name.map(|i| i.to_string()), + } + } + + pub async fn execute( + self, + ) -> Result< + impl Stream, EthtoolError>>, + EthtoolError, + > { + let EthtoolLinkStateGetRequest { + mut handle, + iface_name, + } = self; + + let ethtool_msg = + EthtoolMessage::new_link_state_get(iface_name.as_deref()); + ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await + } +} diff --git a/src/link_state/handle.rs b/src/link_state/handle.rs new file mode 100644 index 0000000..cc30f43 --- /dev/null +++ b/src/link_state/handle.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +use crate::{EthtoolHandle, EthtoolLinkStateGetRequest}; + +pub struct EthtoolLinkStateHandle(EthtoolHandle); + +impl EthtoolLinkStateHandle { + pub fn new(handle: EthtoolHandle) -> Self { + EthtoolLinkStateHandle(handle) + } + + /// Retrieve the current link state of an interface + pub fn get( + &mut self, + iface_name: Option<&str>, + ) -> EthtoolLinkStateGetRequest { + EthtoolLinkStateGetRequest::new(self.0.clone(), iface_name) + } +} diff --git a/src/link_state/mod.rs b/src/link_state/mod.rs new file mode 100644 index 0000000..8850e09 --- /dev/null +++ b/src/link_state/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +mod attr; +mod get; +mod handle; + +pub(crate) use attr::parse_link_state_nlas; + +pub use attr::EthtoolLinkStateAttr; +pub use get::EthtoolLinkStateGetRequest; +pub use handle::EthtoolLinkStateHandle; diff --git a/src/message.rs b/src/message.rs index 6274cc7..ad50238 100644 --- a/src/message.rs +++ b/src/message.rs @@ -10,6 +10,7 @@ use crate::{ feature::{parse_feature_nlas, EthtoolFeatureAttr}, fec::{parse_fec_nlas, EthtoolFecAttr}, link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr}, + link_state::{parse_link_state_nlas, EthtoolLinkStateAttr}, pause::{parse_pause_nlas, EthtoolPauseAttr}, ring::{parse_ring_nlas, EthtoolRingAttr}, tsinfo::{parse_tsinfo_nlas, EthtoolTsInfoAttr}, @@ -33,6 +34,8 @@ const ETHTOOL_MSG_FEC_GET_REPLY: u8 = 30; const ETHTOOL_MSG_CHANNELS_GET: u8 = 17; const ETHTOOL_MSG_CHANNELS_GET_REPLY: u8 = 18; const ETHTOOL_MSG_CHANNELS_SET: u8 = 18; +const ETHTOOL_MSG_LINKSTATE_GET: u8 = 6; +const ETHTOOL_MSG_LINKSTATE_GET_REPLY: u8 = 6; const ETHTOOL_MSG_MODULE_EEPROM_GET: u8 = 31; const ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY: u8 = 32; @@ -55,6 +58,8 @@ pub enum EthtoolCmd { ChannelGet, ChannelGetReply, ChannelSet, + LinkStateGet, + LinkStateGetReply, ModuleEEPROMGet, ModuleEEPROMGetReply, } @@ -82,7 +87,9 @@ impl From for u8 { EthtoolCmd::ModuleEEPROMGet => ETHTOOL_MSG_MODULE_EEPROM_GET, EthtoolCmd::ModuleEEPROMGetReply => { ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY - } + }, + EthtoolCmd::LinkStateGet => ETHTOOL_MSG_LINKSTATE_GET, + EthtoolCmd::LinkStateGetReply => ETHTOOL_MSG_LINKSTATE_GET_REPLY } } } @@ -98,6 +105,7 @@ pub enum EthtoolAttr { Fec(EthtoolFecAttr), Channel(EthtoolChannelAttr), ModuleEEPROM(EthtoolModuleEEPROMAttr), + LinkState(EthtoolLinkStateAttr), } impl Nla for EthtoolAttr { @@ -112,6 +120,7 @@ impl Nla for EthtoolAttr { Self::Fec(attr) => attr.value_len(), Self::Channel(attr) => attr.value_len(), Self::ModuleEEPROM(attr) => attr.value_len(), + Self::LinkState(attr) => attr.value_len(), } } @@ -126,6 +135,7 @@ impl Nla for EthtoolAttr { Self::Fec(attr) => attr.kind(), Self::Channel(attr) => attr.kind(), Self::ModuleEEPROM(attr) => attr.kind(), + Self::LinkState(attr) => attr.kind(), } } @@ -140,6 +150,7 @@ impl Nla for EthtoolAttr { Self::Fec(attr) => attr.emit_value(buffer), Self::Channel(attr) => attr.emit_value(buffer), Self::ModuleEEPROM(attr) => attr.emit_value(buffer), + Self::LinkState(attr) => attr.emit_value(buffer), } } } @@ -349,6 +360,25 @@ impl EthtoolMessage { nlas, } } + + pub fn new_link_state_get(iface_name: Option<&str>) -> Self { + let nlas = match iface_name { + Some(s) => { + vec![EthtoolAttr::LinkState(EthtoolLinkStateAttr::Header( + vec![EthtoolHeader::DevName(s.to_string())], + ))] + } + None => { + vec![EthtoolAttr::LinkState(EthtoolLinkStateAttr::Header( + vec![], + ))] + } + }; + EthtoolMessage { + cmd: EthtoolCmd::LinkStateGet, + nlas, + } + } } impl Emitable for EthtoolMessage { @@ -403,6 +433,10 @@ impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage { cmd: EthtoolCmd::ModuleEEPROMGetReply, nlas: parse_module_eeprom_nlas(buffer)?, }, + ETHTOOL_MSG_LINKSTATE_GET_REPLY => Self { + cmd: EthtoolCmd::LinkStateGetReply, + nlas: parse_link_state_nlas(buffer)?, + }, cmd => { return Err(DecodeError::from(format!( "Unsupported ethtool reply command: {cmd}" From 50259a4816456e1f19b20cbbd5589a49b1e8d2b1 Mon Sep 17 00:00:00 2001 From: laundrei Date: Wed, 25 Feb 2026 21:11:12 -0500 Subject: [PATCH 2/3] Run cargo fmt --all to fix formatting --- examples/dump_link_state.rs | 8 ++------ src/lib.rs | 3 +-- src/link_state/attr.rs | 23 +++++++++++------------ src/link_state/get.rs | 5 +---- src/link_state/handle.rs | 2 +- src/message.rs | 4 ++-- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/examples/dump_link_state.rs b/examples/dump_link_state.rs index 6978efa..bb49b37 100644 --- a/examples/dump_link_state.rs +++ b/examples/dump_link_state.rs @@ -15,12 +15,8 @@ async fn get_link_state(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); - let mut link_state_handle = handle - .link_state() - .get(iface_name) - .execute() - .await - .unwrap(); + let mut link_state_handle = + handle.link_state().get(iface_name).execute().await.unwrap(); let mut msgs = Vec::new(); while let Some(Ok(msg)) = link_state_handle.next().await { diff --git a/src/lib.rs b/src/lib.rs index 01ca157..321077f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,8 +48,7 @@ pub use link_mode::{ EthtoolLinkModeHandle, }; pub use link_state::{ - EthtoolLinkStateAttr, EthtoolLinkStateGetRequest, - EthtoolLinkStateHandle, + EthtoolLinkStateAttr, EthtoolLinkStateGetRequest, EthtoolLinkStateHandle, }; pub use message::{EthtoolAttr, EthtoolCmd, EthtoolMessage}; pub use pause::{ diff --git a/src/link_state/attr.rs b/src/link_state/attr.rs index 1566dfc..af35a7b 100644 --- a/src/link_state/attr.rs +++ b/src/link_state/attr.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT use netlink_packet_core::{ - parse_u32, parse_u8, DecodeError, DefaultNla, Emitable, ErrorContext, - Nla, NlaBuffer, NlasIterator, Parseable, NLA_F_NESTED, + parse_u32, parse_u8, DecodeError, DefaultNla, Emitable, ErrorContext, Nla, + NlaBuffer, NlasIterator, Parseable, NLA_F_NESTED, }; use crate::{EthtoolAttr, EthtoolHeader}; @@ -67,8 +67,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> Ok(match buf.kind() { ETHTOOL_A_LINKSTATE_HEADER => { let mut nlas = Vec::new(); - let error_msg = - "failed to parse link state header attributes"; + let error_msg = "failed to parse link state header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = @@ -94,16 +93,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> parse_u8(payload) .context("invalid ETHTOOL_A_LINKSTATE_EXT_STATE value")?, ), - ETHTOOL_A_LINKSTATE_EXT_SUBSTATE => Self::ExtSubstate( - parse_u8(payload).context( + ETHTOOL_A_LINKSTATE_EXT_SUBSTATE => { + Self::ExtSubstate(parse_u8(payload).context( "invalid ETHTOOL_A_LINKSTATE_EXT_SUBSTATE value", - )?, - ), - ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT => Self::ExtDownCnt( - parse_u32(payload).context( + )?) + } + ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT => { + Self::ExtDownCnt(parse_u32(payload).context( "invalid ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT value", - )?, - ), + )?) + } _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), diff --git a/src/link_state/get.rs b/src/link_state/get.rs index 1338d8a..08474be 100644 --- a/src/link_state/get.rs +++ b/src/link_state/get.rs @@ -11,10 +11,7 @@ pub struct EthtoolLinkStateGetRequest { } impl EthtoolLinkStateGetRequest { - pub(crate) fn new( - handle: EthtoolHandle, - iface_name: Option<&str>, - ) -> Self { + pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolLinkStateGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), diff --git a/src/link_state/handle.rs b/src/link_state/handle.rs index cc30f43..b74f548 100644 --- a/src/link_state/handle.rs +++ b/src/link_state/handle.rs @@ -9,7 +9,7 @@ impl EthtoolLinkStateHandle { EthtoolLinkStateHandle(handle) } - /// Retrieve the current link state of an interface + /// Retrieve the current link state of an interface pub fn get( &mut self, iface_name: Option<&str>, diff --git a/src/message.rs b/src/message.rs index ad50238..19b333f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -87,9 +87,9 @@ impl From for u8 { EthtoolCmd::ModuleEEPROMGet => ETHTOOL_MSG_MODULE_EEPROM_GET, EthtoolCmd::ModuleEEPROMGetReply => { ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY - }, + } EthtoolCmd::LinkStateGet => ETHTOOL_MSG_LINKSTATE_GET, - EthtoolCmd::LinkStateGetReply => ETHTOOL_MSG_LINKSTATE_GET_REPLY + EthtoolCmd::LinkStateGetReply => ETHTOOL_MSG_LINKSTATE_GET_REPLY, } } } From d9a220eb6445f9168281083a153c3610df372c8f Mon Sep 17 00:00:00 2001 From: laundrei Date: Tue, 10 Mar 2026 22:48:12 -0400 Subject: [PATCH 3/3] add support for setting the ethtool header. I need this to set ETHTOOL_FLAG_STATS to get additional stats from LINKSTATE_GET --- examples/dump_link_state.rs | 8 ++++++-- src/link_state/get.rs | 11 +++++++++-- src/link_state/handle.rs | 3 ++- src/message.rs | 26 +++++++++++++------------- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/examples/dump_link_state.rs b/examples/dump_link_state.rs index bb49b37..1b38b38 100644 --- a/examples/dump_link_state.rs +++ b/examples/dump_link_state.rs @@ -15,8 +15,12 @@ async fn get_link_state(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); - let mut link_state_handle = - handle.link_state().get(iface_name).execute().await.unwrap(); + let mut link_state_handle = handle + .link_state() + .get(iface_name, Some(0)) + .execute() + .await + .unwrap(); let mut msgs = Vec::new(); while let Some(Ok(msg)) = link_state_handle.next().await { diff --git a/src/link_state/get.rs b/src/link_state/get.rs index 08474be..b31eff1 100644 --- a/src/link_state/get.rs +++ b/src/link_state/get.rs @@ -8,13 +8,19 @@ use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolLinkStateGetRequest { handle: EthtoolHandle, iface_name: Option, + flags: Option, } impl EthtoolLinkStateGetRequest { - pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { + pub(crate) fn new( + handle: EthtoolHandle, + iface_name: Option<&str>, + flags: Option, + ) -> Self { EthtoolLinkStateGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), + flags, } } @@ -27,10 +33,11 @@ impl EthtoolLinkStateGetRequest { let EthtoolLinkStateGetRequest { mut handle, iface_name, + flags, } = self; let ethtool_msg = - EthtoolMessage::new_link_state_get(iface_name.as_deref()); + EthtoolMessage::new_link_state_get(iface_name.as_deref(), flags); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } diff --git a/src/link_state/handle.rs b/src/link_state/handle.rs index b74f548..0360a08 100644 --- a/src/link_state/handle.rs +++ b/src/link_state/handle.rs @@ -13,7 +13,8 @@ impl EthtoolLinkStateHandle { pub fn get( &mut self, iface_name: Option<&str>, + flags: Option, ) -> EthtoolLinkStateGetRequest { - EthtoolLinkStateGetRequest::new(self.0.clone(), iface_name) + EthtoolLinkStateGetRequest::new(self.0.clone(), iface_name, flags) } } diff --git a/src/message.rs b/src/message.rs index 19b333f..a4a0e5c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -361,19 +361,19 @@ impl EthtoolMessage { } } - pub fn new_link_state_get(iface_name: Option<&str>) -> Self { - let nlas = match iface_name { - Some(s) => { - vec![EthtoolAttr::LinkState(EthtoolLinkStateAttr::Header( - vec![EthtoolHeader::DevName(s.to_string())], - ))] - } - None => { - vec![EthtoolAttr::LinkState(EthtoolLinkStateAttr::Header( - vec![], - ))] - } - }; + pub fn new_link_state_get( + iface_name: Option<&str>, + flags: Option, + ) -> Self { + let mut header = vec![]; + if let Some(s) = iface_name { + header.push(EthtoolHeader::DevName(s.to_string())) + } + if let Some(f) = flags { + header.push(EthtoolHeader::Flags(f)) + } + let nlas = + vec![EthtoolAttr::LinkState(EthtoolLinkStateAttr::Header(header))]; EthtoolMessage { cmd: EthtoolCmd::LinkStateGet, nlas,