Skip to content

Commit

Permalink
Add IPv6 versions of bind_device_by_index
Browse files Browse the repository at this point in the history
  • Loading branch information
pinkisemils committed Apr 13, 2023
1 parent 5bdcbf0 commit 975f678
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# 0.5.3

## Changed
* Deprecated `bind_device_by_index` and added versioned
IPv4 and IPv6 anaolgues `bind_device_by_index_v4` and
`bind_device_by_index_v6.`


# 0.5.2

* Add Unix socket methods to `SockAddr`
Expand Down
101 changes: 100 additions & 1 deletion src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,36 @@ impl crate::Socket {
.map(|_| ())
}

/// This method is deprecated, use [`bind_device_by_index_v4`].
///
/// [`bind_device_by_index_v4`]: crate::Socket::bind_device_by_index_v4
#[cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
))]
#[cfg_attr(
docsrs,
doc(cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
)))
)]
#[deprecated]
pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
let index = interface.map_or(0, NonZeroU32::get);
unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
}

/// Sets the value for `IP_BOUND_IF` option on this socket.
///
/// If a socket is bound to an interface, only packets received from that
Expand Down Expand Up @@ -1864,11 +1894,47 @@ impl crate::Socket {
)
)))
)]
pub fn bind_device_by_index(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
pub fn bind_device_by_index_v4(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
let index = interface.map_or(0, NonZeroU32::get);
unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) }
}

/// Sets the value for `IPV6_BOUND_IF` option on this socket.
///
/// If a socket is bound to an interface, only packets received from that
/// particular interface are processed by the socket.
///
/// If `interface` is `None`, the binding is removed. If the `interface`
/// index is not valid, an error is returned.
///
/// One can use [`libc::if_nametoindex`] to convert an interface alias to an
/// index.
#[cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
))]
#[cfg_attr(
docsrs,
doc(cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
)))
)]
pub fn bind_device_by_index_v6(&self, interface: Option<NonZeroU32>) -> io::Result<()> {
let index = interface.map_or(0, NonZeroU32::get);
unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) }
}

/// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index
/// for the interface to which the socket is bound.
///
Expand Down Expand Up @@ -1901,6 +1967,39 @@ impl crate::Socket {
Ok(NonZeroU32::new(index))
}

/// Gets the value for `IPV6_BOUND_IF` option on this socket, i.e. the index
/// for the interface to which the socket is bound.
///
/// Returns `None` if the socket is not bound to any interface, otherwise
/// returns an interface index.
#[cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
))]
#[cfg_attr(
docsrs,
doc(cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
)))
)]
pub fn device_index_v6(&self) -> io::Result<Option<NonZeroU32>> {
let index = unsafe {
getsockopt::<libc::c_uint>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)?
};
Ok(NonZeroU32::new(index))
}

/// Get the value of the `SO_INCOMING_CPU` option on this socket.
///
/// For more information about this option, see [`set_cpu_affinity`].
Expand Down
45 changes: 45 additions & 0 deletions tests/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,51 @@ fn device() {
panic!("failed to bind to any device.");
}

#[cfg(all(
feature = "all",
any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)
))]
#[test]
fn device_v6() {
// Some common network interface on macOS.
const INTERFACES: &[&str] = &["lo\0", "lo0\0", "en0\0"];

let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
assert_eq!(socket.device_index_v6().unwrap(), None);

for interface in INTERFACES.iter() {
let iface_index = std::num::NonZeroU32::new(unsafe {
libc::if_nametoindex(interface.as_ptr() as *const _)
});
// If no index is returned, try another interface alias
if iface_index.is_none() {
continue;
}
if let Err(err) = socket.bind_device_by_index_v6(iface_index) {
// Network interface is not available try another.
if matches!(err.raw_os_error(), Some(libc::ENODEV)) {
eprintln!("error binding to device (`{interface}`): {err}");
continue;
} else {
panic!("unexpected error binding device: {}", err);
}
}
assert_eq!(socket.device_index_v6().unwrap(), iface_index);

socket.bind_device_by_index_v6(None).unwrap();
assert_eq!(socket.device_index_v6().unwrap(), None);
// Just need to do it with one interface.
return;
}

panic!("failed to bind to any device.");
}

#[cfg(all(
feature = "all",
any(
Expand Down

0 comments on commit 975f678

Please sign in to comment.