Skip to content
Open
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
30 changes: 30 additions & 0 deletions src/backend/libc/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,36 @@ pub(crate) fn ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
}

#[cfg(any(
apple,
freebsdlike,
linux_like,
target_os = "aix",
target_os = "cygwin",
target_os = "nto",
))]
#[inline]
pub(crate) fn set_ipv6_checksum(fd: BorrowedFd<'_>, offset: Option<u32>) -> io::Result<()> {
let offset: c::c_int = match offset {
Some(offset) => offset.try_into().map_err(|_| io::Errno::INVAL)?,
None => -1,
};
setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_CHECKSUM, offset)
}

#[cfg(any(
apple,
freebsdlike,
linux_like,
target_os = "aix",
target_os = "cygwin",
target_os = "nto",
))]
#[inline]
pub(crate) fn ipv6_checksum(fd: BorrowedFd<'_>) -> io::Result<Option<u32>> {
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_CHECKSUM).map(|offset: c::c_int| offset.try_into().ok())
}

#[cfg(any(linux_kernel, target_os = "fuchsia"))]
#[inline]
pub(crate) fn ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
Expand Down
2 changes: 1 addition & 1 deletion src/backend/linux_raw/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub(crate) use linux_raw_sys::{
AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE,
AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_VSOCK, AF_WANPIPE, AF_X25,
AF_XDP, IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH,
IPPROTO_ROUTING, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND,
IPPROTO_ROUTING, IPV6_ADD_MEMBERSHIP, IPV6_CHECKSUM, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND,
IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_PMTUDISC_DO, IPV6_PMTUDISC_DONT,
IPV6_PMTUDISC_INTERFACE, IPV6_PMTUDISC_OMIT, IPV6_PMTUDISC_PROBE, IPV6_PMTUDISC_WANT,
IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
Expand Down
14 changes: 14 additions & 0 deletions src/backend/linux_raw/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,20 @@ pub(crate) fn ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool)
}

#[inline]
pub(crate) fn set_ipv6_checksum(fd: BorrowedFd<'_>, offset: Option<u32>) -> io::Result<()> {
let offset: c::c_int = match offset {
Some(offset) => offset.try_into().map_err(|_| io::Errno::INVAL)?,
None => -1,
};
setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_CHECKSUM, offset)
}

#[inline]
pub(crate) fn ipv6_checksum(fd: BorrowedFd<'_>) -> io::Result<Option<u32>> {
getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_CHECKSUM).map(|offset: c::c_int| offset.try_into().ok())
}

#[inline]
pub(crate) fn ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> {
let level = c::IPPROTO_IP;
Expand Down
52 changes: 52 additions & 0 deletions src/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,58 @@ pub fn ipv6_freebind<Fd: AsFd>(fd: Fd) -> io::Result<bool> {
backend::net::sockopt::ipv6_freebind(fd.as_fd())
}

/// `setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, value)`
///
/// On a raw IPv6 socket, `Some(offset)` asks the kernel to compute and store
/// a checksum at byte offset `offset` in outgoing payloads, and to verify it
/// on incoming payloads. `None` disables this. ICMPv6 sockets always use the
/// kernel-computed checksum and setting this option on them fails.
///
/// The behavior of this option is specified in [RFC 3542 Section 3.1].
///
/// See the [module-level documentation] for more.
///
/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions
/// [RFC 3542 Section 3.1]: https://datatracker.ietf.org/doc/html/rfc3542#section-3.1
#[cfg(any(
apple,
freebsdlike,
linux_like,
target_os = "aix",
target_os = "cygwin",
target_os = "nto",
))]
#[inline]
#[doc(alias = "IPV6_CHECKSUM")]
pub fn set_ipv6_checksum<Fd: AsFd>(fd: Fd, value: Option<u32>) -> io::Result<()> {
backend::net::sockopt::set_ipv6_checksum(fd.as_fd(), value)
}

/// `getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM)`
///
/// Returns the checksum offset on a raw IPv6 socket, or `None` if checksum
/// computation is disabled.
///
/// The behavior of this option is specified in [RFC 3542 Section 3.1].
///
/// See the [module-level documentation] for more.
///
/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions
/// [RFC 3542 Section 3.1]: https://datatracker.ietf.org/doc/html/rfc3542#section-3.1
#[cfg(any(
apple,
freebsdlike,
linux_like,
target_os = "aix",
target_os = "cygwin",
target_os = "nto",
))]
#[inline]
#[doc(alias = "IPV6_CHECKSUM")]
pub fn ipv6_checksum<Fd: AsFd>(fd: Fd) -> io::Result<Option<u32>> {
backend::net::sockopt::ipv6_checksum(fd.as_fd())
}

/// `getsockopt(fd, IPPROTO_IP, SO_ORIGINAL_DST)`
///
/// Even though this corresponds to a `SO_*` constant, it is an `IPPROTO_IP`
Expand Down
29 changes: 29 additions & 0 deletions tests/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,3 +707,32 @@ fn test_sockopts_txtime() {
Err(e) => panic!("{e}"),
}
}

#[cfg(linux_kernel)]
#[test]
fn test_sockopts_ipv6_checksum() {
crate::init();

// Raw sockets require `CAP_NET_RAW`.
let s = match rustix::net::socket(AddressFamily::INET6, SocketType::RAW, Some(ipproto::UDP)) {
Ok(s) => s,
Err(io::Errno::PERM | io::Errno::ACCESS) => return,
Err(e) => panic!("{e}"),
};

assert_eq!(sockopt::ipv6_checksum(&s), Ok(None));

sockopt::set_ipv6_checksum(&s, Some(4)).unwrap();
assert_eq!(sockopt::ipv6_checksum(&s), Ok(Some(4)));

// Offsets which don't fit in a `c_int` are rejected rather than being
// treated as `-1`.
assert_eq!(
sockopt::set_ipv6_checksum(&s, Some(u32::MAX)),
Err(io::Errno::INVAL)
);
assert_eq!(sockopt::ipv6_checksum(&s), Ok(Some(4)));

sockopt::set_ipv6_checksum(&s, None).unwrap();
assert_eq!(sockopt::ipv6_checksum(&s), Ok(None));
}
Loading