Skip to content

Commit 854a546

Browse files
bors[bot]brianmay
andauthored
Merge #1772
1772: Add support for RecvOrigDstAddr on Linux r=asomers a=brianmay Fixes #1767 Co-authored-by: Brian May <brian@linuxpenguins.xyz>
2 parents f8794dd + c45cd74 commit 854a546

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3434
(#[1752](https://github.com/nix-rust/nix/pull/1752))
3535
- Added `signal::SigSet::from_sigset_t_unchecked()`.
3636
(#[1741](https://github.com/nix-rust/nix/pull/1741))
37+
- Added IP_ORIGDSTADDR using Ipv4OrigDstAddr in setsockopt and recvmsg.
38+
(#[1772](https://github.com/nix-rust/nix/pull/1772))
39+
- Added IPV6_ORIGDSTADDR using Ipv6OrigDstAddr in setsockopt and recvmsg.
40+
(#[1772](https://github.com/nix-rust/nix/pull/1772))
3741

3842
### Changed
3943

src/sys/socket/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,14 @@ pub enum ControlMessageOwned {
764764
#[cfg(feature = "net")]
765765
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
766766
Ipv4RecvDstAddr(libc::in_addr),
767+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
768+
#[cfg(feature = "net")]
769+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
770+
Ipv4OrigDstAddr(libc::sockaddr_in),
771+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
772+
#[cfg(feature = "net")]
773+
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
774+
Ipv6OrigDstAddr(libc::sockaddr_in6),
767775

768776
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
769777
/// packets from a single sender.
@@ -923,6 +931,12 @@ impl ControlMessageOwned {
923931
let dl = ptr::read_unaligned(p as *const libc::in_addr);
924932
ControlMessageOwned::Ipv4RecvDstAddr(dl)
925933
},
934+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
935+
#[cfg(feature = "net")]
936+
(libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
937+
let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
938+
ControlMessageOwned::Ipv4OrigDstAddr(dl)
939+
},
926940
#[cfg(target_os = "linux")]
927941
#[cfg(feature = "net")]
928942
(libc::SOL_UDP, libc::UDP_GRO) => {
@@ -946,6 +960,12 @@ impl ControlMessageOwned {
946960
let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
947961
ControlMessageOwned::Ipv6RecvErr(err, addr)
948962
},
963+
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
964+
#[cfg(feature = "net")]
965+
(libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
966+
let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
967+
ControlMessageOwned::Ipv6OrigDstAddr(dl)
968+
},
949969
(_, _) => {
950970
let sl = slice::from_raw_parts(p, len);
951971
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+
Ipv4OrigDstAddr, Both, libc::IPPROTO_IP, libc::IP_ORIGDSTADDR, 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+
Ipv6OrigDstAddr, Both, libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR, 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: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,176 @@ pub fn test_recvif() {
17451745
}
17461746
}
17471747

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

0 commit comments

Comments
 (0)