Skip to content

Commit 2ca3ca6

Browse files
Merge branch 'cleanup-talpid-net-ifreq'
2 parents 4b43979 + e4cf13a commit 2ca3ca6

File tree

1 file changed

+106
-91
lines changed

1 file changed

+106
-91
lines changed

talpid-net/src/unix.rs

Lines changed: 106 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,130 @@
11
#![cfg(any(target_os = "linux", target_os = "macos"))]
22

3-
#[cfg(target_os = "linux")]
4-
use std::ffi::c_ulong;
5-
use std::{ffi::c_uint, io, os::fd::AsRawFd};
3+
use std::ffi::c_uint;
4+
use std::io;
5+
use std::mem;
6+
use std::os::fd::AsRawFd;
7+
use std::ptr;
68

7-
use nix::{errno::Errno, net::if_::if_nametoindex};
8-
use socket2::Domain;
9+
use nix::errno::Errno;
10+
use nix::libc::ifreq;
11+
use nix::net::if_::if_nametoindex;
12+
use socket2::{Domain, Protocol, Socket, Type};
913
use talpid_types::ErrorExt;
1014

11-
/// Converts an interface name into the corresponding index.
12-
pub fn iface_index(name: &str) -> Result<c_uint, IfaceIndexLookupError> {
13-
if_nametoindex(name).map_err(|error| IfaceIndexLookupError {
14-
interface_name: name.to_owned(),
15-
error,
16-
})
17-
}
18-
1915
#[derive(Debug, thiserror::Error)]
2016
#[error("Failed to get index for interface {interface_name}: {error}")]
2117
pub struct IfaceIndexLookupError {
2218
pub interface_name: String,
2319
pub error: Errno,
2420
}
2521

26-
#[cfg(target_os = "macos")]
27-
const SIOCSIFMTU: u64 = 0x80206934;
28-
#[cfg(target_os = "macos")]
29-
const SIOCGIFMTU: u64 = 0xc0206933;
30-
#[cfg(target_os = "linux")]
31-
const SIOCSIFMTU: c_ulong = libc::SIOCSIFMTU;
32-
#[cfg(target_os = "linux")]
33-
const SIOCGIFMTU: c_ulong = libc::SIOCSIFMTU;
22+
/// Converts an interface name into the corresponding index.
23+
pub fn iface_index(name: &str) -> Result<c_uint, IfaceIndexLookupError> {
24+
if_nametoindex(name).map_err(|error| IfaceIndexLookupError {
25+
interface_name: name.to_owned(),
26+
error,
27+
})
28+
}
3429

3530
pub fn set_mtu(interface_name: &str, mtu: u16) -> Result<(), io::Error> {
36-
let sock = socket2::Socket::new(
37-
Domain::IPV4,
38-
socket2::Type::STREAM,
39-
Some(socket2::Protocol::TCP),
40-
)?;
41-
42-
// SAFETY: ifreq is a C struct, these can safely be zeroed.
43-
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
44-
if interface_name.len() >= ifr.ifr_name.len() {
45-
return Err(io::Error::new(
46-
io::ErrorKind::InvalidInput,
47-
"Interface name too long",
48-
));
49-
}
50-
51-
// SAFETY: `interface_name.len()` is less than `ifr.ifr_name.len()`
52-
unsafe {
53-
std::ptr::copy_nonoverlapping(
54-
interface_name.as_ptr() as *const libc::c_char,
55-
ifr.ifr_name.as_mut_ptr(),
56-
interface_name.len(),
57-
)
58-
};
59-
ifr.ifr_ifru.ifru_mtu = mtu as i32;
60-
61-
// For some reason, libc crate defines ioctl to take a c_int (which is defined as i32), but the c_ulong type is defined as u64:
62-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/fn.ioctl.html
63-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/type.c_ulong.html
64-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/constant.SIOCSIFMTU.html
65-
#[allow(clippy::useless_conversion)]
66-
let request = SIOCSIFMTU.try_into().unwrap();
67-
// SAFETY: SIOCSIFMTU expects an ifreq with an MTU and interface set
68-
if unsafe { libc::ioctl(sock.as_raw_fd(), request, &ifr) } < 0 {
69-
let e = std::io::Error::last_os_error();
31+
IfReq::new(interface_name)?.set_mtu(mtu).inspect_err(|e| {
7032
log::error!("{}", e.display_chain_with_msg("SIOCSIFMTU failed"));
71-
return Err(e);
72-
}
73-
Ok(())
33+
})
7434
}
7535

7636
pub fn get_mtu(interface_name: &str) -> Result<u16, io::Error> {
77-
let sock = socket2::Socket::new(
78-
Domain::IPV4,
79-
socket2::Type::STREAM,
80-
Some(socket2::Protocol::TCP),
81-
)?;
37+
IfReq::new(interface_name)?.get_mtu().inspect_err(|e| {
38+
log::error!("{}", e.display_chain_with_msg("SIOCGIFMTU failed"));
39+
})
40+
}
8241

83-
// SAFETY: ifreq is a C struct, these can safely be zeroed.
84-
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
85-
if interface_name.len() >= ifr.ifr_name.len() {
86-
return Err(io::Error::new(
87-
io::ErrorKind::InvalidInput,
88-
"Interface name too long",
89-
));
42+
/// An [`ifreq`] initialized with an interface name.
43+
struct IfReq {
44+
interface_request: ifreq,
45+
socket: Socket,
46+
}
47+
48+
impl IfReq {
49+
/// Returns an [`ifreq`] refering to `interface`.
50+
///
51+
/// - `interface`: Name of the interface (e.g. `eth0`).
52+
fn new(interface: &str) -> Result<Self, io::Error> {
53+
let invalid_input = |msg| io::Error::new(io::ErrorKind::InvalidInput, msg);
54+
if !interface.is_ascii() {
55+
return Err(invalid_input("Interface name contains UTF-8"));
56+
};
57+
let interface_name = interface.as_bytes();
58+
// `ifreq.ifr_name` may only contain max IF_NAMESIZE ASCII characters, including a trailing
59+
// null terminator.
60+
if interface_name.len() >= nix::libc::IF_NAMESIZE {
61+
return Err(invalid_input("Interface name too long"));
62+
};
63+
// SAFETY: ifreq is a C struct, these can safely be zeroed.
64+
let mut ifr: ifreq = unsafe { mem::zeroed() };
65+
// SAFETY: `interface_name.len()` does not exceed IF_NAMESIZE (+ a trailing null terminator) and `interface_name` only
66+
// contains ASCII.
67+
unsafe {
68+
ptr::copy_nonoverlapping(
69+
interface_name.as_ptr().cast::<libc::c_char>(),
70+
ifr.ifr_name.as_mut_ptr(),
71+
interface_name.len(),
72+
)
73+
};
74+
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?;
75+
Ok(Self {
76+
interface_request: ifr,
77+
socket,
78+
})
9079
}
9180

92-
// SAFETY: `interface_name.len()` is less than `ifr.ifr_name.len()`
93-
unsafe {
94-
std::ptr::copy_nonoverlapping(
95-
interface_name.as_ptr() as *const libc::c_char,
96-
ifr.ifr_name.as_mut_ptr(),
97-
interface_name.len(),
98-
)
99-
};
81+
/// Set MTU for this interface.
82+
// SIOCSIFMTU in a trenchcoat.
83+
fn set_mtu(mut self, mtu: u16) -> Result<(), io::Error> {
84+
self.interface_request.ifr_ifru.ifru_mtu = i32::from(mtu);
85+
let socket = self.socket.as_raw_fd();
86+
#[cfg(target_os = "macos")]
87+
const SIOCSIFMTU: u64 = 0x80206934;
88+
#[cfg(target_os = "linux")]
89+
const SIOCSIFMTU: libc::c_ulong = libc::SIOCSIFMTU;
90+
// For some reason, libc crate defines ioctl to take a c_int (which is defined as i32), but the c_ulong type is defined as u64:
91+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/fn.ioctl.html
92+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/type.c_ulong.html
93+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/constant.SIOCSIFMTU.html
94+
#[allow(clippy::useless_conversion)]
95+
let request = SIOCSIFMTU.try_into().unwrap();
96+
// SAFETY: SIOCSIFMTU expects an ifreq with an MTU and interface set. The interface is set
97+
// by [Self::new].
98+
match unsafe { libc::ioctl(socket, request, &self.interface_request) } {
99+
n if n < 0 => Err(io::Error::last_os_error()),
100+
_ => Ok(()),
101+
}
102+
}
100103

101-
// For some reason, libc crate defines ioctl to take a c_int (which is defined as i32), but the c_ulong type is defined as u64:
102-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/fn.ioctl.html
103-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/type.c_ulong.html
104-
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/constant.SIOCGIFMTU.html
105-
#[allow(clippy::useless_conversion)]
106-
let request = SIOCGIFMTU.try_into().unwrap();
107-
// SAFETY: SIOCGIFMTU expects an ifreq with an interface set
108-
if unsafe { libc::ioctl(sock.as_raw_fd(), request, &ifr) } < 0 {
109-
let e = std::io::Error::last_os_error();
110-
log::error!("{}", e.display_chain_with_msg("SIOCGIFMTU failed"));
111-
return Err(e);
104+
/// Get MTU of this interface.
105+
// SIOCGIFMTU in a trenchcoat.
106+
fn get_mtu(self) -> Result<u16, io::Error> {
107+
let socket = self.socket.as_raw_fd();
108+
#[cfg(target_os = "macos")]
109+
const SIOCGIFMTU: u64 = 0xc0206933;
110+
#[cfg(target_os = "linux")]
111+
const SIOCGIFMTU: libc::c_ulong = libc::SIOCSIFMTU;
112+
// For some reason, libc crate defines ioctl to take a c_int (which is defined as i32), but the c_ulong type is defined as u64:
113+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/fn.ioctl.html
114+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/type.c_ulong.html
115+
// https://docs.rs/libc/latest/x86_64-unknown-linux-musl/libc/constant.SIOCGIFMTU.html
116+
#[allow(clippy::useless_conversion)]
117+
let request = SIOCGIFMTU.try_into().unwrap();
118+
// SAFETY: SIOCGIFMTU expects an ifreq with an interface set, which is guaranteed by
119+
// [Self::new].
120+
match unsafe { libc::ioctl(socket, request, &self.interface_request) } {
121+
n if n < 0 => Err(io::Error::last_os_error()),
122+
_ => {
123+
// SAFETY: ifru_mtu is initialized by SIOCGIFMTU
124+
let mtu = unsafe { self.interface_request.ifr_ifru.ifru_mtu };
125+
let mtu = u16::try_from(mtu).expect("MTU of interface to be less than u16::MAX");
126+
Ok(mtu)
127+
}
128+
}
112129
}
113-
// SAFETY: ifru_mtu is initialized by SIOCGIFMTU
114-
Ok(u16::try_from(unsafe { ifr.ifr_ifru.ifru_mtu }).unwrap())
115130
}

0 commit comments

Comments
 (0)