#![allow(non_upper_case_globals)]
#[macro_use]
extern crate nix;
mod errors;
mod ethtool_const;
mod internal;
mod settings_parser;
use crate::ethtool_const::*;
use crate::settings_parser::SettingsParser;
use internal::{CmdContext, EthtoolCommnad};
pub use errors::EthtoolError;
#[derive(Debug, Clone)]
pub struct EthernetInfo {
name: String,
port: EthtoolPort,
ports: Vec<EthtoolPortBits>,
supported: Vec<EthtoolLinkModeBits>,
advertised: Vec<EthtoolLinkModeBits>,
}
impl EthernetInfo {
pub fn from_settings_parser(name: &str, settings_parser: SettingsParser) -> Self {
let supported = settings_parser.supported_link_modes();
let advertised = settings_parser.advertised_link_modes();
EthernetInfo {
name: name.to_string(),
port: settings_parser.port(),
ports: settings_parser.supported_ports(),
advertised,
supported,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn port(&self) -> EthtoolPort {
self.port
}
pub fn ports(&self) -> &Vec<EthtoolPortBits> {
&self.ports
}
pub fn supported(&self) -> &Vec<EthtoolLinkModeBits> {
&self.supported
}
pub fn advertised(&self) -> &Vec<EthtoolLinkModeBits> {
&self.advertised
}
}
impl TryFrom<&str> for EthernetInfo {
type Error = EthtoolError;
fn try_from(name: &str) -> Result<Self, Self::Error> {
let ctx = CmdContext::new(name)?;
let ethernet_info = do_ioctl_get_ethernet_info(ctx)?;
Ok(ethernet_info)
}
}
fn do_ioctl_get_ethernet_info(mut ctx: CmdContext) -> Result<EthernetInfo, EthtoolError> {
let mut ecmd = EthtoolCommnad::new(ETHTOOL_GLINKSETTINGS)?;
ecmd = ctx.get_ethtool_link_settings(ecmd)?;
if ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
return Err(EthtoolError::new(
"Failed to determine number of words for link mode bitmaps",
));
}
ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
ecmd = ctx.get_ethtool_link_settings(ecmd)?;
if ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
return Err(EthtoolError::new(
"Failed to check the link_mode_masks_nwords.",
));
}
ctx.close_socket();
Ok(ecmd.into_ethernet_info(ctx.ifname()))
}
pub fn get_ethernet_info(devname: Option<&str>) -> Vec<EthernetInfo> {
let mut ethernet_info_vec = Vec::new();
if let Some(devname) = devname {
let ethernet_info = EthernetInfo::try_from(devname);
if let Ok(ethernet_info) = ethernet_info {
ethernet_info_vec.push(ethernet_info);
}
} else if let Ok(interfaces) = nix::net::if_::if_nameindex() {
for iface in interfaces.iter() {
if let Ok(ethernet_info) =
EthernetInfo::try_from(iface.name().to_string_lossy().as_ref())
{
ethernet_info_vec.push(ethernet_info);
}
}
}
ethernet_info_vec
}