Skip to content

Commit ffc793e

Browse files
bors[bot]matttpt
andauthored
Merge #1776
1776: Add support for the IP_SENDSRCADDR control message r=rtzoeller a=matttpt This control message is available on FreeBSD, NetBSD, and OpenBSD. When used with `sendmsg`, it sets the IPv4 source address. This adds support through a new `ControlMessage::Ipv4SendSrcAddr` variant that complements `ControlMessageOwned::Ipv4RecvDstAddr`. A few notes: * `IP_SENDSRCADDR` is actually just an alias for `IP_RECVDSTADDR` (though the code doesn't depend on this). * On NetBSD, `IP_PKTINFO` can be used to accomplish the same thing and is already supported by nix. On FreeBSD and OpenBSD, though, `IP_SENDSRCADDR` is the only method I'm aware of. * The accompanying test binds a UDP socket to all local interfaces (0.0.0.0). If this is not acceptable, please let me know; however, FreeBSD requires this to use `IP_SENDSRCADDR`. I'll add a change-log entry once I see the PR number. Thanks! Co-authored-by: Matthew Ingwersen <matttpt@gmail.com>
2 parents a9861d8 + 6bf07fd commit ffc793e

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
4040
(#[1772](https://github.com/nix-rust/nix/pull/1772))
4141
- Added IPV6_ORIGDSTADDR using Ipv6OrigDstAddr in setsockopt and recvmsg.
4242
(#[1772](https://github.com/nix-rust/nix/pull/1772))
43+
- Added `IP_SENDSRCADDR` using `Ipv4SendSrcAddr` in `sendmsg`.
44+
(#[1776](https://github.com/nix-rust/nix/pull/1776))
4345

4446
### Changed
4547

src/sys/socket/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,17 @@ pub enum ControlMessage<'a> {
11171117
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
11181118
Ipv6PacketInfo(&'a libc::in6_pktinfo),
11191119

1120+
/// Configure the IPv4 source address with `IP_SENDSRCADDR`.
1121+
#[cfg(any(
1122+
target_os = "netbsd",
1123+
target_os = "freebsd",
1124+
target_os = "openbsd",
1125+
target_os = "dragonfly",
1126+
))]
1127+
#[cfg(feature = "net")]
1128+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
1129+
Ipv4SendSrcAddr(&'a libc::in_addr),
1130+
11201131
/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
11211132
/// ancilliary msg (cmsg) should be attached to recieved
11221133
/// skbs indicating the number of packets dropped by the
@@ -1226,6 +1237,10 @@ impl<'a> ControlMessage<'a> {
12261237
target_os = "android", target_os = "ios",))]
12271238
#[cfg(feature = "net")]
12281239
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
1240+
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
1241+
target_os = "openbsd", target_os = "dragonfly"))]
1242+
#[cfg(feature = "net")]
1243+
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
12291244
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
12301245
ControlMessage::RxqOvfl(drop_count) => {
12311246
drop_count as *const _ as *const u8
@@ -1285,6 +1300,10 @@ impl<'a> ControlMessage<'a> {
12851300
target_os = "android", target_os = "ios",))]
12861301
#[cfg(feature = "net")]
12871302
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
1303+
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
1304+
target_os = "openbsd", target_os = "dragonfly"))]
1305+
#[cfg(feature = "net")]
1306+
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
12881307
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
12891308
ControlMessage::RxqOvfl(drop_count) => {
12901309
mem::size_of_val(drop_count)
@@ -1320,6 +1339,10 @@ impl<'a> ControlMessage<'a> {
13201339
target_os = "android", target_os = "ios",))]
13211340
#[cfg(feature = "net")]
13221341
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
1342+
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
1343+
target_os = "openbsd", target_os = "dragonfly"))]
1344+
#[cfg(feature = "net")]
1345+
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
13231346
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
13241347
ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
13251348
#[cfg(target_os = "linux")]
@@ -1362,6 +1385,10 @@ impl<'a> ControlMessage<'a> {
13621385
target_os = "android", target_os = "ios",))]
13631386
#[cfg(feature = "net")]
13641387
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
1388+
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
1389+
target_os = "openbsd", target_os = "dragonfly"))]
1390+
#[cfg(feature = "net")]
1391+
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
13651392
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
13661393
ControlMessage::RxqOvfl(_) => {
13671394
libc::SO_RXQ_OVFL

test/sys/test_socket.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,58 @@ pub fn test_sendmsg_ipv6packetinfo() {
11221122
.expect("sendmsg");
11231123
}
11241124

1125+
// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This
1126+
// creates a UDP socket bound to all local interfaces (0.0.0.0). It then
1127+
// sends message to itself at 127.0.0.1 while explicitly specifying
1128+
// 127.0.0.1 as the source address through an Ipv4SendSrcAddr
1129+
// (IP_SENDSRCADDR) control message.
1130+
//
1131+
// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg
1132+
// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.)
1133+
#[cfg(any(
1134+
target_os = "netbsd",
1135+
target_os = "freebsd",
1136+
target_os = "openbsd",
1137+
target_os = "dragonfly",
1138+
))]
1139+
#[test]
1140+
pub fn test_sendmsg_ipv4sendsrcaddr() {
1141+
use nix::sys::socket::{
1142+
bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
1143+
SockFlag, SockType, SockaddrIn,
1144+
};
1145+
use std::io::IoSlice;
1146+
1147+
let sock = socket(
1148+
AddressFamily::Inet,
1149+
SockType::Datagram,
1150+
SockFlag::empty(),
1151+
None,
1152+
)
1153+
.expect("socket failed");
1154+
1155+
let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0);
1156+
bind(sock, &unspec_sock_addr).expect("bind failed");
1157+
let bound_sock_addr: SockaddrIn = getsockname(sock).unwrap();
1158+
let localhost_sock_addr: SockaddrIn =
1159+
SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port());
1160+
1161+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
1162+
let iov = [IoSlice::new(&slice)];
1163+
let cmsg = [ControlMessage::Ipv4SendSrcAddr(
1164+
&localhost_sock_addr.as_ref().sin_addr,
1165+
)];
1166+
1167+
sendmsg(
1168+
sock,
1169+
&iov,
1170+
&cmsg,
1171+
MsgFlags::empty(),
1172+
Some(&localhost_sock_addr),
1173+
)
1174+
.expect("sendmsg");
1175+
}
1176+
11251177
/// Tests that passing multiple fds using a single `ControlMessage` works.
11261178
// Disable the test on emulated platforms due to a bug in QEMU versions <
11271179
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808

0 commit comments

Comments
 (0)