@@ -14,6 +14,8 @@ use crate::fd::BorrowedFd;
1414use crate :: ffi:: CStr ;
1515use crate :: io;
1616use crate :: net:: sockopt:: Timeout ;
17+ #[ cfg( target_os = "linux" ) ]
18+ use crate :: net:: xdp:: { XdpMmapOffsets , XdpOptionsFlags , XdpRingOffset , XdpStatistics , XdpUmemReg } ;
1719#[ cfg( not( any(
1820 apple,
1921 windows,
@@ -73,6 +75,8 @@ use c::TCP_KEEPALIVE as TCP_KEEPIDLE;
7375use c:: TCP_KEEPIDLE ;
7476use core:: mem:: { size_of, MaybeUninit } ;
7577use core:: time:: Duration ;
78+ #[ cfg( target_os = "linux" ) ]
79+ use linux_raw_sys:: xdp:: { xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1} ;
7680#[ cfg( windows) ]
7781use windows_sys:: Win32 :: Foundation :: BOOL ;
7882
@@ -963,6 +967,170 @@ pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
963967 getsockopt ( fd, c:: SOL_SOCKET , c:: SO_PEERCRED )
964968}
965969
970+ #[ cfg( target_os = "linux" ) ]
971+ #[ inline]
972+ pub ( crate ) fn set_xdp_umem_reg ( fd : BorrowedFd < ' _ > , value : XdpUmemReg ) -> io:: Result < ( ) > {
973+ setsockopt ( fd, c:: SOL_XDP , c:: XDP_UMEM_REG , value)
974+ }
975+
976+ #[ cfg( target_os = "linux" ) ]
977+ #[ inline]
978+ pub ( crate ) fn set_xdp_umem_fill_ring_size ( fd : BorrowedFd < ' _ > , value : u32 ) -> io:: Result < ( ) > {
979+ setsockopt ( fd, c:: SOL_XDP , c:: XDP_UMEM_FILL_RING , value)
980+ }
981+
982+ #[ cfg( target_os = "linux" ) ]
983+ #[ inline]
984+ pub ( crate ) fn set_xdp_umem_completion_ring_size ( fd : BorrowedFd < ' _ > , value : u32 ) -> io:: Result < ( ) > {
985+ setsockopt ( fd, c:: SOL_XDP , c:: XDP_UMEM_COMPLETION_RING , value)
986+ }
987+
988+ #[ cfg( target_os = "linux" ) ]
989+ #[ inline]
990+ pub ( crate ) fn set_xdp_tx_ring_size ( fd : BorrowedFd < ' _ > , value : u32 ) -> io:: Result < ( ) > {
991+ setsockopt ( fd, c:: SOL_XDP , c:: XDP_TX_RING , value)
992+ }
993+
994+ #[ cfg( target_os = "linux" ) ]
995+ #[ inline]
996+ pub ( crate ) fn set_xdp_rx_ring_size ( fd : BorrowedFd < ' _ > , value : u32 ) -> io:: Result < ( ) > {
997+ setsockopt ( fd, c:: SOL_XDP , c:: XDP_RX_RING , value)
998+ }
999+
1000+ #[ cfg( target_os = "linux" ) ]
1001+ #[ inline]
1002+ pub ( crate ) fn get_xdp_mmap_offsets ( fd : BorrowedFd < ' _ > ) -> io:: Result < XdpMmapOffsets > {
1003+ // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the supplied pointer,
1004+ // depending on the kernel version. Both structs only contain u64 values.
1005+ // By using the larger of both as the parameter, we can shuffle the values to the non-v1 version
1006+ // returned by `get_xdp_mmap_offsets` while keeping the return type unaffected by the kernel
1007+ // version. This works because C will layout all struct members one after the other.
1008+
1009+ let mut optlen = core:: mem:: size_of :: < xdp_mmap_offsets > ( ) . try_into ( ) . unwrap ( ) ;
1010+ debug_assert ! (
1011+ optlen as usize >= core:: mem:: size_of:: <c:: c_int>( ) ,
1012+ "Socket APIs don't ever use `bool` directly"
1013+ ) ;
1014+ let mut value = MaybeUninit :: < xdp_mmap_offsets > :: zeroed ( ) ;
1015+ getsockopt_raw ( fd, c:: SOL_XDP , c:: XDP_MMAP_OFFSETS , & mut value, & mut optlen) ?;
1016+
1017+ if optlen as usize == core:: mem:: size_of :: < c:: xdp_mmap_offsets_v1 > ( ) {
1018+ // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1019+ // by `MaybeUninit::<xdp_statistics>::zeroed()`
1020+ let xpd_mmap_offsets = unsafe { value. assume_init ( ) } ;
1021+ Ok ( XdpMmapOffsets {
1022+ rx : XdpRingOffset {
1023+ producer : xpd_mmap_offsets. rx . producer ,
1024+ consumer : xpd_mmap_offsets. rx . consumer ,
1025+ desc : xpd_mmap_offsets. rx . desc ,
1026+ flags : None ,
1027+ } ,
1028+ tx : XdpRingOffset {
1029+ producer : xpd_mmap_offsets. rx . flags ,
1030+ consumer : xpd_mmap_offsets. tx . producer ,
1031+ desc : xpd_mmap_offsets. tx . consumer ,
1032+ flags : None ,
1033+ } ,
1034+ fr : XdpRingOffset {
1035+ producer : xpd_mmap_offsets. tx . desc ,
1036+ consumer : xpd_mmap_offsets. tx . flags ,
1037+ desc : xpd_mmap_offsets. fr . producer ,
1038+ flags : None ,
1039+ } ,
1040+ cr : XdpRingOffset {
1041+ producer : xpd_mmap_offsets. fr . consumer ,
1042+ consumer : xpd_mmap_offsets. fr . desc ,
1043+ desc : xpd_mmap_offsets. fr . flags ,
1044+ flags : None ,
1045+ } ,
1046+ } )
1047+ } else {
1048+ assert_eq ! (
1049+ optlen as usize ,
1050+ core:: mem:: size_of:: <xdp_mmap_offsets>( ) ,
1051+ "unexpected getsockopt size"
1052+ ) ;
1053+ // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1054+ // by `MaybeUninit::<xdp_statistics>::zeroed()`
1055+ let xpd_mmap_offsets = unsafe { value. assume_init ( ) } ;
1056+ Ok ( XdpMmapOffsets {
1057+ rx : XdpRingOffset {
1058+ producer : xpd_mmap_offsets. rx . producer ,
1059+ consumer : xpd_mmap_offsets. rx . consumer ,
1060+ desc : xpd_mmap_offsets. rx . desc ,
1061+ flags : Some ( xpd_mmap_offsets. rx . flags ) ,
1062+ } ,
1063+ tx : XdpRingOffset {
1064+ producer : xpd_mmap_offsets. tx . producer ,
1065+ consumer : xpd_mmap_offsets. tx . consumer ,
1066+ desc : xpd_mmap_offsets. tx . desc ,
1067+ flags : Some ( xpd_mmap_offsets. tx . flags ) ,
1068+ } ,
1069+ fr : XdpRingOffset {
1070+ producer : xpd_mmap_offsets. fr . producer ,
1071+ consumer : xpd_mmap_offsets. fr . consumer ,
1072+ desc : xpd_mmap_offsets. fr . desc ,
1073+ flags : Some ( xpd_mmap_offsets. fr . flags ) ,
1074+ } ,
1075+ cr : XdpRingOffset {
1076+ producer : xpd_mmap_offsets. cr . producer ,
1077+ consumer : xpd_mmap_offsets. cr . consumer ,
1078+ desc : xpd_mmap_offsets. cr . desc ,
1079+ flags : Some ( xpd_mmap_offsets. cr . flags ) ,
1080+ } ,
1081+ } )
1082+ }
1083+ }
1084+
1085+ #[ cfg( target_os = "linux" ) ]
1086+ #[ inline]
1087+ pub ( crate ) fn get_xdp_statistics ( fd : BorrowedFd < ' _ > ) -> io:: Result < XdpStatistics > {
1088+ let mut optlen = core:: mem:: size_of :: < xdp_statistics > ( ) . try_into ( ) . unwrap ( ) ;
1089+ debug_assert ! (
1090+ optlen as usize >= core:: mem:: size_of:: <c:: c_int>( ) ,
1091+ "Socket APIs don't ever use `bool` directly"
1092+ ) ;
1093+ let mut value = MaybeUninit :: < xdp_statistics > :: zeroed ( ) ;
1094+ getsockopt_raw ( fd, c:: SOL_XDP , c:: XDP_STATISTICS , & mut value, & mut optlen) ?;
1095+
1096+ if optlen as usize == core:: mem:: size_of :: < xdp_statistics_v1 > ( ) {
1097+ // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1098+ // by `MaybeUninit::<xdp_statistics>::zeroed()`
1099+ let xdp_statistics = unsafe { value. assume_init ( ) } ;
1100+ Ok ( XdpStatistics {
1101+ rx_dropped : xdp_statistics. rx_dropped ,
1102+ rx_invalid_descs : xdp_statistics. rx_dropped ,
1103+ tx_invalid_descs : xdp_statistics. rx_dropped ,
1104+ rx_ring_full : None ,
1105+ rx_fill_ring_empty_descs : None ,
1106+ tx_ring_empty_descs : None ,
1107+ } )
1108+ } else {
1109+ assert_eq ! (
1110+ optlen as usize ,
1111+ core:: mem:: size_of:: <xdp_statistics>( ) ,
1112+ "unexpected getsockopt size"
1113+ ) ;
1114+ // Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1115+ // by `MaybeUninit::<xdp_statistics>::zeroed()`
1116+ let xdp_statistics = unsafe { value. assume_init ( ) } ;
1117+ Ok ( XdpStatistics {
1118+ rx_dropped : xdp_statistics. rx_dropped ,
1119+ rx_invalid_descs : xdp_statistics. rx_invalid_descs ,
1120+ tx_invalid_descs : xdp_statistics. tx_invalid_descs ,
1121+ rx_ring_full : Some ( xdp_statistics. rx_ring_full ) ,
1122+ rx_fill_ring_empty_descs : Some ( xdp_statistics. rx_fill_ring_empty_descs ) ,
1123+ tx_ring_empty_descs : Some ( xdp_statistics. tx_ring_empty_descs ) ,
1124+ } )
1125+ }
1126+ }
1127+
1128+ #[ cfg( target_os = "linux" ) ]
1129+ #[ inline]
1130+ pub ( crate ) fn get_xdp_options ( fd : BorrowedFd < ' _ > ) -> io:: Result < XdpOptionsFlags > {
1131+ getsockopt ( fd, c:: SOL_XDP , c:: XDP_OPTIONS )
1132+ }
1133+
9661134#[ inline]
9671135fn to_ip_mreq ( multiaddr : & Ipv4Addr , interface : & Ipv4Addr ) -> c:: ip_mreq {
9681136 c:: ip_mreq {
0 commit comments