Skip to content

Commit 574e3a9

Browse files
committed
mctp-linux: Use rustix rather than libc
rustix provides higher level wrappers for syscalls, reducing unsafe code. This removes the libc dependency from mctp-linux, and rustix compiles to direct syscalls for slightly smaller code size. rustix is already an indirect dependency from async-io. No functional change expected. Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
1 parent 851326c commit 574e3a9

3 files changed

Lines changed: 90 additions & 158 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mctp-linux/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository.workspace = true
88
categories = ["network-programming", "embedded", "hardware-support", "os::linux-apis"]
99

1010
[dependencies]
11-
libc = "0.2"
11+
rustix = { version = "1.1.4", features = ["net"] }
1212
mctp = { workspace = true, features = ["std"] }
1313
async-io = { workspace = true }
1414

mctp-linux/src/lib.rs

Lines changed: 88 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
//! Interface for the Linux socket-based MCTP support.
1111
//!
12-
//! This crate provides some minimal wrappers around standard [`libc`] socket
12+
//! This crate provides some minimal wrappers around standard socket
1313
//! operations, using MCTP-specific addressing structures.
1414
//!
1515
//! [`MctpSocket`] provides support for blocking socket operations
@@ -42,20 +42,22 @@
4242
use async_io::Async;
4343
use core::mem;
4444
use std::fmt;
45-
use std::io::Error;
46-
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
45+
use std::io::{Error, ErrorKind, IoSlice};
46+
use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd, RawFd};
4747
use std::time::Duration;
4848

4949
use mctp::{
5050
Eid, MsgIC, MsgType, Result, Tag, TagValue, MCTP_ADDR_ANY, MCTP_TAG_OWNER,
5151
};
5252

53-
/* until we have these in libc... */
54-
const AF_MCTP: libc::sa_family_t = 45;
53+
use rustix::net::AddressFamily;
54+
55+
const AF_MCTP: AddressFamily = AddressFamily::from_raw(45);
5556
#[repr(C)]
5657
#[allow(non_camel_case_types)]
58+
#[derive(Clone, Debug, Hash)]
5759
struct sockaddr_mctp {
58-
smctp_family: libc::sa_family_t,
60+
smctp_family: rustix::net::RawAddressFamily,
5961
__smctp_pad0: u16,
6062
smctp_network: u32,
6163
smctp_addr: u8,
@@ -75,7 +77,7 @@ impl MctpSockAddr {
7577
/// and tag value.
7678
pub fn new(eid: u8, net: u32, typ: u8, tag: u8) -> Self {
7779
MctpSockAddr(sockaddr_mctp {
78-
smctp_family: AF_MCTP,
80+
smctp_family: AF_MCTP.as_raw(),
7981
__smctp_pad0: 0,
8082
smctp_network: net,
8183
smctp_addr: eid,
@@ -84,23 +86,40 @@ impl MctpSockAddr {
8486
__smctp_pad1: 0,
8587
})
8688
}
89+
}
8790

88-
fn zero() -> Self {
89-
Self::new(0, MCTP_NET_ANY, 0, 0)
90-
}
91-
92-
fn as_raw(&self) -> (*const libc::sockaddr, libc::socklen_t) {
93-
(
94-
&self.0 as *const sockaddr_mctp as *const libc::sockaddr,
95-
mem::size_of::<sockaddr_mctp>() as libc::socklen_t,
91+
unsafe impl rustix::net::addr::SocketAddrArg for MctpSockAddr {
92+
unsafe fn with_sockaddr<R>(
93+
&self,
94+
f: impl FnOnce(
95+
*const rustix::net::addr::SocketAddrOpaque,
96+
rustix::net::addr::SocketAddrLen,
97+
) -> R,
98+
) -> R {
99+
f(
100+
&self.0 as *const sockaddr_mctp
101+
as *const rustix::net::addr::SocketAddrOpaque,
102+
mem::size_of::<sockaddr_mctp>() as _,
96103
)
97104
}
105+
}
98106

99-
fn as_raw_mut(&mut self) -> (*mut libc::sockaddr, libc::socklen_t) {
100-
(
101-
&mut self.0 as *mut sockaddr_mctp as *mut libc::sockaddr,
102-
mem::size_of::<sockaddr_mctp>() as libc::socklen_t,
103-
)
107+
impl TryFrom<rustix::net::SocketAddrAny> for MctpSockAddr {
108+
type Error = ();
109+
fn try_from(
110+
sock: rustix::net::SocketAddrAny,
111+
) -> core::result::Result<Self, Self::Error> {
112+
if sock.address_family() != AF_MCTP {
113+
return Err(());
114+
}
115+
116+
if sock.addr_len() as usize != mem::size_of::<sockaddr_mctp>() {
117+
return Err(());
118+
}
119+
120+
Ok(Self(
121+
unsafe { &*(sock.as_ptr() as *const sockaddr_mctp) }.clone(),
122+
))
104123
}
105124
}
106125

@@ -134,31 +153,22 @@ fn tag_to_smctp(tag: &Tag) -> u8 {
134153
tag.tag().0 | to_bit
135154
}
136155

137-
// helper for IO error construction
138-
fn last_os_error() -> mctp::Error {
139-
mctp::Error::Io(Error::last_os_error())
140-
}
141-
142156
/// MCTP socket object.
143157
pub struct MctpSocket(OwnedFd);
144158

145159
impl MctpSocket {
146160
/// Create a new MCTP socket. This can then be used for send/receive
147161
/// operations.
148162
pub fn new() -> Result<Self> {
149-
let rc = unsafe {
150-
libc::socket(
151-
AF_MCTP.into(),
152-
libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
153-
0,
154-
)
155-
};
156-
if rc < 0 {
157-
return Err(last_os_error());
163+
match rustix::net::socket_with(
164+
AF_MCTP,
165+
rustix::net::SocketType::DGRAM,
166+
rustix::net::SocketFlags::CLOEXEC,
167+
None,
168+
) {
169+
Ok(fd) => Ok(MctpSocket(fd)),
170+
Err(e) => Err(mctp::Error::Io(e.into())),
158171
}
159-
// safety: the fd is valid, and we have exclusive ownership
160-
let fd = unsafe { OwnedFd::from_raw_fd(rc) };
161-
Ok(MctpSocket(fd))
162172
}
163173

164174
// Inner recvfrom, returning an io::Error on failure. This can be
@@ -168,34 +178,29 @@ impl MctpSocket {
168178
&self,
169179
buf: &mut [u8],
170180
) -> std::io::Result<(usize, MctpSockAddr)> {
171-
let mut addr = MctpSockAddr::zero();
172-
let (addr_ptr, mut addr_len) = addr.as_raw_mut();
173-
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;
174-
let buf_len = buf.len() as libc::size_t;
175-
let fd = self.as_raw_fd();
176-
177-
let rc = unsafe {
178-
libc::recvfrom(
179-
fd,
180-
buf_ptr,
181-
buf_len,
182-
libc::MSG_TRUNC,
183-
addr_ptr,
184-
&mut addr_len,
185-
)
186-
};
187-
188-
if rc < 0 {
189-
Err(Error::last_os_error())
190-
} else {
191-
Ok((rc as usize, addr))
181+
match rustix::net::recvfrom(
182+
self.as_fd(),
183+
buf,
184+
rustix::net::RecvFlags::TRUNC,
185+
) {
186+
Ok((_out, len, addr)) => {
187+
// OK unwrap, recv returns an address
188+
let addr = addr.unwrap();
189+
// Shouldn't fail unless the kernel returns a bad address
190+
let addr = addr
191+
.try_into()
192+
.map_err(|_| Error::from(ErrorKind::Other))?;
193+
// OK unwrap, address.try_into().unwrap();
194+
Ok((len, addr))
195+
}
196+
Err(e) => Err(e.into()),
192197
}
193198
}
194199

195200
/// Blocking receive from a socket, into `buf`, returning a length
196201
/// and source address
197202
///
198-
/// Essentially a wrapper around [libc::recvfrom], using MCTP-specific
203+
/// Essentially a wrapper around `recvfrom`, using MCTP-specific
199204
/// addressing.
200205
pub fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, MctpSockAddr)> {
201206
let (len, addr) = self.io_recvfrom(buf).map_err(mctp::Error::Io)?;
@@ -210,44 +215,29 @@ impl MctpSocket {
210215
bufs: &[&[u8]],
211216
addr: &MctpSockAddr,
212217
) -> std::io::Result<usize> {
213-
let mut iov: Vec<libc::iovec> = bufs
214-
.iter()
215-
.map(|b| libc::iovec {
216-
iov_base: b.as_ptr() as *mut libc::c_void,
217-
iov_len: b.len() as libc::size_t,
218-
})
219-
.collect();
220-
221-
let (addr_ptr, addr_len) = addr.as_raw();
222-
223-
let mut msg: libc::msghdr = unsafe { mem::zeroed() };
224-
msg.msg_name = addr_ptr as *mut libc::c_void;
225-
msg.msg_namelen = addr_len;
226-
msg.msg_iov = iov.as_mut_ptr();
227-
msg.msg_iovlen = iov.len() as _;
228-
229-
let fd = self.as_raw_fd();
230-
let rc = unsafe { libc::sendmsg(fd, &msg, 0) };
231-
232-
if rc < 0 {
233-
Err(Error::last_os_error())
234-
} else {
235-
Ok(rc as usize)
236-
}
218+
let iov: Vec<IoSlice> = bufs.iter().map(|b| IoSlice::new(b)).collect();
219+
220+
Ok(rustix::net::sendmsg_addr(
221+
self.as_fd(),
222+
addr,
223+
&iov,
224+
&mut rustix::net::SendAncillaryBuffer::default(),
225+
rustix::net::SendFlags::empty(),
226+
)?)
237227
}
238228

239229
/// Blocking send to a socket, given a buffer and address, returning
240230
/// the number of bytes sent.
241231
///
242-
/// Essentially a wrapper around [libc::sendto].
232+
/// Essentially a wrapper around `sendto`.
243233
pub fn sendto(&self, buf: &[u8], addr: &MctpSockAddr) -> Result<usize> {
244234
self.sendmsg(&[buf], addr)
245235
}
246236

247237
/// Blocking send to a socket, given a slice of buffers and address, returning
248238
/// the number of bytes sent.
249239
///
250-
/// Essentially a wrapper around [libc::sendmsg].
240+
/// Essentially a wrapper around `sendmsg`.
251241
pub fn sendmsg(
252242
&self,
253243
bufs: &[&[u8]],
@@ -258,89 +248,31 @@ impl MctpSocket {
258248

259249
/// Bind the socket to a local address.
260250
pub fn bind(&self, addr: &MctpSockAddr) -> Result<()> {
261-
let (addr_ptr, addr_len) = addr.as_raw();
262-
let fd = self.as_raw_fd();
263-
264-
let rc = unsafe { libc::bind(fd, addr_ptr, addr_len) };
265-
266-
if rc < 0 {
267-
Err(last_os_error())
268-
} else {
269-
Ok(())
270-
}
251+
rustix::net::bind(self.as_fd(), addr)
252+
.map_err(|e| mctp::Error::Io(e.into()))
271253
}
272254

273255
/// Set the read timeout.
274256
///
275257
/// A valid of `None` will have no timeout.
276258
pub fn set_read_timeout(&self, dur: Option<Duration>) -> Result<()> {
277-
// Avoid warnings about using time_t with musl. See comment in read_timeout().
278-
#![allow(deprecated)]
279-
280-
let dur = dur.unwrap_or(Duration::ZERO);
281-
let tv = libc::timeval {
282-
tv_sec: dur.as_secs() as libc::time_t,
283-
tv_usec: dur.subsec_micros() as libc::suseconds_t,
284-
};
285-
let fd = self.as_raw_fd();
286-
let rc = unsafe {
287-
libc::setsockopt(
288-
fd,
289-
libc::SOL_SOCKET,
290-
libc::SO_RCVTIMEO,
291-
(&tv as *const libc::timeval) as *const libc::c_void,
292-
std::mem::size_of::<libc::timeval>() as libc::socklen_t,
293-
)
294-
};
295-
296-
if rc < 0 {
297-
Err(last_os_error())
298-
} else {
299-
Ok(())
300-
}
259+
rustix::net::sockopt::set_socket_timeout(
260+
self.as_fd(),
261+
rustix::net::sockopt::Timeout::Recv,
262+
dur,
263+
)
264+
.map_err(|e| mctp::Error::Io(e.into()))
301265
}
302266

303267
/// Retrieves the read timeout
304268
///
305269
/// A value of `None` indicates no timeout.
306270
pub fn read_timeout(&self) -> Result<Option<Duration>> {
307-
// Avoid warnings about using time_t with musl. It is safe here since we are
308-
// only using it directly with libc, that should be compiled with the
309-
// same definitions as libc crate. https://github.com/rust-lang/libc/issues/1848
310-
#![allow(deprecated)]
311-
312-
let mut tv = std::mem::MaybeUninit::<libc::timeval>::uninit();
313-
let mut tv_len =
314-
std::mem::size_of::<libc::timeval>() as libc::socklen_t;
315-
let fd = self.as_raw_fd();
316-
let rc = unsafe {
317-
libc::getsockopt(
318-
fd,
319-
libc::SOL_SOCKET,
320-
libc::SO_RCVTIMEO,
321-
tv.as_mut_ptr() as *mut libc::c_void,
322-
&mut tv_len as *mut libc::socklen_t,
323-
)
324-
};
325-
326-
if rc < 0 {
327-
Err(last_os_error())
328-
} else {
329-
let tv = unsafe { tv.assume_init() };
330-
if tv.tv_sec < 0 || tv.tv_usec < 0 {
331-
// Negative timeout from socket
332-
return Err(mctp::Error::Other);
333-
}
334-
335-
if tv.tv_sec == 0 && tv.tv_usec == 0 {
336-
Ok(None)
337-
} else {
338-
Ok(Some(
339-
Duration::from_secs(tv.tv_sec as u64)
340-
+ Duration::from_micros(tv.tv_usec as u64),
341-
))
342-
}
343-
}
271+
rustix::net::sockopt::socket_timeout(
272+
self.as_fd(),
273+
rustix::net::sockopt::Timeout::Recv,
274+
)
275+
.map_err(|e| mctp::Error::Io(e.into()))
344276
}
345277
}
346278

0 commit comments

Comments
 (0)