Skip to content

Commit

Permalink
Merge #1222
Browse files Browse the repository at this point in the history
1222: Add Ipv{4,6}PacketInfo support to ControlMessage for send{m,}msg. r=asomers a=isomer

This adds Ipv4PacketInfo and Ipv6PacketInfo to ControlMessage, allowing these to be used with sendmsg/sendmmsg.

Co-authored-by: Perry Lorier <perryl@google.com>
  • Loading branch information
bors[bot] and isomer committed Jun 27, 2020
2 parents ea099dd + 78347d1 commit 2c42b30
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -33,6 +33,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
(#[1233](https://github.com/nix-rust/nix/pull/1233))
- Added `EventFilter` bitflags for `EV_DISPATCH` and `EV_RECEIPT` on OpenBSD.
(#[1252](https://github.com/nix-rust/nix/pull/1252))
- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage`.
(#[1222](https://github.com/nix-rust/nix/pull/1222))

### Changed
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))
Expand Down
43 changes: 43 additions & 0 deletions src/sys/socket/mod.rs
Expand Up @@ -717,6 +717,25 @@ pub enum ControlMessage<'a> {
/// following one by one, and the last, possibly smaller one.
#[cfg(target_os = "linux")]
UdpGsoSegments(&'a u16),

/// Configure the sending addressing and interface for v4
///
/// For further information, please refer to the
/// [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html) man page.
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd"))]
Ipv4PacketInfo(&'a libc::in_pktinfo),

/// Configure the sending addressing and interface for v6
///
/// For further information, please refer to the
/// [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd"))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),
}

// An opaque structure used to prevent cmsghdr from being a public type
Expand Down Expand Up @@ -798,6 +817,12 @@ impl<'a> ControlMessage<'a> {
ControlMessage::UdpGsoSegments(gso_size) => {
gso_size as *const _ as *const u8
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd"))]
ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd"))]
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
};
unsafe {
ptr::copy_nonoverlapping(
Expand Down Expand Up @@ -838,6 +863,12 @@ impl<'a> ControlMessage<'a> {
ControlMessage::UdpGsoSegments(gso_size) => {
mem::size_of_val(gso_size)
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd"))]
ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd"))]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
}
}

Expand All @@ -854,6 +885,12 @@ impl<'a> ControlMessage<'a> {
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
#[cfg(target_os = "linux")]
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd"))]
ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd"))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
}
}

Expand Down Expand Up @@ -881,6 +918,12 @@ impl<'a> ControlMessage<'a> {
ControlMessage::UdpGsoSegments(_) => {
libc::UDP_SEGMENT
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd"))]
ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd"))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
}
}

Expand Down
105 changes: 105 additions & 0 deletions test/sys/test_socket.rs
Expand Up @@ -666,6 +666,111 @@ pub fn test_af_alg_aead() {
assert_eq!(decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]);
}

// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`.
// This creates a (udp) socket bound to localhost, then sends a message to
// itself but uses Ipv4PacketInfo to force the source address to be localhost.
//
// This would be a more interesting test if we could assume that the test host
// has more than one IP address (since we could select a different address to
// test from).
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd"))]
#[test]
pub fn test_sendmsg_ipv4packetinfo() {
use nix::sys::uio::IoVec;
use nix::sys::socket::{socket, sendmsg, bind,
AddressFamily, SockType, SockFlag, SockAddr,
ControlMessage, MsgFlags};

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

let std_sa = SocketAddr::from_str("127.0.0.1:4000").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);

bind(sock, &sock_addr).expect("bind failed");

let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
let iov = [IoVec::from_slice(&slice)];

if let InetAddr::V4(sin) = inet_addr {
let pi = libc::in_pktinfo {
ipi_ifindex: 0, /* Unspecified interface */
ipi_addr: libc::in_addr { s_addr: 0 },
ipi_spec_dst: sin.sin_addr,
};

let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];

sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
.expect("sendmsg");
} else {
panic!("No IPv4 addresses available for testing?");
}
}

// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
// This creates a (udp) socket bound to ip6-localhost, then sends a message to
// itself but uses Ipv6PacketInfo to force the source address to be
// ip6-localhost.
//
// This would be a more interesting test if we could assume that the test host
// has more than one IP address (since we could select a different address to
// test from).
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd"))]
#[test]
pub fn test_sendmsg_ipv6packetinfo() {
use nix::Error;
use nix::errno::Errno;
use nix::sys::uio::IoVec;
use nix::sys::socket::{socket, sendmsg, bind,
AddressFamily, SockType, SockFlag, SockAddr,
ControlMessage, MsgFlags};

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

let std_sa = SocketAddr::from_str("[::1]:6000").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);

match bind(sock, &sock_addr) {
Err(Error::Sys(Errno::EADDRNOTAVAIL)) => {
println!("IPv6 not available, skipping test.");
return;
},
_ => (),
}

let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
let iov = [IoVec::from_slice(&slice)];

if let InetAddr::V6(sin) = inet_addr {
let pi = libc::in6_pktinfo {
ipi6_ifindex: 0, /* Unspecified interface */
ipi6_addr: sin.sin6_addr,
};

let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];

sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
.expect("sendmsg");
} else {
println!("No IPv6 addresses available for testing: skipping testing Ipv6PacketInfo");
}
}

/// 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 2c42b30

Please sign in to comment.