summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/sys/socket/mod.rs110
-rw-r--r--src/sys/socket/sockopt.rs23
-rw-r--r--test/sys/test_socket.rs287
4 files changed, 330 insertions, 92 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6c6b487..a5b73791 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
+- Add IP_RECVIF & IP_RECVDSTADDR. Enable IP_PKTINFO and IP6_PKTINFO on netbsd/openbsd.
+ ([#1002](https://github.com/nix-rust/nix/pull/1002))
### Changed
### Fixed
### Removed
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 62c0ea74..64d2fc12 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -535,6 +535,22 @@ pub enum ControlMessage<'a> {
target_os = "macos"
))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ Ipv4RecvIf(&'a libc::sockaddr_dl),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ Ipv4RecvDstAddr(&'a libc::in_addr),
/// Catch-all variant for unimplemented cmsg types.
#[doc(hidden)]
@@ -594,6 +610,26 @@ impl<'a> ControlMessage<'a> {
ControlMessage::Ipv6PacketInfo(pktinfo) => {
mem::size_of_val(pktinfo)
},
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvIf(dl) => {
+ mem::size_of_val(dl)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvDstAddr(inaddr) => {
+ mem::size_of_val(inaddr)
+ },
ControlMessage::Unknown(UnknownCmsg(_, bytes)) => {
mem::size_of_val(bytes)
}
@@ -622,6 +658,22 @@ impl<'a> ControlMessage<'a> {
target_os = "macos"
))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvIf(_) => libc::IPPROTO_IP,
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvDstAddr(_) => libc::IPPROTO_IP,
ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_level,
}
}
@@ -648,6 +700,22 @@ impl<'a> ControlMessage<'a> {
target_os = "macos"
))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvIf(_) => libc::IP_RECVIF,
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvDstAddr(_) => libc::IP_RECVDSTADDR,
ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_type,
}
}
@@ -708,6 +776,26 @@ impl<'a> ControlMessage<'a> {
ControlMessage::Ipv6PacketInfo(pktinfo) => {
copy_bytes(pktinfo, buf)
}
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvIf(dl) => {
+ copy_bytes(dl, buf)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ ControlMessage::Ipv4RecvDstAddr(inaddr) => {
+ copy_bytes(inaddr, buf)
+ },
ControlMessage::Unknown(_) => unreachable!(),
}
};
@@ -760,6 +848,28 @@ impl<'a> ControlMessage<'a> {
ControlMessage::Ipv4PacketInfo(
&*(data.as_ptr() as *const _))
}
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ (libc::IPPROTO_IP, libc::IP_RECVIF) => {
+ ControlMessage::Ipv4RecvIf(
+ &*(data.as_ptr() as *const _))
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+ ControlMessage::Ipv4RecvDstAddr(
+ &*(data.as_ptr() as *const _))
+ }
(_, _) => {
ControlMessage::Unknown(UnknownCmsg(header, data))
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 3cd46c66..1920987e 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -275,7 +275,8 @@ sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsSt
target_os = "android",
target_os = "ios",
target_os = "linux",
- target_os = "macos"
+ target_os = "macos",
+ target_os = "netbsd",
))]
sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
#[cfg(any(
@@ -283,9 +284,27 @@ sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
- target_os = "macos"
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
))]
sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
/*
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 1ed6c1f6..2cc3475f 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -453,11 +453,62 @@ pub fn test_syscontrol() {
// connect(fd, &sockaddr).expect("connect failed");
}
+use nix::ifaddrs::InterfaceAddress;
+use nix::sys::socket::AddressFamily;
#[cfg(any(
target_os = "android",
+ target_os = "freebsd",
target_os = "ios",
target_os = "linux",
- target_os = "macos"
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+fn loopback_address(family: AddressFamily) -> Option<InterfaceAddress> {
+ use std::io;
+ use std::io::Write;
+ use nix::ifaddrs::getifaddrs;
+ use nix::sys::socket::SockAddr;
+ use nix::net::if_::*;
+
+ let addrs = match getifaddrs() {
+ Ok(iter) => iter,
+ Err(e) => {
+ let stdioerr = io::stderr();
+ let mut handle = stdioerr.lock();
+ writeln!(handle, "getifaddrs: {:?}", e).unwrap();
+ return None;
+ },
+ };
+ // return first address matching family
+ for ifaddr in addrs {
+ if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
+ match ifaddr.address {
+ Some(SockAddr::Inet(InetAddr::V4(..))) => {
+ match family {
+ AddressFamily::Inet => return Some(ifaddr),
+ _ => continue
+ }
+ },
+ Some(SockAddr::Inet(InetAddr::V6(..))) => {
+ match family {
+ AddressFamily::Inet6 => return Some(ifaddr),
+ _ => continue
+ }
+ },
+ _ => continue,
+ }
+ }
+ }
+ None
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
))]
// qemu doesn't seem to be emulating this correctly in these architectures
#[cfg_attr(any(
@@ -468,57 +519,43 @@ pub fn test_syscontrol() {
#[test]
pub fn test_recv_ipv4pktinfo() {
use libc;
- use nix::ifaddrs::{getifaddrs, InterfaceAddress};
- use nix::net::if_::*;
use nix::sys::socket::sockopt::Ipv4PacketInfo;
- use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType};
- use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+ use nix::sys::socket::{bind, SockFlag, SockType};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
use nix::sys::uio::IoVec;
- use std::io;
- use std::io::Write;
- use std::thread;
-
- fn loopback_v4addr() -> Option<InterfaceAddress> {
- let addrs = match getifaddrs() {
- Ok(iter) => iter,
- Err(e) => {
- let stdioerr = io::stderr();
- let mut handle = stdioerr.lock();
- writeln!(handle, "getifaddrs: {:?}", e).unwrap();
- return None;
- },
- };
- for ifaddr in addrs {
- if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
- match ifaddr.address {
- Some(SockAddr::Inet(InetAddr::V4(..))) => {
- return Some(ifaddr);
- }
- _ => continue,
- }
- }
- }
- None
- }
+ use nix::net::if_::*;
- let lo_ifaddr = loopback_v4addr();
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
let (lo_name, lo) = match lo_ifaddr {
Some(ifaddr) => (ifaddr.interface_name,
ifaddr.address.expect("Expect IPv4 address on interface")),
None => return,
};
let receive = socket(
- AddressFamily::Inet,
- SockType::Datagram,
- SockFlag::empty(),
- None,
- ).expect("receive socket failed");
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("receive socket failed");
bind(receive, &lo).expect("bind failed");
let sa = getsockname(receive).expect("getsockname failed");
setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed");
- let thread = thread::spawn(move || {
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+ }
+
+ {
let mut buf = [0u8; 8];
let iovec = [IoVec::from_mut_slice(&mut buf)];
let mut space = CmsgSpace::<libc::in_pktinfo>::new();
@@ -552,20 +589,115 @@ pub fn test_recv_ipv4pktinfo() {
iovec[0].as_slice(),
[1u8, 2, 3, 4, 5, 6, 7, 8]
);
- });
+ }
+}
- let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
- let iov = [IoVec::from_slice(&slice)];
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+), ignore)]
+#[test]
+pub fn test_recvif() {
+ use libc;
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr};
+ use nix::sys::socket::{bind, SockFlag, SockType};
+ use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+ use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
+ use nix::sys::uio::IoVec;
- let send = socket(
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface")),
+ None => return,
+ };
+ let receive = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
- ).expect("send socket failed");
- sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+ ).expect("receive socket failed");
+ bind(receive, &lo).expect("bind failed");
+ let sa = getsockname(receive).expect("getsockname failed");
+ setsockopt(receive, Ipv4RecvIf, &true).expect("setsockopt IP_RECVIF failed");
+ setsockopt(receive, Ipv4RecvDstAddr, &true).expect("setsockopt IP_RECVDSTADDR failed");
- thread.join().unwrap();
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let iovec = [IoVec::from_mut_slice(&mut buf)];
+ let mut space = CmsgSpace::<(libc::sockaddr_dl, CmsgSpace<libc::in_addr>)>::new();
+ let msg = recvmsg(
+ receive,
+ &iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ ).expect("recvmsg failed");
+ assert!(
+ !msg.flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
+ );
+ assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
+
+ let mut rx_recvif = false;
+ let mut rx_recvdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessage::Ipv4RecvIf(dl) => {
+ rx_recvif = true;
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ dl.sdl_index as libc::c_uint,
+ i,
+ "unexpected ifindex (expected {}, got {})",
+ i,
+ dl.sdl_index
+ );
+ },
+ ControlMessage::Ipv4RecvDstAddr(addr) => {
+ rx_recvdstaddr = true;
+ if let SockAddr::Inet(InetAddr::V4(a)) = lo {
+ assert_eq!(a.sin_addr.s_addr,
+ addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ a.sin_addr.s_addr,
+ addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ },
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert_eq!(rx_recvif, true);
+ assert_eq!(rx_recvdstaddr, true);
+ assert_eq!(
+ iovec[0].as_slice(),
+ [1u8, 2, 3, 4, 5, 6, 7, 8]
+ );
+ }
}
#[cfg(any(
@@ -573,7 +705,9 @@ pub fn test_recv_ipv4pktinfo() {
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
- target_os = "macos"
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
))]
// qemu doesn't seem to be emulating this correctly in these architectures
#[cfg_attr(any(
@@ -584,41 +718,14 @@ pub fn test_recv_ipv4pktinfo() {
#[test]
pub fn test_recv_ipv6pktinfo() {
use libc;
- use nix::ifaddrs::{getifaddrs, InterfaceAddress};
use nix::net::if_::*;
use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType};
- use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
use nix::sys::uio::IoVec;
- use std::io;
- use std::io::Write;
- use std::thread;
-
- fn loopback_v6addr() -> Option<InterfaceAddress> {
- let addrs = match getifaddrs() {
- Ok(iter) => iter,
- Err(e) => {
- let stdioerr = io::stderr();
- let mut handle = stdioerr.lock();
- writeln!(handle, "getifaddrs: {:?}", e).unwrap();
- return None;
- },
- };
- for ifaddr in addrs {
- if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
- match ifaddr.address {
- Some(SockAddr::Inet(InetAddr::V6(..))) => {
- return Some(ifaddr);
- }
- _ => continue,
- }
- }
- }
- None
- }
- let lo_ifaddr = loopback_v6addr();
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
let (lo_name, lo) = match lo_ifaddr {
Some(ifaddr) => (ifaddr.interface_name,
ifaddr.address.expect("Expect IPv4 address on interface")),
@@ -634,7 +741,20 @@ pub fn test_recv_ipv6pktinfo() {
let sa = getsockname(receive).expect("getsockname failed");
setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed");
- let thread = thread::spawn(move || {
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+ }
+
+ {
let mut buf = [0u8; 8];
let iovec = [IoVec::from_mut_slice(&mut buf)];
let mut space = CmsgSpace::<libc::in6_pktinfo>::new();
@@ -668,18 +788,5 @@ pub fn test_recv_ipv6pktinfo() {
iovec[0].as_slice(),
[1u8, 2, 3, 4, 5, 6, 7, 8]
);
- });
-
- let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
- let iov = [IoVec::from_slice(&slice)];
-
- let send = socket(
- AddressFamily::Inet6,
- SockType::Datagram,
- SockFlag::empty(),
- None,
- ).expect("send socket failed");
- sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
-
- thread.join().unwrap();
+ }
}