Skip to content

Commit f45c480

Browse files
committed
Add IPv6 versions of bind_device_by_index
1 parent 5bdcbf0 commit f45c480

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

src/sys/unix.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1833,6 +1833,35 @@ impl crate::Socket {
18331833
.map(|_| ())
18341834
}
18351835

1836+
/// This method is deprecated, use [`bind_device_by_index_v4`].
1837+
///
1838+
/// [`bind_device_by_index_v4`]: crate::Socket::bind_device_by_index_v4
1839+
#[cfg(all(
1840+
feature = "all",
1841+
any(
1842+
target_os = "ios",
1843+
target_os = "macos",
1844+
target_os = "tvos",
1845+
target_os = "watchos",
1846+
)
1847+
))]
1848+
#[cfg_attr(
1849+
docsrs,
1850+
doc(cfg(all(
1851+
feature = "all",
1852+
any(
1853+
target_os = "ios",
1854+
target_os = "macos",
1855+
target_os = "tvos",
1856+
target_os = "watchos",
1857+
)
1858+
)))
1859+
)]
1860+
#[deprecated]
1861+
pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1862+
self.bind_device_by_index_v4(interface)
1863+
}
1864+
18361865
/// Sets the value for `IP_BOUND_IF` option on this socket.
18371866
///
18381867
/// If a socket is bound to an interface, only packets received from that
@@ -1864,11 +1893,47 @@ impl crate::Socket {
18641893
)
18651894
)))
18661895
)]
1867-
pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1896+
pub fn bind_device_by_index_v4(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
18681897
let index = interface.map_or(0, NonZeroU32::get);
18691898
unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
18701899
}
18711900

1901+
/// Sets the value for `IPV6_BOUND_IF` option on this socket.
1902+
///
1903+
/// If a socket is bound to an interface, only packets received from that
1904+
/// particular interface are processed by the socket.
1905+
///
1906+
/// If `interface` is `None`, the binding is removed. If the `interface`
1907+
/// index is not valid, an error is returned.
1908+
///
1909+
/// One can use [`libc::if_nametoindex`] to convert an interface alias to an
1910+
/// index.
1911+
#[cfg(all(
1912+
feature = "all",
1913+
any(
1914+
target_os = "ios",
1915+
target_os = "macos",
1916+
target_os = "tvos",
1917+
target_os = "watchos",
1918+
)
1919+
))]
1920+
#[cfg_attr(
1921+
docsrs,
1922+
doc(cfg(all(
1923+
feature = "all",
1924+
any(
1925+
target_os = "ios",
1926+
target_os = "macos",
1927+
target_os = "tvos",
1928+
target_os = "watchos",
1929+
)
1930+
)))
1931+
)]
1932+
pub fn bind_device_by_index_v6(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1933+
let index = interface.map_or(0, NonZeroU32::get);
1934+
unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) }
1935+
}
1936+
18721937
/// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
18731938
/// for the interface to which the socket is bound.
18741939
///
@@ -1901,6 +1966,39 @@ impl crate::Socket {
19011966
Ok(NonZeroU32::new(index))
19021967
}
19031968

1969+
/// Gets the value for `IPV6_BOUND_IF` option on this socket, i.e. the index
1970+
/// for the interface to which the socket is bound.
1971+
///
1972+
/// Returns `None` if the socket is not bound to any interface, otherwise
1973+
/// returns an interface index.
1974+
#[cfg(all(
1975+
feature = "all",
1976+
any(
1977+
target_os = "ios",
1978+
target_os = "macos",
1979+
target_os = "tvos",
1980+
target_os = "watchos",
1981+
)
1982+
))]
1983+
#[cfg_attr(
1984+
docsrs,
1985+
doc(cfg(all(
1986+
feature = "all",
1987+
any(
1988+
target_os = "ios",
1989+
target_os = "macos",
1990+
target_os = "tvos",
1991+
target_os = "watchos",
1992+
)
1993+
)))
1994+
)]
1995+
pub fn device_index_v6(&self) -> io::Result<Option<NonZeroU32>> {
1996+
let index = unsafe {
1997+
getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)?
1998+
};
1999+
Ok(NonZeroU32::new(index))
2000+
}
2001+
19042002
/// Get the value of the `SO_INCOMING_CPU` option on this socket.
19052003
///
19062004
/// For more information about this option, see [`set_cpu_affinity`].

tests/socket.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ fn device() {
936936
if iface_index.is_none() {
937937
continue;
938938
}
939-
if let Err(err) = socket.bind_device_by_index(iface_index) {
939+
if let Err(err) = socket.bind_device_by_index_v4(iface_index) {
940940
// Network interface is not available try another.
941941
if matches!(err.raw_os_error(), Some(libc::ENODEV)) {
942942
eprintln!("error binding to device (`{interface}`): {err}");
@@ -947,7 +947,7 @@ fn device() {
947947
}
948948
assert_eq!(socket.device_index().unwrap(), iface_index);
949949

950-
socket.bind_device_by_index(None).unwrap();
950+
socket.bind_device_by_index_v4(None).unwrap();
951951
assert_eq!(socket.device_index().unwrap(), None);
952952
// Just need to do it with one interface.
953953
return;
@@ -956,6 +956,51 @@ fn device() {
956956
panic!("failed to bind to any device.");
957957
}
958958

959+
#[cfg(all(
960+
feature = "all",
961+
any(
962+
target_os = "ios",
963+
target_os = "macos",
964+
target_os = "tvos",
965+
target_os = "watchos",
966+
)
967+
))]
968+
#[test]
969+
fn device_v6() {
970+
// Some common network interface on macOS.
971+
const INTERFACES: &[&str] = &["lo\0", "lo0\0", "en0\0"];
972+
973+
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
974+
assert_eq!(socket.device_index_v6().unwrap(), None);
975+
976+
for interface in INTERFACES.iter() {
977+
let iface_index = std::num::NonZeroU32::new(unsafe {
978+
libc::if_nametoindex(interface.as_ptr() as *const _)
979+
});
980+
// If no index is returned, try another interface alias
981+
if iface_index.is_none() {
982+
continue;
983+
}
984+
if let Err(err) = socket.bind_device_by_index_v6(iface_index) {
985+
// Network interface is not available try another.
986+
if matches!(err.raw_os_error(), Some(libc::ENODEV)) {
987+
eprintln!("error binding to device (`{interface}`): {err}");
988+
continue;
989+
} else {
990+
panic!("unexpected error binding device: {}", err);
991+
}
992+
}
993+
assert_eq!(socket.device_index_v6().unwrap(), iface_index);
994+
995+
socket.bind_device_by_index_v6(None).unwrap();
996+
assert_eq!(socket.device_index_v6().unwrap(), None);
997+
// Just need to do it with one interface.
998+
return;
999+
}
1000+
1001+
panic!("failed to bind to any device.");
1002+
}
1003+
9591004
#[cfg(all(
9601005
feature = "all",
9611006
any(

0 commit comments

Comments
 (0)