Skip to content

Commit

Permalink
Add support for the IP_SENDSRCADDR control message
Browse files Browse the repository at this point in the history
This control message (actually just an alias for IP_RECVDSTADDR) sets
the IPv4 source address when used with sendmsg. It is available on
FreeBSD, NetBSD, OpenBSD, and DragonFlyBSD.
  • Loading branch information
matttpt committed Aug 5, 2022
1 parent 67329c5 commit 6bf07fd
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -38,6 +38,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1772](https://github.com/nix-rust/nix/pull/1772))
- Added IPV6_ORIGDSTADDR using Ipv6OrigDstAddr in setsockopt and recvmsg.
(#[1772](https://github.com/nix-rust/nix/pull/1772))
- Added `IP_SENDSRCADDR` using `Ipv4SendSrcAddr` in `sendmsg`.
(#[1776](https://github.com/nix-rust/nix/pull/1776))

### Changed

Expand Down
27 changes: 27 additions & 0 deletions src/sys/socket/mod.rs
Expand Up @@ -1117,6 +1117,17 @@ pub enum ControlMessage<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),

/// Configure the IPv4 source address with `IP_SENDSRCADDR`.
#[cfg(any(
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4SendSrcAddr(&'a libc::in_addr),

/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
/// ancilliary msg (cmsg) should be attached to recieved
/// skbs indicating the number of packets dropped by the
Expand Down Expand Up @@ -1226,6 +1237,10 @@ impl<'a> ControlMessage<'a> {
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(drop_count) => {
drop_count as *const _ as *const u8
Expand Down Expand Up @@ -1285,6 +1300,10 @@ impl<'a> ControlMessage<'a> {
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(drop_count) => {
mem::size_of_val(drop_count)
Expand Down Expand Up @@ -1320,6 +1339,10 @@ impl<'a> ControlMessage<'a> {
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -1362,6 +1385,10 @@ impl<'a> ControlMessage<'a> {
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(_) => {
libc::SO_RXQ_OVFL
Expand Down
52 changes: 52 additions & 0 deletions test/sys/test_socket.rs
Expand Up @@ -1122,6 +1122,58 @@ pub fn test_sendmsg_ipv6packetinfo() {
.expect("sendmsg");
}

// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This
// creates a UDP socket bound to all local interfaces (0.0.0.0). It then
// sends message to itself at 127.0.0.1 while explicitly specifying
// 127.0.0.1 as the source address through an Ipv4SendSrcAddr
// (IP_SENDSRCADDR) control message.
//
// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg
// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.)
#[cfg(any(
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
#[test]
pub fn test_sendmsg_ipv4sendsrcaddr() {
use nix::sys::socket::{
bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
SockFlag, SockType, SockaddrIn,
};
use std::io::IoSlice;

let sock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("socket failed");

let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0);
bind(sock, &unspec_sock_addr).expect("bind failed");
let bound_sock_addr: SockaddrIn = getsockname(sock).unwrap();
let localhost_sock_addr: SockaddrIn =
SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port());

let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
let iov = [IoSlice::new(&slice)];
let cmsg = [ControlMessage::Ipv4SendSrcAddr(
&localhost_sock_addr.as_ref().sin_addr,
)];

sendmsg(
sock,
&iov,
&cmsg,
MsgFlags::empty(),
Some(&localhost_sock_addr),
)
.expect("sendmsg");
}

/// Tests that passing multiple fds using a single `ControlMessage` works.
// Disable the test on emulated platforms due to a bug in QEMU versions <
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
Expand Down

0 comments on commit 6bf07fd

Please sign in to comment.