Skip to content

Commit e504662

Browse files
committed
Add support for RecvOrigDstAddr on Linux
Fixes #1767
1 parent 7cc33c1 commit e504662

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

src/sys/socket/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,14 @@ pub enum ControlMessageOwned {
757757
#[cfg(feature = "net")]
758758
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
759759
Ipv4RecvDstAddr(libc::in_addr),
760+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
761+
#[cfg(feature = "net")]
762+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
763+
Ipv4RecvOrigDstAddr(libc::in_addr),
764+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
765+
#[cfg(feature = "net")]
766+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
767+
Ipv6RecvOrigDstAddr(libc::in6_addr),
760768

761769
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
762770
/// packets from a single sender.
@@ -916,6 +924,12 @@ impl ControlMessageOwned {
916924
let dl = ptr::read_unaligned(p as *const libc::in_addr);
917925
ControlMessageOwned::Ipv4RecvDstAddr(dl)
918926
},
927+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
928+
#[cfg(feature = "net")]
929+
(libc::IPPROTO_IP, libc::IP_RECVORIGDSTADDR) => {
930+
let dl = ptr::read_unaligned(p as *const libc::in_addr);
931+
ControlMessageOwned::Ipv4RecvOrigDstAddr(dl)
932+
},
919933
#[cfg(target_os = "linux")]
920934
#[cfg(feature = "net")]
921935
(libc::SOL_UDP, libc::UDP_GRO) => {
@@ -939,6 +953,12 @@ impl ControlMessageOwned {
939953
let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
940954
ControlMessageOwned::Ipv6RecvErr(err, addr)
941955
},
956+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
957+
#[cfg(feature = "net")]
958+
(libc::IPPROTO_IPV6, libc::IPV6_RECVORIGDSTADDR) => {
959+
let dl = ptr::read_unaligned(p as *const libc::in6_addr);
960+
ControlMessageOwned::Ipv6RecvOrigDstAddr(dl)
961+
},
942962
(_, _) => {
943963
let sl = slice::from_raw_parts(p, len);
944964
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));

src/sys/socket/sockopt.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,13 @@ sockopt_impl!(
574574
/// The `recvmsg(2)` call will return the destination IP address for a UDP
575575
/// datagram.
576576
Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
577+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
578+
#[cfg(feature = "net")]
579+
sockopt_impl!(
580+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
581+
/// The `recvmsg(2)` call will return the destination IP address for a UDP
582+
/// datagram.
583+
Ipv4RecvOrigDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVORIGDSTADDR, bool);
577584
#[cfg(target_os = "linux")]
578585
#[cfg(feature = "net")]
579586
sockopt_impl!(
@@ -621,6 +628,13 @@ sockopt_impl!(
621628
sockopt_impl!(
622629
/// Set the unicast hop limit for the socket.
623630
Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
631+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
632+
#[cfg(feature = "net")]
633+
sockopt_impl!(
634+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
635+
/// The `recvmsg(2)` call will return the destination IP address for a UDP
636+
/// datagram.
637+
Ipv6RecvOrigDstAddr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVORIGDSTADDR, bool);
624638
#[cfg(any(target_os = "ios", target_os = "macos"))]
625639
sockopt_impl!(
626640
/// Set "don't fragment packet" flag on the IP packet.

test/sys/test_socket.rs

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,174 @@ pub fn test_recvif() {
17451745
}
17461746
}
17471747

1748+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
1749+
#[test]
1750+
pub fn test_recvif_ipv4() {
1751+
use nix::sys::socket::sockopt::{Ipv4RecvOrigDstAddr};
1752+
use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
1753+
use nix::sys::socket::{getsockname, setsockopt, socket};
1754+
use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
1755+
use std::io::{IoSlice, IoSliceMut};
1756+
1757+
let lo_ifaddr = loopback_address(AddressFamily::Inet);
1758+
let (_lo_name, lo) = match lo_ifaddr {
1759+
Some(ifaddr) => (
1760+
ifaddr.interface_name,
1761+
ifaddr.address.expect("Expect IPv4 address on interface"),
1762+
),
1763+
None => return,
1764+
};
1765+
let receive = socket(
1766+
AddressFamily::Inet,
1767+
SockType::Datagram,
1768+
SockFlag::empty(),
1769+
None,
1770+
)
1771+
.expect("receive socket failed");
1772+
bind(receive, &lo).expect("bind failed");
1773+
let sa: SockaddrIn = getsockname(receive).expect("getsockname failed");
1774+
setsockopt(receive, Ipv4RecvOrigDstAddr, &true)
1775+
.expect("setsockopt IP_RECVORIGDSTADDR failed");
1776+
1777+
{
1778+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
1779+
let iov = [IoSlice::new(&slice)];
1780+
1781+
let send = socket(
1782+
AddressFamily::Inet,
1783+
SockType::Datagram,
1784+
SockFlag::empty(),
1785+
None,
1786+
)
1787+
.expect("send socket failed");
1788+
sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
1789+
.expect("sendmsg failed");
1790+
}
1791+
1792+
{
1793+
let mut buf = [0u8; 8];
1794+
let mut iovec = [IoSliceMut::new(&mut buf)];
1795+
let mut space = cmsg_space!(libc::in_addr, [u8; 0]);
1796+
let msg = recvmsg::<()>(
1797+
receive,
1798+
&mut iovec,
1799+
Some(&mut space),
1800+
MsgFlags::empty(),
1801+
)
1802+
.expect("recvmsg failed");
1803+
assert!(!msg
1804+
.flags
1805+
.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
1806+
assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
1807+
1808+
let mut rx_recvorigdstaddr = false;
1809+
for cmsg in msg.cmsgs() {
1810+
match cmsg {
1811+
ControlMessageOwned::Ipv4RecvOrigDstAddr(addr) => {
1812+
rx_recvorigdstaddr = true;
1813+
if let Some(sin) = lo.as_sockaddr_in() {
1814+
assert_eq!(sin.as_ref().sin_addr.s_addr,
1815+
addr.s_addr,
1816+
"unexpected destination address (expected {}, got {})",
1817+
sin.as_ref().sin_addr.s_addr,
1818+
addr.s_addr);
1819+
} else {
1820+
panic!("unexpected Sockaddr");
1821+
}
1822+
}
1823+
_ => panic!("unexpected additional control msg"),
1824+
}
1825+
}
1826+
assert!(rx_recvorigdstaddr);
1827+
assert_eq!(msg.bytes, 8);
1828+
assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
1829+
}
1830+
}
1831+
1832+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
1833+
#[test]
1834+
pub fn test_recvif_ipv6() {
1835+
use nix::sys::socket::sockopt::{Ipv6RecvOrigDstAddr};
1836+
use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
1837+
use nix::sys::socket::{getsockname, setsockopt, socket};
1838+
use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
1839+
use std::io::{IoSlice, IoSliceMut};
1840+
1841+
let lo_ifaddr = loopback_address(AddressFamily::Inet6);
1842+
let (_lo_name, lo) = match lo_ifaddr {
1843+
Some(ifaddr) => (
1844+
ifaddr.interface_name,
1845+
ifaddr.address.expect("Expect IPv6 address on interface"),
1846+
),
1847+
None => return,
1848+
};
1849+
let receive = socket(
1850+
AddressFamily::Inet6,
1851+
SockType::Datagram,
1852+
SockFlag::empty(),
1853+
None,
1854+
)
1855+
.expect("receive socket failed");
1856+
bind(receive, &lo).expect("bind failed");
1857+
let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed");
1858+
setsockopt(receive, Ipv6RecvOrigDstAddr, &true)
1859+
.expect("setsockopt IP_RECVORIGDSTADDR failed");
1860+
1861+
{
1862+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
1863+
let iov = [IoSlice::new(&slice)];
1864+
1865+
let send = socket(
1866+
AddressFamily::Inet6,
1867+
SockType::Datagram,
1868+
SockFlag::empty(),
1869+
None,
1870+
)
1871+
.expect("send socket failed");
1872+
sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
1873+
.expect("sendmsg failed");
1874+
}
1875+
1876+
{
1877+
let mut buf = [0u8; 8];
1878+
let mut iovec = [IoSliceMut::new(&mut buf)];
1879+
let mut space = cmsg_space!(libc::in6_addr, [u8; 0]);
1880+
let msg = recvmsg::<()>(
1881+
receive,
1882+
&mut iovec,
1883+
Some(&mut space),
1884+
MsgFlags::empty(),
1885+
)
1886+
.expect("recvmsg failed");
1887+
assert!(!msg
1888+
.flags
1889+
.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
1890+
assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
1891+
1892+
let mut rx_recvorigdstaddr = false;
1893+
for cmsg in msg.cmsgs() {
1894+
match cmsg {
1895+
ControlMessageOwned::Ipv6RecvOrigDstAddr(addr) => {
1896+
rx_recvorigdstaddr = true;
1897+
if let Some(sin) = lo.as_sockaddr_in6() {
1898+
assert_eq!(sin.as_ref().sin6_addr.s6_addr,
1899+
addr.s6_addr,
1900+
"unexpected destination address (expected {:?}, got {:?})",
1901+
sin.as_ref().sin6_addr.s6_addr,
1902+
addr.s6_addr);
1903+
} else {
1904+
panic!("unexpected Sockaddr");
1905+
}
1906+
}
1907+
_ => panic!("unexpected additional control msg"),
1908+
}
1909+
}
1910+
assert!(rx_recvorigdstaddr);
1911+
assert_eq!(msg.bytes, 8);
1912+
assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
1913+
}
1914+
}
1915+
17481916
#[cfg(any(
17491917
target_os = "android",
17501918
target_os = "freebsd",

0 commit comments

Comments
 (0)