summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--src/sys/socket/mod.rs20
-rw-r--r--src/sys/socket/sockopt.rs14
-rw-r--r--test/sys/test_socket.rs170
4 files changed, 208 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 235b1c10..c720d165 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1752](https://github.com/nix-rust/nix/pull/1752))
- Added `signal::SigSet::from_sigset_t_unchecked()`.
(#[1741](https://github.com/nix-rust/nix/pull/1741))
+- Added IP_ORIGDSTADDR using Ipv4OrigDstAddr in setsockopt and recvmsg.
+ (#[1772](https://github.com/nix-rust/nix/pull/1772))
+- Added IPV6_ORIGDSTADDR using Ipv6OrigDstAddr in setsockopt and recvmsg.
+ (#[1772](https://github.com/nix-rust/nix/pull/1772))
### Changed
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index e789c116..88378b17 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -757,6 +757,14 @@ pub enum ControlMessageOwned {
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvDstAddr(libc::in_addr),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4OrigDstAddr(libc::sockaddr_in),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6OrigDstAddr(libc::sockaddr_in6),
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
/// packets from a single sender.
@@ -916,6 +924,12 @@ impl ControlMessageOwned {
let dl = ptr::read_unaligned(p as *const libc::in_addr);
ControlMessageOwned::Ipv4RecvDstAddr(dl)
},
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+ ControlMessageOwned::Ipv4OrigDstAddr(dl)
+ },
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
(libc::SOL_UDP, libc::UDP_GRO) => {
@@ -939,6 +953,12 @@ impl ControlMessageOwned {
let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
ControlMessageOwned::Ipv6RecvErr(err, addr)
},
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+ ControlMessageOwned::Ipv6OrigDstAddr(dl)
+ },
(_, _) => {
let sl = slice::from_raw_parts(p, len);
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 8f85748f..fff496db 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -574,6 +574,13 @@ sockopt_impl!(
/// The `recvmsg(2)` call will return the destination IP address for a UDP
/// datagram.
Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4OrigDstAddr, Both, libc::IPPROTO_IP, libc::IP_ORIGDSTADDR, bool);
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
sockopt_impl!(
@@ -621,6 +628,13 @@ sockopt_impl!(
sockopt_impl!(
/// Set the unicast hop limit for the socket.
Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv6OrigDstAddr, Both, libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR, bool);
#[cfg(any(target_os = "ios", target_os = "macos"))]
sockopt_impl!(
/// Set "don't fragment packet" flag on the IP packet.
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 56819c9f..0a74b5e1 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -1745,6 +1745,176 @@ pub fn test_recvif() {
}
}
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv4() {
+ use nix::sys::socket::sockopt::Ipv4OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ 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");
+ bind(receive, &lo).expect("bind failed");
+ let sa: SockaddrIn = getsockname(receive).expect("getsockname failed");
+ setsockopt(receive, Ipv4OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&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 mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in);
+ let msg = recvmsg::<()>(
+ receive,
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv4OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in() {
+ assert_eq!(sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv6() {
+ use nix::sys::socket::sockopt::Ipv6OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+ let (_lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv6 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive, &lo).expect("bind failed");
+ let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed");
+ setsockopt(receive, Ipv6OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&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 mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in6);
+ let msg = recvmsg::<()>(
+ receive,
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv6OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in6() {
+ assert_eq!(sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr,
+ "unexpected destination address (expected {:?}, got {:?})",
+ sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
#[cfg(any(
target_os = "android",
target_os = "freebsd",