1use std::{ffi::{CStr, CString}, fmt::{Debug, Display}};
4
5pub use libc::IFNAMSIZ;
6
7type IfNameInner = [libc::c_char; IFNAMSIZ];
8
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
10pub struct IfName {
11 inner: IfNameInner,
12}
13
14impl IfName {
15 pub fn new<From: Into<Vec<u8>>>(name: From) -> std::io::Result<Self> {
16 let name = CString::new(name)
17 .map_err(|e| std::io::Error::other(e))?;
18
19 Self::from_c_str(name.as_ref())
20 }
21
22 pub fn from_c_str(name: &CStr) -> std::io::Result<Self> {
23 let name = name.to_bytes();
24 if name.len() >= IFNAMSIZ {
25 return Err(std::io::Error::other("Invalid interface name length"));
26 }
27
28 let mut ifname_buf: IfNameInner = [0; {IFNAMSIZ}];
29 for (i, c) in name.iter().enumerate() {
30 ifname_buf[i] = (*c) as libc::c_char;
31 }
32
33 Ok(Self {
34 inner: ifname_buf,
35 })
36 }
37
38 pub fn as_c_str<'a>(&'a self) -> &'a CStr {
39 unsafe { CStr::from_ptr(self.inner.as_ptr()) }
40 }
41}
42
43impl Display for IfName {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 let string = format!("{:?}", self.as_c_str());
46 let str = string.trim_matches('"');
47 f.write_str(str)
48 }
49}
50
51impl Debug for IfName {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.write_str(&format!("IfName({})", self))
54 }
55}
56
57#[cfg(all(feature = "libc", not(feature = "rtnetlink")))]
58pub fn index_to_name(index: InterfaceId) -> Result<IfName, std::io::Error> {
62 let index = if let Some(index) = index.inner() {
63 index
64 } else {
65 return Err(std::io::Error::new(
66 std::io::ErrorKind::InvalidInput,
67 "interface index is unspecified",
68 ));
69 };
70 let mut ifname_buf: IfNameInner = [0; {IFNAMSIZ}];
71 let ret = unsafe { libc::if_indextoname(index, ifname_buf.as_mut_ptr() as *mut libc::c_char) };
72 if ret.is_null() {
73 return Err(std::io::Error::last_os_error());
74 }
75
76 Ok(IfName { inner: ifname_buf })
77}
78
79#[cfg(feature = "rtnetlink")]
80pub fn index_to_name(index: InterfaceId) -> Result<IfName, std::io::Error> {
84 let index = if let Some(index) = index.inner() {
85 index
86 } else {
87 return Err(std::io::Error::new(
88 std::io::ErrorKind::InvalidInput,
89 "interface index is unspecified",
90 ));
91 };
92 let handle = ftth_rtnl::RtnlClient::new();
93 let link_handle = handle.link();
94 let interface = link_handle.interface_get(index as u32)?;
95 let name = IfName::new(interface.if_name)?;
96 Ok(name)
97}
98
99
100#[cfg(all(feature = "libc", not(feature = "rtnetlink")))]
101pub fn name_to_index(name: IfName) -> Result<InterfaceId, std::io::Error> {
105 let index = unsafe { libc::if_nametoindex(name.inner.as_ptr() as *const libc::c_char) };
106 if index == 0 {
107 return Err(std::io::Error::last_os_error());
108 }
109 Ok(InterfaceId::new(Some(index)))
110}
111
112#[cfg(feature = "rtnetlink")]
113pub fn name_to_index(name: IfName) -> Result<InterfaceId, std::io::Error> {
117 let name = name.to_string();
118 let handle = ftth_rtnl::RtnlClient::new();
119 let link_handle = handle.link();
120 let interface = link_handle.interface_get_by_name(&name)?;
121 Ok(InterfaceId::new(Some(interface.if_id as libc::c_uint)))
122}
123
124#[derive(Clone, Copy, PartialEq, Eq, Hash)]
125pub struct InterfaceId {
126 if_index: libc::c_uint,
127}
128
129impl InterfaceId {
130 pub const UNSPECIFIED: Self = Self { if_index: 0 };
131
132 pub fn new(if_index: Option<libc::c_uint>) -> Self {
133 Self { if_index: if_index.unwrap_or(0) }
134 }
135
136 pub fn inner(&self) -> Option<libc::c_uint> {
137 if self.if_index == 0 {
138 None
139 } else {
140 Some(self.if_index)
141 }
142 }
143
144 pub fn is_unspecified(&self) -> bool {
145 self.if_index == 0
146 }
147}
148
149impl Debug for InterfaceId {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 if let Some(id) = self.inner() {
152 f.write_str(&format!("InterfaceId({id})"))
153 } else {
154 f.write_str(&format!("InterfaceId(UNSPECIFIED)"))
155 }
156 }
157}
158
159impl Display for InterfaceId {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.write_str(&format!("{}", self.if_index))
162 }
163}
164
165impl Into<libc::c_uint> for InterfaceId {
166 fn into(self) -> libc::c_uint {
167 self.if_index
168 }
169}
170
171impl From<libc::c_uint> for InterfaceId {
172 fn from(value: libc::c_uint) -> Self {
173 Self {
174 if_index: value,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
181pub struct Interface {
182 if_id: InterfaceId,
183 if_name: IfName,
184}
185
186impl Interface {
187 pub fn new(if_id: Option<libc::c_uint>, if_name: String) -> std::io::Result<Self> {
188 Ok(Self {
189 if_id: InterfaceId::new(if_id),
190 if_name: IfName::new(if_name)?,
191 })
192 }
193
194 pub fn from_id(if_id: InterfaceId) -> Option<Self> {
198 index_to_name(if_id).ok().map(|name| Self {
199 if_id,
200 if_name: name,
201 })
202 }
203
204 pub fn from_name(name: IfName) -> Option<Self> {
205 name_to_index(name).ok().map(|if_id| Self {
206 if_id,
207 if_name: name,
208 })
209 }
210}