77// except according to those terms.
88
99use std:: cmp:: min;
10+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
11+ use std:: ffi:: { CStr , CString } ;
1012#[ cfg( not( target_os = "redox" ) ) ]
1113use std:: io:: { IoSlice , IoSliceMut } ;
1214use std:: mem:: { self , size_of, MaybeUninit } ;
@@ -19,6 +21,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
1921use std:: os:: unix:: net:: { UnixDatagram , UnixListener , UnixStream } ;
2022#[ cfg( feature = "all" ) ]
2123use std:: path:: Path ;
24+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
25+ use std:: slice;
2226use std:: time:: Duration ;
2327use std:: { io, ptr} ;
2428
@@ -867,6 +871,73 @@ impl crate::Socket {
867871 unsafe { setsockopt :: < c_int > ( self . inner , libc:: SOL_SOCKET , libc:: SO_MARK , mark as c_int ) }
868872 }
869873
874+ /// Gets the value for the `SO_BINDTODEVICE` option on this socket.
875+ ///
876+ /// This value gets the socket binded device's interface name.
877+ ///
878+ /// This function is only available on Linux.
879+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
880+ pub fn device ( & self ) -> io:: Result < Option < CString > > {
881+ // TODO: replace with `MaybeUninit::uninit_array` once stable.
882+ let mut buf: [ MaybeUninit < u8 > ; libc:: IFNAMSIZ ] =
883+ unsafe { MaybeUninit :: < [ MaybeUninit < u8 > ; libc:: IFNAMSIZ ] > :: uninit ( ) . assume_init ( ) } ;
884+ let mut len = buf. len ( ) as libc:: socklen_t ;
885+ unsafe {
886+ syscall ! ( getsockopt(
887+ self . inner,
888+ libc:: SOL_SOCKET ,
889+ libc:: SO_BINDTODEVICE ,
890+ buf. as_mut_ptr( ) . cast( ) ,
891+ & mut len,
892+ ) ) ?;
893+ }
894+ if len == 0 {
895+ Ok ( None )
896+ } else {
897+ // Allocate a buffer for `CString` with the length including the
898+ // null terminator.
899+ let len = len as usize ;
900+ let mut name = Vec :: with_capacity ( len) ;
901+
902+ // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
903+ // Safety: `len` bytes are writen by the OS, this includes a null
904+ // terminator. However we don't copy the null terminator because
905+ // `CString::from_vec_unchecked` adds its own null terminator.
906+ let buf = unsafe { slice:: from_raw_parts ( buf. as_ptr ( ) . cast ( ) , len - 1 ) } ;
907+ name. extend_from_slice ( buf) ;
908+
909+ // Safety: the OS initialised the string for us, which shouldn't
910+ // include any null bytes.
911+ Ok ( Some ( unsafe { CString :: from_vec_unchecked ( name) } ) )
912+ }
913+ }
914+
915+ /// Sets the value for the `SO_BINDTODEVICE` option on this socket.
916+ ///
917+ /// If a socket is bound to an interface, only packets received from that
918+ /// particular interface are processed by the socket. Note that this only
919+ /// works for some socket types, particularly `AF_INET` sockets.
920+ ///
921+ /// If `interface` is `None` or an empty string it removes the binding.
922+ ///
923+ /// This function is only available on Linux.
924+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
925+ pub fn bind_device ( & self , interface : Option < & CStr > ) -> io:: Result < ( ) > {
926+ let ( value, len) = if let Some ( interface) = interface {
927+ ( interface. as_ptr ( ) , interface. to_bytes_with_nul ( ) . len ( ) )
928+ } else {
929+ ( ptr:: null ( ) , 0 )
930+ } ;
931+ syscall ! ( setsockopt(
932+ self . inner,
933+ libc:: SOL_SOCKET ,
934+ libc:: SO_BINDTODEVICE ,
935+ value. cast( ) ,
936+ len as libc:: socklen_t,
937+ ) )
938+ . map ( |_| ( ) )
939+ }
940+
870941 /// Get the value of the `SO_REUSEPORT` option on this socket.
871942 ///
872943 /// For more information about this option, see [`set_reuse_port`].
0 commit comments