Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow configurable IPv4/IPv6 socket binding #2472

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -208,29 +212,34 @@ async fn test<A: ToSocketAddrs>(

static IPV4_LOCALHOST: &str = "127.0.0.1:0";
static IPV6_LOCALHOST: &str = "[::1]:0";
static IPV6_ANY_ADDRESS: &str = "[::]: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 +253,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 +271,18 @@ async fn ipv6_two_socket_test() -> io::Result<()> {
other => other,
}
}

#[cfg(unix)]
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn only_v6_test() -> io::Result<()> {
let mut only_v6 = false;
let server_socket = syscall::bind_udp(IPV6_ANY_ADDRESS, false, false, only_v6)?;
assert_eq!(server_socket.only_v6()?, only_v6);

only_v6 = true;
let server_socket = syscall::bind_udp(IPV6_ANY_ADDRESS, false, false, only_v6)?;
assert_eq!(server_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
Loading