From 6bf07fdbb9e77984bd1c4f080d41a85ff821a2cb Mon Sep 17 00:00:00 2001 From: Matthew Ingwersen Date: Sun, 31 Jul 2022 22:24:22 +0000 Subject: [PATCH] Add support for the IP_SENDSRCADDR control message 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. --- CHANGELOG.md | 2 ++ src/sys/socket/mod.rs | 27 +++++++++++++++++++++ test/sys/test_socket.rs | 52 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe811971c..96de857eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 2dfa8ec6e6..a853777838 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -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 @@ -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 @@ -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) @@ -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")] @@ -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 diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 06d5b76166..b4ca279d67 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -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