Skip to content

Commit

Permalink
Add Ipv{4,6}PacketInfo support to ControlMessage for send{m,}msg
Browse files Browse the repository at this point in the history
This adds Ipv4PacketInfo and Ipv6PacketInfo to ControlMessage,
allowing these to be used with sendmsg/sendmmsg.

This change contains the following squashed commits:

Add Ipv{4,6}PacketInfo to ControlMessage.

Add documentation links to Ipv{4,6}PacketInfo

Add changelog entry for Ipv{4,6}PacketInfo

Add link to PR in the Changelog.

Add extra build environments.

Add tests for Ipv{4,6}PacketInfo.

Swap #[test] and #[cfg]

The CI appears to be running the test, even though it's not cfg'd for
that platform.  I _think_ this might be due to these being in the wrong
order.  So lets try swapping them.

s/freebsd/netbsd/ for Ipv4PacketInfo

netbsd supports in_pktinfo, not freebsd.

Fix the cfg for Ipv{4,6}PacketInfo usage.

Ah, I see what I did wrong.  I had fixed the definitions, but I had the
wrong cfg() in the usage.  This has the usage match the definitions.

Change SOL_IPV6 to IPPROTO_IPV6.

FreeBSD doesn't have SOL_IPV6, but does have IPPROTO_IPV6, and the two
constants are defined as being equal.  So change to use IPPROTO_IPV6.

Skip Ipv6PacketInfo test if v6 is not available.

If IPv6 is not available, then when we try and bind to ip6-localhost,
we'll get a EADDRNOTAVAIL, so skip the test.

This should mean that the test will run on any machine that has a v6
loopback address.

More architecture cfg() fixes.

These all need to be the same, and they were not.  Make them them all
the same.  Attempt III.

Fix up mismatched cfg's again.

Take IV.  Make sure the cfg's that use a enum variant match the enum
definition.
  • Loading branch information
isomer authored and tazjin committed Jun 26, 2020
1 parent ea099dd commit 78347d1
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 78347d1

Please sign in to comment.