summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/sys/socket/mod.rs43
-rw-r--r--test/sys/test_socket.rs105
3 files changed, 150 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 346af45b..481a1a2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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))
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 6909c154..b972835f 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -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
@@ -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(
@@ -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),
}
}
@@ -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,
}
}
@@ -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,
}
}
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index d6c1ec0c..a5fa54bd 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -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