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

refactor(s2n-quic-platform): move socket config to separate module #1737

Merged
merged 1 commit into from
May 2, 2023
Merged
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: 5 additions & 0 deletions quic/s2n-quic-core/src/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ impl Handle for Tuple {
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MaxMtu(NonZeroU16);

impl MaxMtu {
/// The minimum value required for path MTU
pub const MIN: Self = Self(unsafe { NonZeroU16::new_unchecked(MIN_ALLOWED_MAX_MTU) });
}

impl Default for MaxMtu {
fn default() -> Self {
DEFAULT_MAX_MTU
Expand Down
185 changes: 25 additions & 160 deletions quic/s2n-quic-platform/src/io/tokio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use super::select::{self, Select};
use crate::{buffer::default as buffer, features::gso, socket::default as socket};
use crate::{buffer::default as buffer, features::gso, socket::default as socket, syscall};
use cfg_if::cfg_if;
use s2n_quic_core::{
endpoint::Endpoint,
Expand Down Expand Up @@ -60,13 +60,11 @@ impl Io {
send_addr,
recv_buffer_size,
send_buffer_size,
max_mtu,
mut max_mtu,
max_segments,
reuse_port,
} = self.builder;

endpoint.set_max_mtu(max_mtu);

let clock = Clock::default();

let mut publisher = event::EndpointPublisherSubscriber::new(
Expand All @@ -78,12 +76,6 @@ impl Io {
endpoint.subscriber(),
);

publisher.on_platform_feature_configured(event::builder::PlatformFeatureConfigured {
configuration: event::builder::PlatformFeatureConfiguration::MaxMtu {
mtu: max_mtu.into(),
},
});

publisher.on_platform_feature_configured(event::builder::PlatformFeatureConfigured {
configuration: event::builder::PlatformFeatureConfiguration::Gso {
max_segments: max_segments.into(),
Expand All @@ -103,7 +95,7 @@ impl Io {
rx_socket.set_nonblocking(true)?;
rx_socket
} else if let Some(recv_addr) = recv_addr {
bind(recv_addr, reuse_port)?
syscall::bind_udp(recv_addr, reuse_port)?
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
Expand All @@ -116,7 +108,7 @@ impl Io {
tx_socket.set_nonblocking(true)?;
tx_socket
} else if let Some(send_addr) = send_addr {
bind(send_addr, reuse_port)?
syscall::bind_udp(send_addr, reuse_port)?
} else {
// No tx_socket or send address was specified, so the tx socket
// will be a handle to the rx socket.
Expand All @@ -143,108 +135,30 @@ impl Io {
convert_addr_to_std(rx_socket.local_addr()?)?,
);

//= https://www.rfc-editor.org/rfc/rfc9000#section-14
//# UDP datagrams MUST NOT be fragmented at the IP layer.

//= https://www.rfc-editor.org/rfc/rfc9000#section-14
//# In IPv4 [IPv4], the Don't Fragment (DF) bit MUST be set if possible, to
//# prevent fragmentation on the path.

//= https://www.rfc-editor.org/rfc/rfc8899#section-3
//# In IPv4, a probe packet MUST be sent with the Don't
//# Fragment (DF) bit set in the IP header and without network layer
//# endpoint fragmentation.

//= https://www.rfc-editor.org/rfc/rfc8899#section-4.5
//# A PL implementing this specification MUST suspend network layer
//# processing of outgoing packets that enforces a PMTU
//# [RFC1191][RFC8201] for each flow utilizing DPLPMTUD and instead use
//# DPLPMTUD to control the size of packets that are sent by a flow.
#[cfg(s2n_quic_platform_mtu_disc)]
{
use std::os::unix::io::AsRawFd;

// IP_PMTUDISC_PROBE setting will set the DF (Don't Fragment) flag
// while also ignoring the Path MTU. This means packets will not
// be fragmented, and the EMSGSIZE error will not be returned for
// packets larger than the Path MTU according to the kernel.
libc!(setsockopt(
tx_socket.as_raw_fd(),
libc::IPPROTO_IP,
libc::IP_MTU_DISCOVER,
&libc::IP_PMTUDISC_PROBE as *const _ as _,
core::mem::size_of_val(&libc::IP_PMTUDISC_PROBE) as _,
))?;

if tx_addr.is_ipv6() {
libc!(setsockopt(
tx_socket.as_raw_fd(),
libc::IPPROTO_IPV6,
libc::IPV6_MTU_DISCOVER,
&libc::IP_PMTUDISC_PROBE as *const _ as _,
core::mem::size_of_val(&libc::IP_PMTUDISC_PROBE) as _,
))?;
}
// Configure MTU discovery
if !syscall::configure_mtu_disc(&tx_socket) {
// disable MTU probing if we can't prevent fragmentation
max_mtu = MaxMtu::MIN;
}

// Set up the RX socket to pass ECN information
#[cfg(s2n_quic_platform_tos)]
{
use std::os::unix::io::AsRawFd;
let enabled: libc::c_int = 1;

// This option needs to be enabled regardless of domain (IPv4 vs IPv6), except on mac
if rx_addr.is_ipv4() || !cfg!(any(target_os = "macos", target_os = "ios")) {
libc!(setsockopt(
rx_socket.as_raw_fd(),
libc::IPPROTO_IP,
libc::IP_RECVTOS,
&enabled as *const _ as _,
core::mem::size_of_val(&enabled) as _,
))?;
}
publisher.on_platform_feature_configured(event::builder::PlatformFeatureConfigured {
configuration: event::builder::PlatformFeatureConfiguration::MaxMtu {
mtu: max_mtu.into(),
},
});

// Configure packet info CMSG
syscall::configure_pktinfo(&rx_socket);

// Configure TOS/ECN
let tos_enabled = syscall::configure_tos(&rx_socket);

if rx_addr.is_ipv6() {
libc!(setsockopt(
rx_socket.as_raw_fd(),
libc::IPPROTO_IPV6,
libc::IPV6_RECVTCLASS,
&enabled as *const _ as _,
core::mem::size_of_val(&enabled) as _,
))?;
}
}
publisher.on_platform_feature_configured(event::builder::PlatformFeatureConfigured {
configuration: event::builder::PlatformFeatureConfiguration::Ecn {
enabled: cfg!(s2n_quic_platform_tos),
enabled: tos_enabled,
},
});

// Set up the RX socket to pass information about the local address and interface
#[cfg(s2n_quic_platform_pktinfo)]
{
use std::os::unix::io::AsRawFd;
let enabled: libc::c_int = 1;

if rx_addr.is_ipv4() {
libc!(setsockopt(
rx_socket.as_raw_fd(),
libc::IPPROTO_IP,
libc::IP_PKTINFO,
&enabled as *const _ as _,
core::mem::size_of_val(&enabled) as _,
))?;
} else {
libc!(setsockopt(
rx_socket.as_raw_fd(),
libc::IPPROTO_IPV6,
libc::IPV6_RECVPKTINFO,
&enabled as *const _ as _,
core::mem::size_of_val(&enabled) as _,
))?;
}
}

let rx_buffer = buffer::Buffer::new_with_mtu(max_mtu.into());
let tx_buffer = buffer::Buffer::new_with_mtu(max_mtu.into());
cfg_if! {
Expand All @@ -267,6 +181,9 @@ impl Io {
addr.into()
});

// Notify the endpoint of the MTU that we chose
endpoint.set_max_mtu(max_mtu);

let instance = Instance {
clock,
rx_socket: rx_socket.into(),
Expand Down Expand Up @@ -295,58 +212,6 @@ impl Io {
}
}

fn bind<A: std::net::ToSocketAddrs>(addr: A, reuse_port: bool) -> io::Result<socket2::Socket> {
use socket2::{Domain, Protocol, Socket, Type};

let addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
std::io::Error::new(
io::ErrorKind::InvalidInput,
"the provided bind address was empty",
)
})?;

let domain = Domain::for_address(addr);
let socket_type = Type::DGRAM;
let protocol = Some(Protocol::UDP);

cfg_if! {
// Set non-blocking mode in a single syscall if supported
if #[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
))] {
let socket_type = socket_type.nonblocking();
let socket = Socket::new(domain, socket_type, protocol)?;
} else {
let socket = Socket::new(domain, socket_type, protocol)?;
socket.set_nonblocking(true)?;
}
};

// allow ipv4 to also connect
if addr.is_ipv6() {
socket.set_only_v6(false)?;
}

socket.set_reuse_address(true)?;

#[cfg(unix)]
socket.set_reuse_port(reuse_port)?;

// mark the variable as "used" regardless of platform support
let _ = reuse_port;

socket.bind(&addr.into())?;

Ok(socket)
}

#[derive(Debug, Default)]
pub struct Builder {
handle: Option<Handle>,
Expand Down Expand Up @@ -778,14 +643,14 @@ mod tests {
receive_addr: A,
send_addr: Option<A>,
) -> io::Result<()> {
let rx_socket = bind(receive_addr, false)?;
let rx_socket = syscall::bind_udp(receive_addr, false)?;
let rx_socket: std::net::UdpSocket = rx_socket.into();
let addr = rx_socket.local_addr()?;

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

if let Some(addr) = send_addr {
let tx_socket = bind(addr, false)?;
let tx_socket = syscall::bind_udp(addr, false)?;
let tx_socket: std::net::UdpSocket = tx_socket.into();
io_builder = io_builder.with_tx_socket(tx_socket)?
}
Expand Down
1 change: 1 addition & 0 deletions quic/s2n-quic-platform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ pub mod features;
pub mod io;
pub mod message;
pub mod socket;
mod syscall;
pub mod time;
Loading