Skip to content

Commit b0210c7

Browse files
committed
std::net: adding new option todevice.
Allows to bind a socket to an network interface, to exclusively listen from it. For packet sockets, regular bind is more appropriate.
1 parent 569d7e3 commit b0210c7

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

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

+53
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use libc::MSG_NOSIGNAL;
1515
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1616
use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary};
1717
use super::{sockaddr_un, SocketAddr};
18+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
19+
use crate::ffi::CStr;
1820
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1921
use crate::io::{IoSlice, IoSliceMut};
2022
use crate::net::Shutdown;
@@ -836,6 +838,57 @@ impl UnixDatagram {
836838
self.0.set_mark(mark)
837839
}
838840

841+
/// Bind the socket to an interface
842+
///
843+
#[cfg_attr(
844+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
845+
doc = "```no_run"
846+
)]
847+
#[cfg_attr(
848+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
849+
doc = "```ignore"
850+
)]
851+
/// #![feature(unix_set_todevice)]
852+
/// use std::os::unix::net::UnixDatagram;
853+
///
854+
/// fn main() -> std::io::Result<()> {
855+
/// let socket = UnixDatagram::unbound()?;
856+
/// socket.set_todevice(c"eth0")?;
857+
/// Ok(())
858+
/// }
859+
/// ```
860+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
861+
#[unstable(feature = "unix_set_todevice", issue = "129182")]
862+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
863+
self.0.set_todevice(ifrname)
864+
}
865+
866+
/// Get the interface this socket is bound to
867+
///
868+
#[cfg_attr(
869+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
870+
doc = "```no_run"
871+
)]
872+
#[cfg_attr(
873+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
874+
doc = "```ignore"
875+
)]
876+
/// #![feature(unix_set_todevice)]
877+
/// use std::os::unix::net::UnixDatagram;
878+
///
879+
/// fn main() -> std::io::Result<()> {
880+
/// let socket = UnixDatagram::unbound()?;
881+
/// socket.set_todevice(c"eth0")?;
882+
/// let name = socket.todevice()?;
883+
/// assert_eq!(Ok("eth0"), name.to_str());
884+
/// Ok(())
885+
/// }
886+
/// ```
887+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
888+
#[unstable(feature = "unix_set_todevice", issue = "129182")]
889+
pub fn todevice(&self) -> io::Result<&CStr> {
890+
self.0.todevice()
891+
}
839892
/// Returns the value of the `SO_ERROR` option.
840893
///
841894
/// # Examples

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

+54
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use super::{peer_cred, UCred};
1212
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
1313
use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary};
1414
use super::{sockaddr_un, SocketAddr};
15+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
16+
use crate::ffi::CStr;
1517
use crate::fmt;
1618
use crate::io::{self, IoSlice, IoSliceMut};
1719
use crate::net::Shutdown;
@@ -405,6 +407,58 @@ impl UnixStream {
405407
self.0.set_mark(mark)
406408
}
407409

410+
/// Bind the socket to an interface
411+
///
412+
#[cfg_attr(
413+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
414+
doc = "```no_run"
415+
)]
416+
#[cfg_attr(
417+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
418+
doc = "```ignore"
419+
)]
420+
/// #![feature(unix_set_todevice)]
421+
/// use std::os::unix::net::UnixStream;
422+
///
423+
/// fn main() -> std::io::Result<()> {
424+
/// let socket = UnixStream::connect("/tmp/sock")?;
425+
/// socket.set_todevice(c"eth0")?;
426+
/// Ok(())
427+
/// }
428+
/// ```
429+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
430+
#[unstable(feature = "unix_set_todevice", issue = "129182")]
431+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
432+
self.0.set_todevice(ifrname)
433+
}
434+
435+
/// Get the interface this socket is bound to
436+
///
437+
#[cfg_attr(
438+
any(target_os = "linux", target_os = "haiku", target_os = "vxworks"),
439+
doc = "```no_run"
440+
)]
441+
#[cfg_attr(
442+
not(any(target_os = "linux", target_os = "haiku", target_os = "vxworks")),
443+
doc = "```ignore"
444+
)]
445+
/// #![feature(unix_set_todevice)]
446+
/// use std::os::unix::net::UnixStream;
447+
///
448+
/// fn main() -> std::io::Result<()> {
449+
/// let socket = UnixStream::connect("/tmp/sock")?;
450+
/// socket.set_todevice(c"eth0")?;
451+
/// let name = socket.todevice()?;
452+
/// assert_eq!(Ok("eth0"), name.to_str());
453+
/// Ok(())
454+
/// }
455+
/// ```
456+
#[cfg(any(doc, target_os = "linux", target_os = "haiku", target_os = "vxworks",))]
457+
#[unstable(feature = "unix_set_todevice", issue = "129182")]
458+
pub fn todevice(&self) -> io::Result<&CStr> {
459+
self.0.todevice()
460+
}
461+
408462
/// Returns the value of the `SO_ERROR` option.
409463
///
410464
/// # Examples

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

+18
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,24 @@ impl Socket {
563563
setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int)
564564
}
565565

566+
#[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))]
567+
pub fn todevice(&self) -> io::Result<&CStr> {
568+
let buf: [libc::c_char; libc::IFNAMSIZ] =
569+
getsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE)?;
570+
let s: &[u8] = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len()) };
571+
let ifrname = CStr::from_bytes_with_nul(s).unwrap();
572+
Ok(ifrname)
573+
}
574+
575+
#[cfg(any(target_os = "linux", target_os = "haiku", target_os = "vxworks"))]
576+
pub fn set_todevice(&self, ifrname: &CStr) -> io::Result<()> {
577+
let mut buf = [0; libc::IFNAMSIZ];
578+
for (src, dst) in ifrname.to_bytes().iter().zip(&mut buf[..libc::IFNAMSIZ - 1]) {
579+
*dst = *src as libc::c_char;
580+
}
581+
setsockopt(self, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, buf)
582+
}
583+
566584
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
567585
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
568586
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }

0 commit comments

Comments
 (0)