Skip to content

Commit

Permalink
set may_fragment based on whether setting IP_DONTFRAG fails with ENOP…
Browse files Browse the repository at this point in the history
…ROTOOPT.
  • Loading branch information
JieLiang Ma authored and djc committed Aug 15, 2023
1 parent 7b72b71 commit b4cc111
Showing 1 changed file with 27 additions and 7 deletions.
34 changes: 27 additions & 7 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct UdpSocketState {
last_send_error: Mutex<Instant>,
max_gso_segments: AtomicUsize,
gro_segments: usize,
may_fragment: bool,

/// True if we have received EINVAL error from `sendmsg` or `sendmmsg` system call at least once.
///
Expand Down Expand Up @@ -74,34 +75,39 @@ impl UdpSocketState {
}
}

let mut may_fragment = false;
#[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(
let result = set_socket_option(
&*io,
libc::IPPROTO_IP,
libc::IP_MTU_DISCOVER,
libc::IP_PMTUDISC_PROBE,
)?;
);
may_fragment = is_result_noprotoopt_err(result) || may_fragment;

if is_ipv4 {
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
} else {
set_socket_option(
let result = set_socket_option(
&*io,
libc::IPPROTO_IPV6,
libc::IPV6_MTU_DISCOVER,
libc::IP_PMTUDISC_PROBE,
)?;
);
may_fragment = is_result_noprotoopt_err(result) || may_fragment;
}
}
#[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)?;
let result =
set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_DONTFRAG, OPTION_ON);
may_fragment = is_result_noprotoopt_err(result) || may_fragment;
}
}
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
Expand All @@ -121,14 +127,17 @@ impl UdpSocketState {
// 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 result =
set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_DONTFRAG, OPTION_ON);
may_fragment = is_result_noprotoopt_err(result) || may_fragment;
}

let now = Instant::now();
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(),
may_fragment,
sendmsg_einval: AtomicBool::new(false),
})
}
Expand Down Expand Up @@ -170,7 +179,7 @@ impl UdpSocketState {
/// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
#[inline]
pub fn may_fragment(&self) -> bool {
false
self.may_fragment
}

/// Returns true if we previously got an EINVAL error from `sendmsg` or `sendmmsg` syscall.
Expand Down Expand Up @@ -811,6 +820,17 @@ fn set_socket_option(
}
}

#[inline]
fn is_result_noprotoopt_err(result: Result<(), io::Error>) -> bool {
match result {
Err(error) => match error.raw_os_error() {
Some(raw_os_error) => raw_os_error == libc::ENOPROTOOPT,
None => false,
},
Ok(_) => false,
}
}

const OPTION_ON: libc::c_int = 1;

#[cfg(not(target_os = "linux"))]
Expand Down

0 comments on commit b4cc111

Please sign in to comment.