Skip to content

Commit

Permalink
udp: add support for ECN on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
stormshield-damiend committed Dec 19, 2023
1 parent 7723cbc commit 7a9cd0d
Show file tree
Hide file tree
Showing 11 changed files with 652 additions and 194 deletions.
1 change: 1 addition & 0 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,7 @@ impl Connection {
/// Retrieving the local IP address is currently supported on the following
/// platforms:
/// - Linux
/// - Windows
///
/// On all non-supported platforms the local IP address will not be available,
/// and the method will return `None`.
Expand Down
3 changes: 2 additions & 1 deletion quinn-udp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ socket2 = "0.5"
tracing = "0.1.10"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52.0", features = ["Win32_Networking_WinSock"] }
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_System_IO", "Win32_Networking_WinSock"] }
once_cell = "1.19.0"
113 changes: 0 additions & 113 deletions quinn-udp/src/cmsg.rs

This file was deleted.

142 changes: 142 additions & 0 deletions quinn-udp/src/cmsg/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::{
ffi::{c_int, c_uchar},
mem, ptr,
};

#[cfg(unix)]
#[path = "unix.rs"]
mod imp;

#[cfg(windows)]
#[path = "windows.rs"]
mod imp;

pub(crate) use imp::Aligned;

// Helper traits for native types for control messages
pub(crate) trait MsgHdr {
type ControlMessage: CMsgHdr;

fn control_len(&self) -> usize;

fn set_control_len(&mut self, len: usize);

fn cmsg_firsthdr(&self) -> *mut Self::ControlMessage;

fn cmsg_nxthdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;
}

pub(crate) trait CMsgHdr {
fn set(&mut self, level: c_int, ty: c_int, len: usize);

fn len(&self) -> usize;

fn cmsg_space(length: usize) -> usize;

fn cmsg_len(length: usize) -> usize;

fn cmsg_data(&self) -> *mut c_uchar;
}

/// Helper to encode a series of control messages (native "cmsgs") to a buffer for use in `sendmsg`
// like API.
///
/// The operation must be "finished" for the native msghdr to be usable, either by calling `finish`
/// explicitly or by dropping the `Encoder`.
pub(crate) struct Encoder<'a, M: MsgHdr> {
hdr: &'a mut M,
cmsg: Option<&'a mut M::ControlMessage>,
len: usize,
}

impl<'a, M: MsgHdr> Encoder<'a, M> {
/// # Safety
/// - `hdr` must contains a suitably aligned pointer to a buffer big enought to hold the control messages bytes
// that can be safely written
/// - The `Encoder` must be dropped before `hdr` is passed to a system call, and must not be leaked.
pub(crate) unsafe fn new(hdr: &'a mut M) -> Self {
Self {
cmsg: hdr.cmsg_firsthdr().as_mut(),
hdr,
len: 0,
}
}

/// Append a control message to the buffer.
///
/// # Panics
/// - If insufficient buffer space remains.
/// - If `T` has stricter alignment requirements than native `cmsghdr`
pub(crate) fn push<T: Copy + ?Sized>(&mut self, level: c_int, ty: c_int, value: T) {
assert!(mem::align_of::<T>() <= mem::align_of::<M::ControlMessage>());
let space = M::ControlMessage::cmsg_space(mem::size_of_val(&value));
assert!(
self.hdr.control_len() >= self.len + space,
"control message buffer too small. Required: {}, Available: {}",
self.len + space,
self.hdr.control_len()
);
let cmsg = self.cmsg.take().expect("no control buffer space remaining");
cmsg.set(
level,
ty,
M::ControlMessage::cmsg_len(mem::size_of_val(&value)),
);
unsafe {
ptr::write(cmsg.cmsg_data() as *const T as *mut T, value);
}
self.len += space;
self.cmsg = unsafe { self.hdr.cmsg_nxthdr(cmsg).as_mut() };
}

/// Finishes appending control messages to the buffer
pub(crate) fn finish(self) {
// Delegates to the `Drop` impl
}
}

// Statically guarantees that the encoding operation is "finished" before the control buffer is read
// by `sendmsg` like API.
impl<'a, M: MsgHdr> Drop for Encoder<'a, M> {
fn drop(&mut self) {
self.hdr.set_control_len(self.len as _);
}
}

/// # Safety
///
/// `cmsg` must refer to a native cmsg containing a payload of type `T`
pub(crate) unsafe fn decode<T: Copy, C: CMsgHdr>(cmsg: &C) -> T {
assert!(mem::align_of::<T>() <= mem::align_of::<C>());
debug_assert_eq!(cmsg.len(), C::cmsg_len(mem::size_of::<T>()));
ptr::read(cmsg.cmsg_data() as *const T)
}

pub(crate) struct Iter<'a, M: MsgHdr> {
hdr: &'a M,
cmsg: Option<&'a M::ControlMessage>,
}

impl<'a, M: MsgHdr> Iter<'a, M> {
/// # Safety
///
/// `hdr` must hold a pointer to memory outliving `'a` which can be soundly read for the
/// lifetime of the constructed `Iter` and contains a buffer of native cmsgs, i.e. is aligned
// for native `cmsghdr`, is fully initialized, and has correct internal links.
pub(crate) unsafe fn new(hdr: &'a M) -> Self {
Self {
hdr,
cmsg: hdr.cmsg_firsthdr().as_ref(),
}
}
}

impl<'a, M: MsgHdr> Iterator for Iter<'a, M> {
type Item = &'a M::ControlMessage;

fn next(&mut self) -> Option<Self::Item> {
let current = self.cmsg.take()?;
self.cmsg = unsafe { self.hdr.cmsg_nxthdr(current).as_ref() };
Some(current)
}
}
53 changes: 53 additions & 0 deletions quinn-udp/src/cmsg/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::ffi::{c_int, c_uchar};

use super::{CMsgHdr, MsgHdr};

#[derive(Copy, Clone)]
#[repr(align(8))] // Conservative bound for align_of<libc::cmsghdr>
pub(crate) struct Aligned<T>(pub(crate) T);

/// Helpers for [`libc::msghdr`]
impl MsgHdr for libc::msghdr {
type ControlMessage = libc::cmsghdr;

fn control_len(&self) -> usize {
self.msg_controllen as _
}

fn set_control_len(&mut self, len: usize) {
self.msg_controllen = len as _;
}

fn cmsg_firsthdr(&self) -> *mut Self::ControlMessage {
unsafe { libc::CMSG_FIRSTHDR(self) }
}

fn cmsg_nxthdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
unsafe { libc::CMSG_NXTHDR(self, cmsg) }
}
}

/// Helpers for [`libc::cmsghdr`]
impl CMsgHdr for libc::cmsghdr {
fn set(&mut self, level: c_int, ty: c_int, len: usize) {
self.cmsg_level = level as _;
self.cmsg_type = ty as _;
self.cmsg_len = len as _;
}

fn len(&self) -> usize {
self.cmsg_len as _
}

fn cmsg_space(length: usize) -> usize {
unsafe { libc::CMSG_SPACE(length as _) as usize }
}

fn cmsg_len(length: usize) -> usize {
unsafe { libc::CMSG_LEN(length as _) as usize }
}

fn cmsg_data(&self) -> *mut c_uchar {
unsafe { libc::CMSG_DATA(self) }
}
}
Loading

0 comments on commit 7a9cd0d

Please sign in to comment.