mirror of
https://github.com/n0-computer/noq.git
synced 2026-09-24 03:58:19 +00:00
udp: simplify socket state initialization
This commit is contained in:
committed by
Benjamin Saunders
parent
20612b632b
commit
4f25f501ef
@@ -18,17 +18,14 @@ pub struct UdpSocketState {
|
||||
}
|
||||
|
||||
impl UdpSocketState {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(socket: UdpSocketRef<'_>) -> io::Result<Self> {
|
||||
socket.0.set_nonblocking(true)?;
|
||||
let now = Instant::now();
|
||||
Self {
|
||||
last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(socket: UdpSockRef<'_>) -> io::Result<()> {
|
||||
socket.0.set_nonblocking(true)
|
||||
}
|
||||
|
||||
pub fn send(
|
||||
&self,
|
||||
socket: UdpSockRef<'_>,
|
||||
|
||||
+85
-98
@@ -1,8 +1,7 @@
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
use std::ptr;
|
||||
use std::{
|
||||
io,
|
||||
io::IoSliceMut,
|
||||
io::{self, IoSliceMut},
|
||||
mem::{self, MaybeUninit},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
|
||||
os::unix::io::AsRawFd,
|
||||
@@ -43,18 +42,95 @@ pub struct UdpSocketState {
|
||||
}
|
||||
|
||||
impl UdpSocketState {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
|
||||
let io = sock.0;
|
||||
let mut cmsg_platform_space = 0;
|
||||
if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") || cfg!(target_os = "macos") {
|
||||
cmsg_platform_space +=
|
||||
unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
|
||||
}
|
||||
|
||||
assert!(
|
||||
CMSG_LEN
|
||||
>= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
|
||||
+ cmsg_platform_space
|
||||
);
|
||||
assert!(
|
||||
mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
|
||||
"control message buffers will be misaligned"
|
||||
);
|
||||
|
||||
io.set_nonblocking(true)?;
|
||||
|
||||
let addr = io.local_addr()?;
|
||||
let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
|
||||
|
||||
// mac and ios do not support IP_RECVTOS on dual-stack sockets :(
|
||||
// older macos versions also don't have the flag and will error out if we don't ignore it
|
||||
if is_ipv4 || !io.only_v6()? {
|
||||
if let Err(err) = set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
|
||||
{
|
||||
tracing::debug!("Ignoring error setting IP_RECVTOS on socket: {err:?}",);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// opportunistically try to enable GRO. See gro::gro_segments().
|
||||
let _ = set_socket_option(&*io, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON);
|
||||
|
||||
// Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
|
||||
set_socket_option(
|
||||
&*io,
|
||||
libc::IPPROTO_IP,
|
||||
libc::IP_MTU_DISCOVER,
|
||||
libc::IP_PMTUDISC_PROBE,
|
||||
)?;
|
||||
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
|
||||
} else {
|
||||
set_socket_option(
|
||||
&*io,
|
||||
libc::IPPROTO_IPV6,
|
||||
libc::IPV6_MTU_DISCOVER,
|
||||
libc::IP_PMTUDISC_PROBE,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_DONTFRAG, OPTION_ON)?;
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
// IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
|
||||
// macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS
|
||||
// macOS also supports IP_PKTINFO
|
||||
{
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Options standardized in RFC 3542
|
||||
if !is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
|
||||
// Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
|
||||
// kernel's path MTU guess, but actually disabling fragmentation requires this too. See
|
||||
// __ip6_append_data in ip6_output.c.
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_DONTFRAG, OPTION_ON)?;
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
Self {
|
||||
Ok(Self {
|
||||
last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
|
||||
max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
|
||||
gro_segments: gro::gro_segments(),
|
||||
sendmsg_einval: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(sock: UdpSockRef<'_>) -> io::Result<()> {
|
||||
init(sock.0)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(&self, socket: UdpSockRef<'_>, transmits: &[Transmit]) -> Result<usize, io::Error> {
|
||||
@@ -101,95 +177,6 @@ impl UdpSocketState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UdpSocketState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn init(io: SockRef<'_>) -> io::Result<()> {
|
||||
let mut cmsg_platform_space = 0;
|
||||
if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") || cfg!(target_os = "macos") {
|
||||
cmsg_platform_space +=
|
||||
unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
|
||||
}
|
||||
|
||||
assert!(
|
||||
CMSG_LEN
|
||||
>= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
|
||||
+ cmsg_platform_space
|
||||
);
|
||||
assert!(
|
||||
mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
|
||||
"control message buffers will be misaligned"
|
||||
);
|
||||
|
||||
io.set_nonblocking(true)?;
|
||||
|
||||
let addr = io.local_addr()?;
|
||||
let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
|
||||
|
||||
// mac and ios do not support IP_RECVTOS on dual-stack sockets :(
|
||||
// older macos versions also don't have the flag and will error out if we don't ignore it
|
||||
if is_ipv4 || !io.only_v6()? {
|
||||
if let Err(err) = set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON) {
|
||||
tracing::debug!("Ignoring error setting IP_RECVTOS on socket: {err:?}",);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// opportunistically try to enable GRO. See gro::gro_segments().
|
||||
let _ = set_socket_option(&*io, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON);
|
||||
|
||||
// Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
|
||||
set_socket_option(
|
||||
&*io,
|
||||
libc::IPPROTO_IP,
|
||||
libc::IP_MTU_DISCOVER,
|
||||
libc::IP_PMTUDISC_PROBE,
|
||||
)?;
|
||||
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
|
||||
} else {
|
||||
set_socket_option(
|
||||
&*io,
|
||||
libc::IPPROTO_IPV6,
|
||||
libc::IPV6_MTU_DISCOVER,
|
||||
libc::IP_PMTUDISC_PROBE,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_DONTFRAG, OPTION_ON)?;
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
// IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
|
||||
// macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS
|
||||
// macOS also supports IP_PKTINFO
|
||||
{
|
||||
if is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Options standardized in RFC 3542
|
||||
if !is_ipv4 {
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
|
||||
// Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
|
||||
// kernel's path MTU guess, but actually disabling fragmentation requires this too. See
|
||||
// __ip6_append_data in ip6_output.c.
|
||||
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_DONTFRAG, OPTION_ON)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
fn send(
|
||||
#[allow(unused_variables)] // only used on Linux
|
||||
|
||||
@@ -17,14 +17,7 @@ pub struct UdpSocketState {
|
||||
}
|
||||
|
||||
impl UdpSocketState {
|
||||
pub fn new() -> Self {
|
||||
let now = Instant::now();
|
||||
Self {
|
||||
last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(socket: UdpSockRef<'_>) -> io::Result<()> {
|
||||
pub fn new(socket: UdpSockRef<'_>) -> io::Result<Self> {
|
||||
socket.0.set_nonblocking(true)?;
|
||||
let addr = socket.0.local_addr()?;
|
||||
let is_ipv6 = addr.as_socket_ipv6().is_some();
|
||||
@@ -77,7 +70,10 @@ impl UdpSocketState {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let now = Instant::now();
|
||||
Ok(Self {
|
||||
last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(&self, socket: UdpSockRef<'_>, transmits: &[Transmit]) -> Result<usize, io::Error> {
|
||||
@@ -153,12 +149,6 @@ impl UdpSocketState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UdpSocketState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const BATCH_SIZE: usize = 1;
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -25,10 +25,9 @@ impl Runtime for AsyncStdRuntime {
|
||||
}
|
||||
|
||||
fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Arc<dyn AsyncUdpSocket>> {
|
||||
udp::UdpSocketState::configure((&sock).into())?;
|
||||
Ok(Arc::new(UdpSocket {
|
||||
inner: udp::UdpSocketState::new((&sock).into())?,
|
||||
io: Async::new(sock)?,
|
||||
inner: udp::UdpSocketState::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,9 @@ impl Runtime for TokioRuntime {
|
||||
}
|
||||
|
||||
fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Arc<dyn AsyncUdpSocket>> {
|
||||
udp::UdpSocketState::configure((&sock).into())?;
|
||||
Ok(Arc::new(UdpSocket {
|
||||
inner: udp::UdpSocketState::new((&sock).into())?,
|
||||
io: tokio::net::UdpSocket::from_std(sock)?,
|
||||
inner: udp::UdpSocketState::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user