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}