Skip to content

Commit 92e8580

Browse files
committed
Adds bind_device_by_index to linux-like OS
This commit adds bind_device_by_index to android, fuchsia, and linux OS. This binds socket to a particular interface similarly to bind_device except that the interfaces are referenced by its index rather than name.
1 parent 36dec54 commit 92e8580

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ targets = [
5151
features = ["all"]
5252

5353
[target."cfg(unix)".dependencies]
54-
libc = "0.2.171"
54+
libc = "0.2.172"
5555

5656
[target.'cfg(windows)'.dependencies.windows-sys]
5757
version = "0.52"

src/sys/unix.rs

+41
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ use std::net::{Ipv4Addr, Ipv6Addr};
1717
#[cfg(all(
1818
feature = "all",
1919
any(
20+
target_os = "android",
21+
target_os = "fuchsia",
2022
target_os = "ios",
2123
target_os = "visionos",
24+
target_os = "linux",
2225
target_os = "macos",
2326
target_os = "tvos",
2427
target_os = "watchos",
@@ -1805,6 +1808,44 @@ impl crate::Socket {
18051808
.map(|_| ())
18061809
}
18071810

1811+
/// Gets the value for the `SO_BINDTOIFINDEX` option on this socket.
1812+
///
1813+
/// This value gets the socket binded device's interface index.
1814+
#[cfg(all(
1815+
feature = "all",
1816+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1817+
))]
1818+
pub fn device_index(&self) -> io::Result<Option<NonZeroU32>> {
1819+
let index_raw = unsafe {
1820+
getsockopt::<libc::c_uint>(self.as_raw(), libc::SOL_SOCKET, libc::SO_BINDTOIFINDEX)?
1821+
};
1822+
Ok(NonZeroU32::new(index_raw))
1823+
}
1824+
1825+
/// Sets the value for `SO_BINDTOIFINDEX` option on this socket.
1826+
///
1827+
/// If a socket is bound to an interface, only packets received from that
1828+
/// particular interface are processed by the socket. Note that this only
1829+
/// works for some socket types, particularly `AF_INET` sockets.
1830+
///
1831+
/// If `interface` is `None`, the binding is removed. If the `interface`
1832+
/// index is not valid, an error is returned.
1833+
#[cfg(all(
1834+
feature = "all",
1835+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1836+
))]
1837+
pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
1838+
let index = interface.map_or(0, NonZeroU32::get);
1839+
unsafe {
1840+
setsockopt(
1841+
self.as_raw(),
1842+
libc::SOL_SOCKET,
1843+
libc::SO_BINDTOIFINDEX,
1844+
index as c_int,
1845+
)
1846+
}
1847+
}
1848+
18081849
/// Sets the value for the `SO_SETFIB` option on this socket.
18091850
///
18101851
/// Bind socket to the specified forwarding table (VRF) on a FreeBSD.

tests/socket.rs

+35
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,41 @@ fn device() {
982982
panic!("failed to bind to any device.");
983983
}
984984

985+
#[cfg(all(
986+
feature = "all",
987+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
988+
))]
989+
#[test]
990+
#[ignore = "setting `SO_BINDTOIFINDEX` requires the `CAP_NET_RAW` capability (works when running as root)"]
991+
fn device_by_index() {
992+
const INTERFACE_INDICES: &[u32; 3] = &[1, 2, 3];
993+
994+
let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
995+
assert_eq!(socket.device().unwrap(), None);
996+
997+
for if_index in INTERFACE_INDICES {
998+
if let Err(err) = socket.bind_device_by_index(std::num::NonZeroU32::new(*if_index)) {
999+
if matches!(err.raw_os_error(), Some(libc::ENODEV)) {
1000+
eprintln!("error binding to interface index (`{if_index}`): {err}");
1001+
continue;
1002+
} else {
1003+
panic!("unexpected error binding device: {}", err);
1004+
}
1005+
}
1006+
assert_eq!(
1007+
socket.device_index().unwrap(),
1008+
std::num::NonZeroU32::new(*if_index)
1009+
);
1010+
1011+
socket.bind_device_by_index(None).unwrap();
1012+
assert_eq!(socket.device_index().unwrap(), None);
1013+
// Just need to do it with one interface.
1014+
return;
1015+
}
1016+
1017+
panic!("failed to bind to any device by IFINDEX.");
1018+
}
1019+
9851020
#[cfg(all(
9861021
feature = "all",
9871022
any(

0 commit comments

Comments
 (0)