#[cfg(unix)]
use std::os::fd::AsFd;
use std::{io, net::SocketAddr, sync::Arc};
use super::{UdpRecv, UdpTransportFactory, UdpTransportFactoryParams};
#[cfg(target_os = "linux")]
use super::UdpSend;
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "windows")))]
mod generic;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux;
#[cfg(target_os = "windows")]
mod windows;
pub struct UdpSocketFactory;
const UDP_RECV_BUFFER_SIZE: usize = 7 * 1024 * 1024;
const UDP_SEND_BUFFER_SIZE: usize = 7 * 1024 * 1024;
impl UdpTransportFactory for UdpSocketFactory {
type SendV4 = UdpSocket;
type SendV6 = UdpSocket;
type RecvV4 = UdpSocket;
type RecvV6 = UdpSocket;
async fn bind(
&mut self,
params: &UdpTransportFactoryParams,
) -> io::Result<((Self::SendV4, Self::RecvV4), (Self::SendV6, Self::RecvV6))> {
let mut port = params.port;
let udp_v4 = UdpSocket::bind((params.addr_v4, port).into())?;
if port == 0 {
port = UdpSocket::local_addr(&udp_v4)?.port();
}
let udp_v6 = UdpSocket::bind((params.addr_v6, port).into())?;
#[cfg(target_os = "linux")]
if let Some(mark) = params.fwmark {
udp_v4.set_fwmark(mark)?;
udp_v6.set_fwmark(mark)?;
}
if let Err(err) = udp_v4.enable_udp_gro() {
log::warn!("Failed to enable UDP GRO for IPv4 socket: {err}");
}
if let Err(err) = udp_v6.enable_udp_gro() {
log::warn!("Failed to enable UDP GRO for IPv6 socket: {err}");
}
Ok(((udp_v4.clone(), udp_v4), (udp_v6.clone(), udp_v6)))
}
}
#[derive(Clone)]
pub struct UdpSocket {
inner: Arc<tokio::net::UdpSocket>,
}
impl UdpSocket {
pub fn bind(addr: SocketAddr) -> io::Result<Self> {
let domain = match addr {
SocketAddr::V4(..) => socket2::Domain::IPV4,
SocketAddr::V6(..) => socket2::Domain::IPV6,
};
let udp_sock =
socket2::Socket::new(domain, socket2::Type::DGRAM, Some(socket2::Protocol::UDP))?;
udp_sock.set_nonblocking(true)?;
udp_sock.set_reuse_address(true)?;
udp_sock.set_recv_buffer_size(UDP_RECV_BUFFER_SIZE)?;
udp_sock.set_send_buffer_size(UDP_SEND_BUFFER_SIZE)?;
udp_sock.bind(&addr.into())?;
let inner = tokio::net::UdpSocket::from_std(udp_sock.into())?;
Ok(Self {
inner: Arc::new(inner),
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
}
#[cfg(unix)]
impl AsFd for UdpSocket {
fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
self.inner.as_fd()
}
}