ethernet_info/
lib.rs

1//! The crate provides a way to get the link information of the interface, including
2//! the port type, supported modes.
3//! The crate is based on the ioctl command, so it can only be used on Linux.
4//!
5//! Examples
6//! ----------------
7//! List all the interfaces' ethtool related information.
8//! ```
9//! use ethernet_info::get_ethernet_info;
10//! let interfaces_eth_info = get_ethernet_info(None);
11//! for interface_info in interfaces_eth_info {
12//!     println!("interface: {}", interface_info.name());
13//!     println!("Port: {}", interface_info.port());
14//!     println!("Supported Ports: {:?}", interface_info.ports());
15//!     println!("Supported: {:?}", interface_info.supported());
16//! }
17//! ```
18//!
19//! Get the ethtool related information of the specified interface.
20//! ```
21//! use ethernet_info::get_ethernet_info;
22//! let interfaces_eth_info = get_ethernet_info(Some("enp1s0"));
23//! for interface_info in interfaces_eth_info {
24//!     println!("interface: {}", interface_info.name());
25//!     println!("Port: {}", interface_info.port());
26//!     println!("Supported Ports: {:?}", interface_info.ports());
27//!     println!("Supported: {:?}", interface_info.supported());
28//! }
29//! ```
30//!
31//! Get the ethtool related of the specified interface by EthernetInfo.
32//! ```
33//! use ethernet_info::EthernetInfo;
34//! if let Ok(interface_info) = EthernetInfo::try_from("enp1s0") {
35//!     println!("interface: {}", interface_info.name());
36//!     println!("Port: {}", interface_info.port());
37//!     println!("Supported Ports: {:?}", interface_info.ports());
38//!     println!("Supported: {:?}", interface_info.supported());
39//! }
40//! ```
41#![allow(non_upper_case_globals)]
42
43#[macro_use]
44extern crate nix;
45
46mod errors;
47mod ethtool_const;
48mod internal;
49mod settings_parser;
50
51use crate::ethtool_const::*;
52use crate::settings_parser::SettingsParser;
53use internal::{CmdContext, EthtoolCommnad};
54
55pub use errors::EthtoolError;
56
57/// The port information includes the port type, supported modes.
58#[derive(Debug, Clone)]
59pub struct EthernetInfo {
60    name: String,
61    port: EthtoolPort,
62    ports: Vec<EthtoolPortBits>,
63    supported: Vec<EthtoolLinkModeBits>,
64    advertised: Vec<EthtoolLinkModeBits>,
65}
66
67impl EthernetInfo {
68    /// Create a EthernetInfo from the SettingsParser
69    pub fn from_settings_parser(name: &str, settings_parser: SettingsParser) -> Self {
70        let supported = settings_parser.supported_link_modes();
71        let advertised = settings_parser.advertised_link_modes();
72        EthernetInfo {
73            name: name.to_string(),
74            port: settings_parser.port(),
75            ports: settings_parser.supported_ports(),
76            advertised,
77            supported,
78        }
79    }
80
81    /// Get the name of the interface
82    pub fn name(&self) -> &str {
83        &self.name
84    }
85
86    /// Get the port type of the interface
87    pub fn port(&self) -> EthtoolPort {
88        self.port
89    }
90
91    pub fn ports(&self) -> &Vec<EthtoolPortBits> {
92        &self.ports
93    }
94
95    /// Get the supported modes of the interface
96    pub fn supported(&self) -> &Vec<EthtoolLinkModeBits> {
97        &self.supported
98    }
99
100    pub fn advertised(&self) -> &Vec<EthtoolLinkModeBits> {
101        &self.advertised
102    }
103}
104
105impl TryFrom<&str> for EthernetInfo {
106    type Error = EthtoolError;
107
108    /// Create a EthernetInfo from the interface name
109    fn try_from(name: &str) -> Result<Self, Self::Error> {
110        let ctx = CmdContext::new(name)?;
111        let ethernet_info = do_ioctl_get_ethernet_info(ctx)?;
112        Ok(ethernet_info)
113    }
114}
115
116/// Use ioctl to get the port information of the interface
117///
118/// The main steps are defined in do_ioctl_ethtool_glinksettings() in ethtool.c
119fn do_ioctl_get_ethernet_info(mut ctx: CmdContext) -> Result<EthernetInfo, EthtoolError> {
120    let mut ecmd = EthtoolCommnad::new(ETHTOOL_GLINKSETTINGS)?;
121    /* Handshake with kernel to determine number of words for link
122     * mode bitmaps. When requested number of bitmap words is not
123     * the one expected by kernel, the latter returns the integer
124     * opposite of what it is expecting. We request length 0 below
125     * (aka. invalid bitmap length) to get this info.
126     */
127    ecmd = ctx.get_ethtool_link_settings(ecmd)?;
128    if ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
129        return Err(EthtoolError::new(
130            "Failed to determine number of words for link mode bitmaps",
131        ));
132    }
133
134    /* got the real ecmd.req.link_mode_masks_nwords,
135     * now send the real request
136     */
137    ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
138    ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
139    ecmd = ctx.get_ethtool_link_settings(ecmd)?;
140
141    /* check the link_mode_masks_nwords again */
142    if ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
143        return Err(EthtoolError::new(
144            "Failed to check the link_mode_masks_nwords.",
145        ));
146    }
147
148    ctx.close_socket();
149
150    Ok(ecmd.into_ethernet_info(ctx.ifname()))
151}
152
153/// Get the ethtool related information of the interface
154/// If devname is None, get all the interfaces' ethtool related information.
155/// If devname is Some(&str), get the specified interface's ethtool related information.
156pub fn get_ethernet_info(devname: Option<&str>) -> Vec<EthernetInfo> {
157    let mut ethernet_info_vec = Vec::new();
158    if let Some(devname) = devname {
159        let ethernet_info = EthernetInfo::try_from(devname);
160        if let Ok(ethernet_info) = ethernet_info {
161            ethernet_info_vec.push(ethernet_info);
162        }
163    } else if let Ok(interfaces) = nix::net::if_::if_nameindex() {
164        for iface in interfaces.iter() {
165            if let Ok(ethernet_info) =
166                EthernetInfo::try_from(iface.name().to_string_lossy().as_ref())
167            {
168                ethernet_info_vec.push(ethernet_info);
169            }
170        }
171    }
172    ethernet_info_vec
173}