nic_port_info/lib.rs
1//! The crate provides a way to get the link information of the NIC, 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//! List all the NICs' port information.
7//! ```
8//! use ethtool::get_nic_port_info;
9//! let nics_port = get_nic_port_info(None);
10//! for nic_port in nics_port {
11//! println!("NIC: {}", nic_port.name());
12//! println!("Port: {}", nic_port.port());
13//! println!("Supported: {:?}", nic_port.supported());
14//! }
15//! ```
16//!
17//! Get the port information of the specified NIC.
18//! ```
19//! use ethtool::get_nic_port_info;
20//! let nics_port = get_nic_port_info(Some("eth0"));
21//! for nic_port in nics_port {
22//! println!("NIC: {}", nic_port.name());
23//! println!("Port: {}", nic_port.port());
24//! println!("Supported: {:?}", nic_port.supported());
25//! }
26//! ```
27//!
28//! Get the port information of the specified NIC by PortInfo.
29//! ```
30//! use ethtool::PortInfo;
31//! let nic_port = PortInfo::from_name("eth0").unwrap();
32//! println!("NIC: {}", nic_port.name());
33//! println!("Port: {}", nic_port.port());
34//! println!("Supported: {:?}", nic_port.supported());
35//! ```
36#![allow(non_upper_case_globals)]
37
38#[macro_use]
39extern crate nix;
40
41mod errors;
42mod ethtool_const;
43mod internal;
44mod settings_parser;
45
46use crate::ethtool_const::*;
47use crate::settings_parser::SettingsParser;
48use internal::{CmdContext, EthtoolCommnad};
49
50pub use errors::EthtoolError;
51
52/// The port information includes the port type, supported modes.
53#[derive(Default, Debug, Clone)]
54pub struct PortInfo {
55 name: String,
56 port: String,
57 supported: Vec<String>,
58}
59
60impl PortInfo {
61 /// Create a PortInfo from the SettingsParser
62 pub fn from_settings_parser(name: &str, settings_parser: SettingsParser) -> Self {
63 let supported = settings_parser.supported_link_modes();
64 PortInfo {
65 name: name.to_string(),
66 port: settings_parser.port(),
67 supported,
68 }
69 }
70
71 /// Get the name of the NIC
72 pub fn name(&self) -> &str {
73 &self.name
74 }
75
76 /// Get the port type of the NIC
77 pub fn port(&self) -> &str {
78 &self.port
79 }
80
81 /// Get the supported modes of the NIC
82 pub fn supported(&self) -> &Vec<String> {
83 &self.supported
84 }
85
86 /// Create a PortInfo from the NIC name
87 pub fn from_name(name: &str) -> Result<Self, EthtoolError> {
88 let ctx = CmdContext::new(name)?;
89 let port = do_ioctl_get_nic_port(ctx)?;
90 Ok(port)
91 }
92}
93
94
95/// Use ioctl to get the port information of the NIC
96///
97/// The main steps are defined in do_ioctl_ethtool_glinksettings() in ethtool.c
98fn do_ioctl_get_nic_port(mut ctx: CmdContext) -> Result<PortInfo, EthtoolError> {
99 let mut ecmd = EthtoolCommnad::new(ETHTOOL_GLINKSETTINGS)?;
100 /* Handshake with kernel to determine number of words for link
101 * mode bitmaps. When requested number of bitmap words is not
102 * the one expected by kernel, the latter returns the integer
103 * opposite of what it is expecting. We request length 0 below
104 * (aka. invalid bitmap length) to get this info.
105 */
106 ctx = ctx.send_ioctl(ecmd)?;
107 ecmd = ctx.get_ethtool_link_settings();
108 if ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
109 return Err(EthtoolError::new(
110 "Failed to determine number of words for link mode bitmaps",
111 ));
112 }
113
114 /* got the real ecmd.req.link_mode_masks_nwords,
115 * now send the real request
116 */
117 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
118 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
119 ctx = ctx.send_ioctl(ecmd)?;
120 ecmd = ctx.get_ethtool_link_settings();
121
122 /* check the link_mode_masks_nwords again */
123 if ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS {
124 return Err(EthtoolError::new(
125 "Failed to check the link_mode_masks_nwords.",
126 ));
127 }
128
129 ctx.close_socket();
130
131 Ok(ecmd.into_port(ctx.ifname()))
132}
133
134
135/// Get the port information of the NIC
136/// If devname is None, get all the NICs' port information.
137/// If devname is Some(&str), get the specified NIC's port information.
138pub fn get_nic_port_info(devname: Option<&str>) -> Vec<PortInfo> {
139 let mut nics_port = Vec::new();
140 if let Some(devname) = devname {
141 let port_info = PortInfo::from_name(devname);
142 if let Ok(port_info) = port_info {
143 nics_port.push(port_info);
144 }
145 } else {
146 let mut add_ports = Vec::new();
147 if let Ok(ifaddrs) = nix::ifaddrs::getifaddrs() {
148 ifaddrs.for_each(|ifaddr| {
149 if let Ok(nic_port) = PortInfo::from_name(&ifaddr.interface_name) {
150 if !add_ports.contains(&nic_port.name) {
151 add_ports.push(nic_port.name.clone());
152 nics_port.push(nic_port);
153 }
154 }
155 });
156 }
157 }
158 nics_port
159}