Skip to content

Commit c40097a

Browse files
committed
unix: Add HopLimit to IpAncillary
Extend UdpSocket to send and receive hop-limit.
1 parent f2e0ce3 commit c40097a

File tree

6 files changed

+168
-17
lines changed

6 files changed

+168
-17
lines changed

library/std/src/os/unix/net/ancillary/ip.rs

+62-3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ impl Socket {
6060
#[non_exhaustive]
6161
pub enum IpAncillaryData {
6262
Ttl(u8),
63+
HopLimit(u8),
6364
}
6465

6566
impl IpAncillaryData {
@@ -84,6 +85,17 @@ impl IpAncillaryData {
8485
IpAncillaryData::Ttl(ttl)
8586
}
8687

88+
/// Create a `AncillaryData::HopLimit` variant.
89+
///
90+
/// # Safety
91+
///
92+
/// `data` must contain a valid control message and the control message must be type of
93+
/// `IPPROTO_IPV6` and level of `IPV6_HOPLIMIT`.
94+
unsafe fn as_hop_limit(data: &[u8]) -> Self {
95+
let hop_limit = IpAncillaryData::as_u8(data);
96+
IpAncillaryData::HopLimit(hop_limit)
97+
}
98+
8799
unsafe fn try_from(cmsg: &libc::cmsghdr) -> Result<Self, AncillaryError> {
88100
let data = get_data_from_cmsghdr(cmsg);
89101

@@ -94,6 +106,12 @@ impl IpAncillaryData {
94106
Err(AncillaryError::Unknown { cmsg_level: libc::IPPROTO_IP, cmsg_type })
95107
}
96108
},
109+
libc::IPPROTO_IPV6 => match (*cmsg).cmsg_type {
110+
libc::IPV6_HOPLIMIT => Ok(IpAncillaryData::as_hop_limit(data)),
111+
cmsg_type => {
112+
Err(AncillaryError::Unknown { cmsg_level: libc::IPPROTO_IPV6, cmsg_type })
113+
}
114+
},
97115
cmsg_level => Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }),
98116
}
99117
}
@@ -146,7 +164,7 @@ pub struct IpAncillary<'a> {
146164
}
147165

148166
impl<'a> IpAncillary<'a> {
149-
/// Create an ancillary data with the given buffer.
167+
/// Create ancillary data backed by the given buffer.
150168
///
151169
/// # Example
152170
///
@@ -186,12 +204,53 @@ impl<'a> IpAncillary<'a> {
186204
self.inner.messages()
187205
}
188206

207+
/// Add hop-limit to the ancillary data.
208+
///
209+
/// The function returns `true` if there was enough space in the buffer.
210+
/// If there was not enough space then no hop-limit was appended.
211+
/// This adds a control message with the level `IPPROTO_IPV6` and type `IPV6_HOPLIMIT`.
212+
///
213+
/// # Example
214+
/// ```no_run
215+
/// #![feature(unix_socket_ancillary_data)]
216+
/// use std::io::IoSlice;
217+
/// use std::net::UdpSocket;
218+
/// use std::os::unix::net::IpAncillary;
219+
///
220+
/// fn main() -> std::io::Result<()> {
221+
/// let sock = UdpSocket::bind("[::1]:34254")?;
222+
/// sock.connect("[::1]:41203")?;
223+
/// let buf1 = [1; 8];
224+
/// let buf2 = [2; 16];
225+
/// let buf3 = [3; 8];
226+
/// let bufs = &[
227+
/// IoSlice::new(&buf1),
228+
/// IoSlice::new(&buf2),
229+
/// IoSlice::new(&buf3),
230+
/// ][..];
231+
/// let mut ancillary_buffer = [0; 128];
232+
/// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]);
233+
/// ancillary.add_hop_limit(20);
234+
/// sock.send_vectored_with_ancillary(bufs, &mut ancillary)
235+
/// .expect("send_vectored_with_ancillary function failed");
236+
/// Ok(())
237+
/// }
238+
/// ```
239+
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
240+
pub fn add_hop_limit(&mut self, hop_limit: u8) -> bool {
241+
let hop_limit: libc::c_int = hop_limit as libc::c_int;
242+
self.inner.add_to_ancillary_data(
243+
from_ref(&hop_limit),
244+
libc::IPPROTO_IPV6,
245+
libc::IPV6_HOPLIMIT,
246+
)
247+
}
248+
189249
/// Add TTL to the ancillary data.
190250
///
191251
/// The function returns `true` if there was enough space in the buffer.
192252
/// If there was not enough space then no file descriptors was appended.
193-
/// Technically, that means this operation adds a control message with the level `IPPROTO_IP`
194-
/// and type `IP_TTL`.
253+
/// This adds a control message with the level `IPPROTO_IP` and type `IP_TTL`.
195254
///
196255
/// # Example
197256
/// ```no_run

library/std/src/os/unix/net/ancillary/unix.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,7 @@ impl<'a> UnixAncillary<'a> {
323323
///
324324
/// The function returns `true` if there was enough space in the buffer.
325325
/// If there was not enough space then no file descriptors was appended.
326-
/// Technically, that means this operation adds a control message with the level `SOL_SOCKET`
327-
/// and type `SCM_RIGHTS`.
326+
/// This adds a control message with the level `SOL_SOCKET` and type `SCM_RIGHTS`.
328327
///
329328
/// # Example
330329
///
@@ -356,8 +355,8 @@ impl<'a> UnixAncillary<'a> {
356355
///
357356
/// The function returns `true` if there was enough space in the buffer.
358357
/// If there was not enough space then no credentials was appended.
359-
/// Technically, that means this operation adds a control message with the level `SOL_SOCKET`
360-
/// and type `SCM_CREDENTIALS` or `SCM_CREDS`.
358+
/// This adds a control message with the level `SOL_SOCKET` and type `SCM_CREDENTIALS`
359+
/// or `SCM_CREDS`.
361360
///
362361
#[doc(cfg(any(target_os = "android", target_os = "linux",)))]
363362
#[cfg(any(doc, target_os = "android", target_os = "linux",))]

library/std/src/os/unix/net/tests.rs

+48-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::*;
22
use crate::io::prelude::*;
33
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
4+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
5+
use crate::net::UdpSocket;
46
use crate::sys_common::io::test::tmpdir;
57
use crate::thread;
68
use crate::time::Duration;
@@ -631,14 +633,7 @@ fn test_send_vectored_with_ancillary_unix_datagram() {
631633

632634
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
633635
#[test]
634-
fn test_send_vectored_with_ancillary_to_udp_socket() {
635-
use super::{IpAncillary, IpAncillaryData};
636-
use crate::{
637-
io::{IoSlice, IoSliceMut},
638-
iter::FromIterator,
639-
net::UdpSocket,
640-
};
641-
636+
fn test_send_vectored_with_ancillary_to_udp_socket_ttl() {
642637
let socket1 = or_panic!(UdpSocket::bind("127.0.0.1:0"));
643638
let socket2 = or_panic!(UdpSocket::bind("127.0.0.1:0"));
644639

@@ -673,6 +668,49 @@ fn test_send_vectored_with_ancillary_to_udp_socket() {
673668

674669
let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages());
675670
assert_eq!(ancillary_data_vec.len(), 1);
676-
let IpAncillaryData::Ttl(ttl) = ancillary_data_vec.pop().unwrap().unwrap();
677-
assert_eq!(ttl, 20);
671+
assert!(
672+
matches!(ancillary_data_vec.pop().unwrap().unwrap(), IpAncillaryData::Ttl(ttl) if ttl == 20)
673+
);
674+
}
675+
676+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
677+
#[test]
678+
fn test_send_vectored_with_ancillary_to_udp_socket_hop_limit() {
679+
let socket1 = or_panic!(UdpSocket::bind("[::1]:0"));
680+
let socket2 = or_panic!(UdpSocket::bind("[::1]:0"));
681+
682+
let addr1 = or_panic!(socket1.local_addr());
683+
let addr2 = or_panic!(socket2.local_addr());
684+
685+
or_panic!(socket2.set_recvhoplimit(true));
686+
687+
let buf1 = [1; 8];
688+
let bufs_send = &[IoSlice::new(&buf1[..])][..];
689+
690+
let mut ancillary1_buffer = [0; 64];
691+
let mut ancillary1 = IpAncillary::new(&mut ancillary1_buffer[..]);
692+
assert!(ancillary1.add_hop_limit(20));
693+
694+
let usize =
695+
or_panic!(socket1.send_vectored_with_ancillary_to(&bufs_send, &mut ancillary1, &addr2));
696+
assert_eq!(usize, 8);
697+
698+
let mut buf2 = [0; 8];
699+
let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..];
700+
701+
let mut ancillary2_buffer = [0; 64];
702+
let mut ancillary2 = IpAncillary::new(&mut ancillary2_buffer[..]);
703+
704+
let (usize, truncated, addr) =
705+
or_panic!(socket2.recv_vectored_with_ancillary_from(&mut bufs_recv, &mut ancillary2));
706+
assert_eq!(usize, 8);
707+
assert_eq!(truncated, false);
708+
assert_eq!(addr1, addr);
709+
assert_eq!(buf1, buf2);
710+
711+
let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages());
712+
assert_eq!(ancillary_data_vec.len(), 1);
713+
assert!(
714+
matches!(ancillary_data_vec.pop().unwrap().unwrap(), IpAncillaryData::HopLimit(ttl) if ttl == 20)
715+
);
678716
}

library/std/src/os/unix/net/udp.rs

+33
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,39 @@ use crate::{
66
};
77

88
impl UdpSocket {
9+
/// Sets the value of the `IPV6_RECVHOPLIMIT` option for this socket.
10+
///
11+
/// If enabled, received packets will come with ancillary data ([`IpAncillary`]) providing
12+
/// the hop-limit value of the packet.
13+
///
14+
/// # Examples
15+
///
16+
///```no_run
17+
/// #![feature(unix_socket_ancillary_data)]
18+
/// use std::net::UdpSocket;
19+
///
20+
/// fn main() -> std::io::Result<()> {
21+
/// let socket = UdpSocket::bind("[::1]:34254")?;
22+
/// socket.set_recvhoplimit(true)?;
23+
/// Ok(())
24+
/// }
25+
/// ```
26+
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
27+
pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> {
28+
self.as_inner().set_recvhoplimit(recvhoplimit)
29+
}
30+
31+
/// Get the current value of the socket for receiving TTL in [`IpAncillary`].
32+
/// This value can be change by [`set_recvhoplimit`].
33+
///
34+
/// Get the socket option `IPV6_RECVHOPLIMIT`.
35+
///
36+
/// [`set_recvhoplimit`]: UdpSocket::set_recvhoplimit
37+
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
38+
pub fn recvhoplimit(&self) -> io::Result<bool> {
39+
self.as_inner().recvhoplimit()
40+
}
41+
942
/// Sets the value of the `IP_RECVTTL` option for this socket.
1043
///
1144
/// If enabled, received packets will come with ancillary data ([`IpAncillary`]) providing

library/std/src/sys/unix/net.rs

+12
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,18 @@ impl Socket {
393393
Ok(passcred != 0)
394394
}
395395

396+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
397+
pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> {
398+
setsockopt(self, libc::IPPROTO_IPV6, libc::IPV6_RECVHOPLIMIT, recvhoplimit as libc::c_int)
399+
}
400+
401+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
402+
pub fn recvhoplimit(&self) -> io::Result<bool> {
403+
let recvhoplimit: libc::c_int =
404+
getsockopt(self, libc::IPPROTO_IPV6, libc::IPV6_RECVHOPLIMIT)?;
405+
Ok(recvhoplimit != 0)
406+
}
407+
396408
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
397409
pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> {
398410
setsockopt(self, libc::IPPROTO_IP, libc::IP_RECVTTL, recvttl as libc::c_int)

library/std/src/sys_common/net.rs

+10
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,16 @@ impl UdpSocket {
507507
self.inner.recv_from(buf)
508508
}
509509

510+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
511+
pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> {
512+
self.inner.set_recvhoplimit(recvhoplimit)
513+
}
514+
515+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
516+
pub fn recvhoplimit(&self) -> io::Result<bool> {
517+
self.inner.recvhoplimit()
518+
}
519+
510520
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
511521
pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> {
512522
self.inner.set_recvttl(recvttl)

0 commit comments

Comments
 (0)