Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
ethtool: Add link mode support
Browse files Browse the repository at this point in the history
Some information of `ethtool <nic_name>` is from link mode, like link
speed, duplex mode and etc.

Example code and integration test case included.

Signed-off-by: Gris Ge <cnfourt@gmail.com>
  • Loading branch information
cathay4t committed Sep 26, 2021
1 parent 2b79197 commit bc43fd6
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 55 deletions.
29 changes: 29 additions & 0 deletions ethtool/examples/dump_link_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use ethtool;
use futures::stream::TryStreamExt;
use tokio;

// Once we find a way to load netsimdev kernel module in CI, we can convert this
// to a test
fn main() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap();
rt.block_on(get_link_mode(None));
}

async fn get_link_mode(iface_name: Option<&str>) {
let (connection, mut handle, _) = ethtool::new_connection().unwrap();
tokio::spawn(connection);

let mut link_mode_handle = handle.link_mode().get(iface_name).execute().await;

let mut msgs = Vec::new();
while let Some(msg) = link_mode_handle.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(msgs.len() > 0);
for msg in msgs {
println!("{:?}", msg);
}
}
28 changes: 3 additions & 25 deletions ethtool/src/feature/get.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use futures::{self, future::Either, FutureExt, StreamExt, TryStream};
use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST};
use futures::TryStream;
use netlink_packet_generic::GenlMessage;

use crate::{try_ethtool, EthtoolError, EthtoolHandle, EthtoolMessage};
use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage};

pub struct EthtoolFeatureGetRequest {
handle: EthtoolHandle,
Expand All @@ -25,28 +24,7 @@ impl EthtoolFeatureGetRequest {
iface_name,
} = self;

let nl_header_flags = match iface_name {
// The NLM_F_ACK is required due to bug of kernel:
// https://bugzilla.redhat.com/show_bug.cgi?id=1953847
// without `NLM_F_MULTI`, rust-netlink will not parse
// multiple netlink message in single socket reply.
// Using NLM_F_ACK will force rust-netlink to parse all till
// acked at the end.
None => NLM_F_DUMP | NLM_F_REQUEST | NLM_F_ACK,
Some(_) => NLM_F_REQUEST,
};

let ethtool_msg = EthtoolMessage::new_feature_get(iface_name.as_deref());

let mut nl_msg = NetlinkMessage::from(GenlMessage::from_payload(ethtool_msg));

nl_msg.header.flags = nl_header_flags;

match handle.request(nl_msg).await {
Ok(response) => Either::Left(response.map(move |msg| Ok(try_ethtool!(msg)))),
Err(e) => Either::Right(
futures::future::err::<GenlMessage<EthtoolMessage>, EthtoolError>(e).into_stream(),
),
}
ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await
}
}
46 changes: 43 additions & 3 deletions ethtool/src/handle.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use futures::Stream;
use futures::{future::Either, FutureExt, Stream, StreamExt, TryStream};
use genetlink::GenetlinkHandle;
use netlink_packet_core::NetlinkMessage;
use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST};
use netlink_packet_generic::GenlMessage;
use netlink_packet_utils::DecodeError;

use crate::{EthtoolError, EthtoolFeatureHandle, EthtoolMessage, EthtoolPauseHandle};
use crate::{
try_ethtool,
EthtoolError,
EthtoolFeatureHandle,
EthtoolLinkModeHandle,
EthtoolMessage,
EthtoolPauseHandle,
};

#[derive(Clone, Debug)]
pub struct EthtoolHandle {
Expand All @@ -24,6 +31,10 @@ impl EthtoolHandle {
EthtoolFeatureHandle::new(self.clone())
}

pub fn link_mode(&mut self) -> EthtoolLinkModeHandle {
EthtoolLinkModeHandle::new(self.clone())
}

pub async fn request(
&mut self,
message: NetlinkMessage<GenlMessage<EthtoolMessage>>,
Expand All @@ -37,3 +48,32 @@ impl EthtoolHandle {
.map_err(|e| EthtoolError::RequestFailed(format!("BUG: Request failed with {}", e)))
}
}

pub(crate) async fn ethtool_execute(
handle: &mut EthtoolHandle,
is_dump: bool,
ethtool_msg: EthtoolMessage,
) -> impl TryStream<Ok = GenlMessage<EthtoolMessage>, Error = EthtoolError> {
let nl_header_flags = if is_dump {
// The NLM_F_ACK is required due to bug of kernel:
// https://bugzilla.redhat.com/show_bug.cgi?id=1953847
// without `NLM_F_MULTI`, rust-netlink will not parse
// multiple netlink message in single socket reply.
// Using NLM_F_ACK will force rust-netlink to parse all till
// acked at the end.
NLM_F_DUMP | NLM_F_REQUEST | NLM_F_ACK
} else {
NLM_F_REQUEST
};

let mut nl_msg = NetlinkMessage::from(GenlMessage::from_payload(ethtool_msg));

nl_msg.header.flags = nl_header_flags;

match handle.request(nl_msg).await {
Ok(response) => Either::Left(response.map(move |msg| Ok(try_ethtool!(msg)))),
Err(e) => Either::Right(
futures::future::err::<GenlMessage<EthtoolMessage>, EthtoolError>(e).into_stream(),
),
}
}
4 changes: 2 additions & 2 deletions ethtool/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ffi::CString;
use anyhow::Context;
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
nla::{self, DefaultNla, NlaBuffer},
nla::{DefaultNla, Nla, NlaBuffer},
parsers::{parse_string, parse_u32},
DecodeError,
Parseable,
Expand All @@ -22,7 +22,7 @@ pub enum EthtoolHeader {
Other(DefaultNla),
}

impl nla::Nla for EthtoolHeader {
impl Nla for EthtoolHeader {
fn value_len(&self) -> usize {
match self {
Self::DevIndex(_) | Self::Flags(_) => 4,
Expand Down
9 changes: 9 additions & 0 deletions ethtool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod error;
mod feature;
mod handle;
mod header;
mod link_mode;
mod macros;
mod message;
mod pause;
Expand All @@ -17,10 +18,18 @@ pub use feature::{
};
pub use handle::EthtoolHandle;
pub use header::EthtoolHeader;
pub use link_mode::{
EthtoolLinkModeAttr,
EthtoolLinkModeDuplex,
EthtoolLinkModeGetRequest,
EthtoolLinkModeHandle,
};
pub use message::{EthtoolAttr, EthtoolCmd, EthtoolMessage};
pub use pause::{
EthtoolPauseAttr,
EthtoolPauseGetRequest,
EthtoolPauseHandle,
EthtoolPauseStatAttr,
};

pub(crate) use handle::ethtool_execute;
Loading

0 comments on commit bc43fd6

Please sign in to comment.