Skip to content

Commit 59e976d

Browse files
committed
Implement socket_bind_device and set_socket_bind_device
1 parent d50036a commit 59e976d

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed

src/backend/libc/net/sockopt.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ use alloc::borrow::ToOwned as _;
6969
target_os = "illumos"
7070
))]
7171
use alloc::string::String;
72+
#[cfg(feature = "alloc")]
73+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
74+
use alloc::vec::Vec;
7275
#[cfg(apple)]
7376
use c::TCP_KEEPALIVE as TCP_KEEPIDLE;
7477
#[cfg(not(any(apple, target_os = "haiku", target_os = "nto", target_os = "openbsd")))]
@@ -356,6 +359,41 @@ pub(crate) fn socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> {
356359
getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize)
357360
}
358361

362+
#[cfg(feature = "alloc")]
363+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
364+
#[inline]
365+
pub(crate) fn socket_bind_device(fd: BorrowedFd<'_>) -> io::Result<Vec<u8>> {
366+
let mut value = MaybeUninit::<[MaybeUninit<u8>; 16]>::uninit();
367+
let mut optlen = 16;
368+
getsockopt_raw(
369+
fd,
370+
c::SOL_SOCKET,
371+
c::SO_BINDTODEVICE,
372+
&mut value,
373+
&mut optlen,
374+
)?;
375+
unsafe {
376+
let value = value.assume_init();
377+
let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
378+
assert!(slice.contains(&b'\0'));
379+
Ok(slice[..optlen as usize - 1].to_vec())
380+
}
381+
}
382+
383+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
384+
#[inline]
385+
pub(crate) fn set_socket_bind_device(
386+
fd: BorrowedFd<'_>,
387+
interface: Option<&[u8]>,
388+
) -> io::Result<()> {
389+
setsockopt(
390+
fd,
391+
c::SOL_SOCKET,
392+
c::SO_BINDTODEVICE,
393+
interface.unwrap_or(&[]),
394+
)
395+
}
396+
359397
#[inline]
360398
pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> {
361399
let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?;

src/backend/linux_raw/net/sockopt.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ use crate::net::{
2222
use alloc::borrow::ToOwned as _;
2323
#[cfg(feature = "alloc")]
2424
use alloc::string::String;
25+
#[cfg(feature = "alloc")]
26+
use alloc::vec::Vec;
2527
use core::mem::{size_of, MaybeUninit};
2628
use core::time::Duration;
2729
use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval};
28-
use linux_raw_sys::net::{IPV6_MTU, IPV6_MULTICAST_IF, IP_MTU, IP_MULTICAST_IF};
30+
use linux_raw_sys::net::{IPV6_MTU, IPV6_MULTICAST_IF, IP_MTU, IP_MULTICAST_IF, SO_BINDTODEVICE};
2931
#[cfg(target_os = "linux")]
3032
use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
3133
#[cfg(target_arch = "x86")]
@@ -432,6 +434,28 @@ pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Res
432434
setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value)
433435
}
434436

437+
#[cfg(feature = "alloc")]
438+
#[inline]
439+
pub(crate) fn socket_bind_device(fd: BorrowedFd<'_>) -> io::Result<Vec<u8>> {
440+
let mut value = MaybeUninit::<[MaybeUninit<u8>; 16]>::uninit();
441+
let mut optlen = 16;
442+
getsockopt_raw(fd, c::SOL_SOCKET, SO_BINDTODEVICE, &mut value, &mut optlen)?;
443+
unsafe {
444+
let value = value.assume_init();
445+
let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]);
446+
assert!(slice.contains(&b'\0'));
447+
Ok(slice[..optlen as usize - 1].to_vec())
448+
}
449+
}
450+
451+
#[inline]
452+
pub(crate) fn set_socket_bind_device(
453+
fd: BorrowedFd<'_>,
454+
interface: Option<&[u8]>,
455+
) -> io::Result<()> {
456+
setsockopt(fd, c::SOL_SOCKET, SO_BINDTODEVICE, interface.unwrap_or(&[]))
457+
}
458+
435459
#[inline]
436460
pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
437461
setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl)

src/net/sockopt.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,30 @@ pub fn set_socket_incoming_cpu<Fd: AsFd>(fd: Fd, value: u32) -> io::Result<()> {
616616
backend::net::sockopt::set_socket_incoming_cpu(fd.as_fd(), value)
617617
}
618618

619+
/// `getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE)`
620+
///
621+
/// See the [module-level documentation] for more.
622+
///
623+
/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions
624+
#[cfg(feature = "alloc")]
625+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
626+
#[inline]
627+
#[doc(alias = "SO_BINDTODEVICE")]
628+
pub fn socket_bind_device<Fd: AsFd>(fd: Fd) -> io::Result<Vec<u8>> {
629+
backend::net::sockopt::socket_bind_device(fd.as_fd())
630+
}
631+
632+
/// `setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, interface)`
633+
///
634+
/// See the [module-level documentation] for more.
635+
///
636+
/// [module-level documentation]: self#references-for-get_socket_-and-set_socket_-functions
637+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
638+
#[doc(alias = "SO_BINDTODEVICE")]
639+
pub fn set_socket_bind_device<Fd: AsFd>(fd: Fd, interface: Option<&[u8]>) -> io::Result<()> {
640+
backend::net::sockopt::set_socket_bind_device(fd.as_fd(), interface)
641+
}
642+
619643
/// `setsockopt(fd, IPPROTO_IP, IP_TTL, value)`
620644
///
621645
/// See the [module-level documentation] for more.

tests/net/sockopt.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,3 +625,26 @@ fn test_sockopts_multicast_ifv6() {
625625
Err(e) => panic!("{e}"),
626626
}
627627
}
628+
629+
#[cfg(feature = "alloc")]
630+
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
631+
#[test]
632+
fn test_socket_bind_device() {
633+
let fd = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap();
634+
635+
let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex")
636+
.unwrap()
637+
.as_str()
638+
.split_at(1)
639+
.0
640+
.parse::<u32>()
641+
.unwrap();
642+
let name = rustix::net::netdevice::index_to_name(&fd, loopback_index).unwrap();
643+
644+
sockopt::set_socket_bind_device(&fd, Some(name.as_bytes())).unwrap();
645+
646+
assert_eq!(
647+
name.as_bytes(),
648+
sockopt::socket_bind_device(&fd).unwrap().as_slice()
649+
);
650+
}

0 commit comments

Comments
 (0)