Skip to content

Instantly share code, notes, and snippets.

@folkertdev
Created May 19, 2023 22:16
Show Gist options
  • Save folkertdev/9bb8abeb5fcd7f32a8bfdc86f2a6fc3b to your computer and use it in GitHub Desktop.
Save folkertdev/9bb8abeb5fcd7f32a8bfdc86f2a6fc3b to your computer and use it in GitHub Desktop.
A rust example that writes a message with ancillary data (a control message) and then reads it from the error queue
use std::io::{Error, ErrorKind};
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::os::unix::io::AsRawFd;
use tokio::io::unix::AsyncFd;
use tokio::io::Interest;
pub(crate) fn cerr(t: libc::c_int) -> std::io::Result<libc::c_int> {
match t {
-1 => Err(std::io::Error::last_os_error()),
_ => Ok(t),
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
// let sock_fd = cerr(unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }).unwrap();
let sock = UdpSocket::bind("0.0.0.0:8012").unwrap();
let sock_fd = async_fd.as_raw_fd();
// Enable IP_RECVERR option to receive error messages
let recv_err: libc::c_int = 1;
unsafe {
let res = libc::setsockopt(
sock_fd,
libc::SOL_IP,
libc::IP_RECVERR,
&recv_err as *const _ as *const libc::c_void,
std::mem::size_of::<libc::c_int>() as libc::socklen_t,
);
if res == -1 {
return Err(Error::last_os_error());
}
}
let handle = tokio::spawn(async move {
// Set the destination address
let mut dest_addr =
unsafe { std::mem::MaybeUninit::<libc::sockaddr_in>::zeroed().assume_init() };
dest_addr.sin_family = libc::AF_INET as _;
dest_addr.sin_port = 1234u16.to_be(); // Destination port
dest_addr.sin_addr.s_addr = libc::INADDR_LOOPBACK.to_be(); // Destination IP address
// Prepare the message data
let message = "Hello, Socket!";
// Prepare the ancillary data (control message)
let mut cmsg_buf =
[0; unsafe { libc::CMSG_SPACE(std::mem::size_of::<libc::c_int>() as _) } as usize];
unsafe {
let cmsg: *mut libc::cmsghdr = cmsg_buf.as_mut_ptr() as *mut libc::cmsghdr;
(*cmsg).cmsg_len =
libc::CMSG_LEN(std::mem::size_of::<libc::c_int>() as libc::socklen_t) as _;
(*cmsg).cmsg_level = libc::IPPROTO_IP;
(*cmsg).cmsg_type = libc::IP_TTL as libc::c_int;
let cmsg_data: *mut libc::c_int = libc::CMSG_DATA(cmsg) as *mut libc::c_int;
*cmsg_data = 64; // Set the TTL value
}
// Prepare the destination address for the sendmsg call
let dest_sockaddr: *const libc::sockaddr = &dest_addr as *const _ as *const libc::sockaddr;
let dest_addrlen: libc::socklen_t = std::mem::size_of_val(&dest_addr) as libc::socklen_t;
// Prepare the message structure for sendmsg
let mut iov = libc::iovec {
iov_base: message.as_ptr() as *mut libc::c_void,
iov_len: message.len(),
};
let mut msg: libc::msghdr = unsafe { std::mem::zeroed() };
msg.msg_name = dest_sockaddr as *mut libc::c_void;
msg.msg_namelen = dest_addrlen;
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_buf.as_mut_ptr() as *mut libc::c_void;
msg.msg_controllen = unsafe { (*msg.msg_control.cast::<libc::cmsghdr>()).cmsg_len };
// Send the message with ancillary data using sendmsg
let res = unsafe { libc::sendmsg(sock_fd, &msg, 0) };
if res == -1 {
panic!("{:?}", Error::last_os_error());
}
});
// Read messages from the error queue
const BUFFER_SIZE: usize = 2048;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut iov_recv = libc::iovec {
iov_base: buffer.as_mut_ptr() as *mut libc::c_void,
iov_len: BUFFER_SIZE,
};
let mut msg_recv: libc::msghdr = unsafe { std::mem::zeroed() };
msg_recv.msg_iov = &mut iov_recv;
msg_recv.msg_iovlen = 1;
loop {
let recv_len = unsafe { libc::recvmsg(sock_fd, &mut msg_recv, libc::MSG_ERRQUEUE) };
if recv_len == -1 {
let err = Error::last_os_error();
if err.kind() == ErrorKind::WouldBlock {
break; // No more messages in the queue
}
return Err(err);
}
println!("Received message from error queue:");
println!("Message Length: {} bytes", recv_len);
println!(
"Message Content: {}",
String::from_utf8_lossy(&buffer[..recv_len as usize])
);
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment