Skip to content
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
94 changes: 92 additions & 2 deletions src/control_message.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,48 @@
use std::marker::PhantomData;
use std::{marker::PhantomData, net::IpAddr};

use crate::socket::Timestamp;

pub(crate) const fn control_message_space<T>() -> usize {
#[cfg(target_os = "linux")]
const SCM_TIMESTAMPING_CMSG_SIZE: usize = control_message_space::<[libc::timespec; 3]>();
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const SCM_TIMESTAMP_NS_CMSG_SIZE: usize = control_message_space::<libc::timespec>();
const SCM_TIMESTAMP_CMSG_SIZE: usize = control_message_space::<libc::timeval>();
#[cfg(target_os = "linux")]
const RECEIVERR_CMSG_SIZE: usize =
control_message_space::<(libc::sock_extended_err, libc::sockaddr_storage)>();
#[cfg(target_os = "linux")]
const IP_PKTINFO_CMSG_SIZE: usize = control_message_space::<libc::in_pktinfo>();
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
const IP_RECVDSTADDR_CMSG_SIZE: usize = control_message_space::<libc::in_addr>();
const IP6_PKTINFO_CMSG_SIZE: usize = control_message_space::<libc::in6_pktinfo>();

// Utility needed since the ord trait max function is not usable in const environments
const fn max(a: usize, b: usize) -> usize {
if a > b {
a
} else {
b
}
}

#[cfg(target_os = "linux")]
pub(crate) const EXPECTED_MAX_CMSG_SIZE: usize =
max(
max(SCM_TIMESTAMPING_CMSG_SIZE, SCM_TIMESTAMP_NS_CMSG_SIZE),
SCM_TIMESTAMP_CMSG_SIZE,
) + max(IP_PKTINFO_CMSG_SIZE, IP6_PKTINFO_CMSG_SIZE)
+ RECEIVERR_CMSG_SIZE;
#[cfg(target_os = "freebsd")]
pub(crate) const EXPECTED_MAX_CMSG_SIZE: usize =
max(SCM_TIMESTAMP_NS_CMSG_SIZE, SCM_TIMESTAMP_CMSG_SIZE)
+ max(IP_RECVDSTADDR_CMSG_SIZE, IP6_PKTINFO_CMSG_SIZE);
#[cfg(target_os = "macos")]
pub(crate) const EXPECTED_MAX_CMSG_SIZE: usize =
SCM_TIMESTAMP_CMSG_SIZE + max(IP_RECVDSTADDR_CMSG_SIZE, IP6_PKTINFO_CMSG_SIZE);
#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
pub(crate) const EXPECTED_MAX_CMSG_SIZE: usize = SCM_TIMESTAMP_CMSG_SIZE + IP6_PKTINFO_CMSG_SIZE;

const fn control_message_space<T>() -> usize {
// Safety: CMSG_SPACE is safe to call
(unsafe { libc::CMSG_SPACE((std::mem::size_of::<T>()) as _) }) as usize
}
Expand Down Expand Up @@ -66,6 +106,7 @@ pub(crate) enum ControlMessage {
},
#[cfg(target_os = "linux")]
ReceiveError(libc::sock_extended_err),
DestinationIp(IpAddr),
Other(libc::cmsghdr),
}

Expand Down Expand Up @@ -168,6 +209,55 @@ impl Iterator for ControlMessageIterator<'_> {

ControlMessage::ReceiveError(error)
}

#[cfg(target_os = "linux")]
(libc::SOL_IP, libc::IP_PKTINFO) => {
// Safety:
// current_msg was constructed from a pointer that pointed to a valid
// control message.
// IP_PKTINFO always has a in_pktinfo in the data
let pktinfo = unsafe {
let ptr = libc::CMSG_DATA(current_msg) as *const libc::in_pktinfo;
std::ptr::read_unaligned(ptr)
};

ControlMessage::DestinationIp(
std::net::Ipv4Addr::from_bits(u32::from_be(pktinfo.ipi_addr.s_addr)).into(),
)
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
// Safety:
// current_msg was constructed from a pointer that pointed to a valid
// control message.
// IP_RECVDSTADDR always has a in_addr in the data
let in_addr = unsafe {
let ptr = libc::CMSG_DATA(current_msg) as *const libc::in_addr;
std::ptr::read_unaligned(ptr)
};

ControlMessage::DestinationIp(
std::net::Ipv4Addr::from_bits(u32::from_be(in_addr.s_addr)).into(),
)
}

(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
// Safety:
// current_msg was constructed from a pointer that pointed to a valid
// control message.
// IPV6_PKTINFO always has a in6_pktinfo in the data
let pktinfo = unsafe {
let ptr = libc::CMSG_DATA(current_msg) as *const libc::in6_pktinfo;
std::ptr::read_unaligned(ptr)
};

ControlMessage::DestinationIp(
std::net::Ipv6Addr::from_bits(u128::from_be_bytes(pktinfo.ipi6_addr.s6_addr))
.into(),
)
}

_ => ControlMessage::Other(*current_msg),
})
}
Expand Down
38 changes: 36 additions & 2 deletions src/networkaddress.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
os::fd::RawFd,
};

Expand All @@ -24,11 +24,15 @@ pub(crate) mod sealed {
pub struct PrivateToken;
}

pub trait NetworkAddress: Sized + SealedNA {
pub trait NetworkAddress: Copy + Sized + SealedNA {
#[doc(hidden)]
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage;
#[doc(hidden)]
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self>;
#[doc(hidden)]
fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self>;
#[doc(hidden)]
fn port(&self) -> u16;
}

pub trait MulticastJoinable: NetworkAddress + SealedMC {
Expand Down Expand Up @@ -98,6 +102,17 @@ impl NetworkAddress for SocketAddrV4 {
u16::from_be_bytes(input.sin_port.to_ne_bytes()),
))
}

fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
match addr {
IpAddr::V4(addr) => Some(SocketAddrV4::new(addr, port)),
IpAddr::V6(_) => None,
}
}

fn port(&self) -> u16 {
self.port()
}
}

impl SealedNA for SocketAddrV6 {}
Expand Down Expand Up @@ -154,6 +169,17 @@ impl NetworkAddress for SocketAddrV6 {
input.sin6_scope_id,
))
}

fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
match addr {
IpAddr::V4(_) => None,
IpAddr::V6(addr) => Some(SocketAddrV6::new(addr, port, 0, 0)),
}
}

fn port(&self) -> u16 {
self.port()
}
}

impl SealedNA for SocketAddr {}
Expand All @@ -179,4 +205,12 @@ impl NetworkAddress for SocketAddr {
_ => None,
}
}

fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
Some(SocketAddr::new(addr, port))
}

fn port(&self) -> u16 {
self.port()
}
}
9 changes: 9 additions & 0 deletions src/networkaddress/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ impl NetworkAddress for EthernetAddress {
input.sll_ifindex,
))
}

fn from_ip_and_port(_addr: std::net::IpAddr, _port: u16) -> Option<Self> {
None
}

fn port(&self) -> u16 {
// Ethernet doesn't have a port, zero is a decent sentinal value to cover that.
0
}
}

impl SealedMC for EthernetAddress {}
Expand Down
Loading