Skip to content

Commit

Permalink
fix: allow configurable IPv4/IPv6 socket binding
Browse files Browse the repository at this point in the history
  • Loading branch information
Boquan Fang authored and boquan-fang committed Feb 12, 2025
1 parent b871ad0 commit 343adcc
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 15 deletions.
5 changes: 3 additions & 2 deletions quic/s2n-quic-platform/src/io/tokio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl Io {
gro_enabled,
reuse_address,
reuse_port,
only_v6,
} = self.builder;

let clock = Clock::default();
Expand Down Expand Up @@ -91,7 +92,7 @@ impl Io {
let rx_socket = if let Some(rx_socket) = rx_socket {
rx_socket
} else if let Some(recv_addr) = recv_addr {
syscall::bind_udp(recv_addr, reuse_address, reuse_port)?
syscall::bind_udp(recv_addr, reuse_address, reuse_port, only_v6)?
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
Expand All @@ -104,7 +105,7 @@ impl Io {
let tx_socket = if let Some(tx_socket) = tx_socket {
tx_socket
} else if let Some(send_addr) = send_addr {
syscall::bind_udp(send_addr, reuse_address, reuse_port)?
syscall::bind_udp(send_addr, reuse_address, reuse_port, only_v6)?
} else {
// No tx_socket or send address was specified, so the tx socket
// will be a handle to the rx socket.
Expand Down
6 changes: 6 additions & 0 deletions quic/s2n-quic-platform/src/io/tokio/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct Builder {
pub(super) gro_enabled: Option<bool>,
pub(super) reuse_address: bool,
pub(super) reuse_port: bool,
pub(super) only_v6: bool,
}

impl Builder {
Expand Down Expand Up @@ -236,6 +237,11 @@ impl Builder {
Ok(self)
}

pub fn with_only_v6(mut self, only_v6: bool) -> io::Result<Self> {
self.only_v6 = only_v6;
Ok(self)
}

pub fn build(self) -> io::Result<Io> {
Ok(Io { builder: self })
}
Expand Down
40 changes: 33 additions & 7 deletions quic/s2n-quic-platform/src/io/tokio/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,19 @@ impl<const IS_SERVER: bool> Endpoint for TestEndpoint<IS_SERVER> {
async fn runtime<A: ToSocketAddrs>(
receive_addr: A,
send_addr: Option<A>,
only_v6: bool,
) -> io::Result<(super::Io, SocketAddress)> {
let rx_socket = syscall::bind_udp(receive_addr, false, false)?;
let mut io_builder = Io::builder().with_only_v6(only_v6)?;

let rx_socket = syscall::bind_udp(receive_addr, false, false, only_v6)?;
rx_socket.set_nonblocking(true)?;
let rx_socket: std::net::UdpSocket = rx_socket.into();
let rx_addr = rx_socket.local_addr()?;

let mut io_builder = Io::builder().with_rx_socket(rx_socket)?;
io_builder = io_builder.with_rx_socket(rx_socket)?;

if let Some(tx_addr) = send_addr {
let tx_socket = syscall::bind_udp(tx_addr, false, false)?;
let tx_socket = syscall::bind_udp(tx_addr, false, false, only_v6)?;
tx_socket.set_nonblocking(true)?;
let tx_socket: std::net::UdpSocket = tx_socket.into();
io_builder = io_builder.with_tx_socket(tx_socket)?
Expand All @@ -177,9 +180,10 @@ async fn test<A: ToSocketAddrs>(
server_tx_addr: Option<A>,
client_rx_addr: A,
client_tx_addr: Option<A>,
only_v6: bool,
) -> io::Result<()> {
let (server_io, server_addr) = runtime(server_rx_addr, server_tx_addr).await?;
let (client_io, client_addr) = runtime(client_rx_addr, client_tx_addr).await?;
let (server_io, server_addr) = runtime(server_rx_addr, server_tx_addr, only_v6).await?;
let (client_io, client_addr) = runtime(client_rx_addr, client_tx_addr, only_v6).await?;

let server_endpoint = {
let mut handle = PathHandle::from_remote_address(client_addr.into());
Expand Down Expand Up @@ -212,25 +216,29 @@ static IPV6_LOCALHOST: &str = "[::1]:0";
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn ipv4_test() -> io::Result<()> {
test(IPV4_LOCALHOST, None, IPV4_LOCALHOST, None).await
let only_v6: bool = false;
test(IPV4_LOCALHOST, None, IPV4_LOCALHOST, None, only_v6).await
}

#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn ipv4_two_socket_test() -> io::Result<()> {
let only_v6: bool = false;
test(
IPV4_LOCALHOST,
Some(IPV4_LOCALHOST),
IPV4_LOCALHOST,
Some(IPV4_LOCALHOST),
only_v6,
)
.await
}

#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn ipv6_test() -> io::Result<()> {
let result = test(IPV6_LOCALHOST, None, IPV6_LOCALHOST, None).await;
let only_v6: bool = false;
let result = test(IPV6_LOCALHOST, None, IPV6_LOCALHOST, None, only_v6).await;

match result {
Err(err) if err.kind() == io::ErrorKind::AddrNotAvailable => {
Expand All @@ -244,11 +252,13 @@ async fn ipv6_test() -> io::Result<()> {
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn ipv6_two_socket_test() -> io::Result<()> {
let only_v6: bool = false;
let result = test(
IPV6_LOCALHOST,
Some(IPV6_LOCALHOST),
IPV6_LOCALHOST,
Some(IPV6_LOCALHOST),
only_v6,
)
.await;

Expand All @@ -260,3 +270,19 @@ async fn ipv6_two_socket_test() -> io::Result<()> {
other => other,
}
}

#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn only_v6_test() -> io::Result<()> {
let mut only_v6 = true;

let socket = syscall::bind_udp(IPV6_LOCALHOST, false, false, only_v6)?;
assert_eq!(socket.only_v6()?, only_v6);

only_v6 = false;

let socket = syscall::bind_udp(IPV6_LOCALHOST, false, false, only_v6)?;
assert_eq!(socket.only_v6()?, only_v6);

Ok(())
}
3 changes: 2 additions & 1 deletion quic/s2n-quic-platform/src/io/xdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ pub mod socket {
interface: &::std::ffi::CStr,
addr: ::std::net::SocketAddr,
) -> ::std::io::Result<::std::net::UdpSocket> {
let socket = crate::syscall::udp_socket(addr)?;
let only_v6 = false;
let socket = crate::syscall::udp_socket(addr, only_v6)?;

// associate the socket with a single interface
crate::syscall::bind_to_interface(&socket, interface)?;
Expand Down
4 changes: 3 additions & 1 deletion quic/s2n-quic-platform/src/socket/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct Options {
pub send_buffer: Option<usize>,
pub recv_buffer: Option<usize>,
pub backlog: usize,
pub only_v6: bool,
}

impl Default for Options {
Expand All @@ -47,6 +48,7 @@ impl Default for Options {
recv_buffer: None,
delay: false,
backlog: 4096,
only_v6: false,
}
}
}
Expand All @@ -62,7 +64,7 @@ impl Options {

#[inline]
pub fn build_udp(&self) -> io::Result<UdpSocket> {
let socket = syscall::udp_socket(self.addr)?;
let socket = syscall::udp_socket(self.addr, self.only_v6)?;

if self.gro {
let _ = syscall::configure_gro(&socket);
Expand Down
8 changes: 4 additions & 4 deletions quic/s2n-quic-platform/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,14 @@ pub trait UnixMessage: crate::message::Message {
);
}

pub fn udp_socket(addr: std::net::SocketAddr) -> io::Result<Socket> {
pub fn udp_socket(addr: std::net::SocketAddr, only_v6: bool) -> io::Result<Socket> {
let domain = Domain::for_address(addr);
let socket_type = Type::DGRAM;
let protocol = Some(Protocol::UDP);

let socket = Socket::new(domain, socket_type, protocol)?;

// allow ipv4 to also connect - ignore the error if it fails
let _ = socket.set_only_v6(false);
let _ = socket.set_only_v6(only_v6);

Ok(socket)
}
Expand All @@ -84,14 +83,15 @@ pub fn bind_udp<A: std::net::ToSocketAddrs>(
addr: A,
reuse_address: bool,
reuse_port: bool,
only_v6: bool,
) -> io::Result<Socket> {
let addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
std::io::Error::new(
io::ErrorKind::InvalidInput,
"the provided bind address was empty",
)
})?;
let socket = udp_socket(addr)?;
let socket = udp_socket(addr, only_v6)?;

socket.set_reuse_address(reuse_address)?;

Expand Down

0 comments on commit 343adcc

Please sign in to comment.