7
7
// except according to those terms.
8
8
9
9
use std:: cmp:: min;
10
+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
11
+ use std:: ffi:: { CStr , CString } ;
10
12
#[ cfg( not( target_os = "redox" ) ) ]
11
13
use std:: io:: { IoSlice , IoSliceMut } ;
12
14
use std:: mem:: { self , size_of, MaybeUninit } ;
@@ -19,6 +21,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
19
21
use std:: os:: unix:: net:: { UnixDatagram , UnixListener , UnixStream } ;
20
22
#[ cfg( feature = "all" ) ]
21
23
use std:: path:: Path ;
24
+ #[ cfg( all( feature = "all" , target_os = "linux" ) ) ]
25
+ use std:: slice;
22
26
use std:: time:: Duration ;
23
27
use std:: { io, ptr} ;
24
28
@@ -867,6 +871,73 @@ impl crate::Socket {
867
871
unsafe { setsockopt :: < c_int > ( self . inner , libc:: SOL_SOCKET , libc:: SO_MARK , mark as c_int ) }
868
872
}
869
873
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
+
870
941
/// Get the value of the `SO_REUSEPORT` option on this socket.
871
942
///
872
943
/// For more information about this option, see [`set_reuse_port`].
0 commit comments