| 1 | //! linux_raw syscalls supporting `rustix::net::sockopt`. |
| 2 | //! |
| 3 | //! # Safety |
| 4 | //! |
| 5 | //! See the `rustix::backend` module documentation for details. |
| 6 | #![allow (unsafe_code, clippy::undocumented_unsafe_blocks)] |
| 7 | |
| 8 | use crate::backend::c; |
| 9 | use crate::backend::conv::{by_mut, c_uint, ret, socklen_t}; |
| 10 | use crate::fd::BorrowedFd; |
| 11 | #[cfg (feature = "alloc" )] |
| 12 | use crate::ffi::CStr; |
| 13 | use crate::io; |
| 14 | use crate::net::sockopt::Timeout; |
| 15 | #[cfg (target_os = "linux" )] |
| 16 | use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg}; |
| 17 | use crate::net::{ |
| 18 | AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage, |
| 19 | SocketAddrV4, SocketAddrV6, SocketType, UCred, |
| 20 | }; |
| 21 | #[cfg (feature = "alloc" )] |
| 22 | use alloc::borrow::ToOwned; |
| 23 | #[cfg (feature = "alloc" )] |
| 24 | use alloc::string::String; |
| 25 | use core::mem::MaybeUninit; |
| 26 | use core::time::Duration; |
| 27 | use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval}; |
| 28 | #[cfg (target_os = "linux" )] |
| 29 | use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1}; |
| 30 | #[cfg (target_arch = "x86" )] |
| 31 | use { |
| 32 | crate::backend::conv::{slice_just_addr, x86_sys}, |
| 33 | crate::backend::reg::{ArgReg, SocketArg}, |
| 34 | linux_raw_sys::net::{SYS_GETSOCKOPT, SYS_SETSOCKOPT}, |
| 35 | }; |
| 36 | |
| 37 | #[inline ] |
| 38 | fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32) -> io::Result<T> { |
| 39 | let mut optlen: c::socklen_t = core::mem::size_of::<T>().try_into().unwrap(); |
| 40 | debug_assert!( |
| 41 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
| 42 | "Socket APIs don't ever use `bool` directly" |
| 43 | ); |
| 44 | |
| 45 | let mut value: MaybeUninit = MaybeUninit::<T>::uninit(); |
| 46 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
| 47 | |
| 48 | assert_eq!( |
| 49 | optlen as usize, |
| 50 | core::mem::size_of::<T>(), |
| 51 | "unexpected getsockopt size" |
| 52 | ); |
| 53 | |
| 54 | unsafe { Ok(value.assume_init()) } |
| 55 | } |
| 56 | |
| 57 | #[inline ] |
| 58 | fn getsockopt_raw<T>( |
| 59 | fd: BorrowedFd<'_>, |
| 60 | level: u32, |
| 61 | optname: u32, |
| 62 | value: &mut MaybeUninit<T>, |
| 63 | optlen: &mut c::socklen_t, |
| 64 | ) -> io::Result<()> { |
| 65 | #[cfg (not(target_arch = "x86" ))] |
| 66 | unsafe { |
| 67 | ret(syscall!( |
| 68 | __NR_getsockopt, |
| 69 | fd, |
| 70 | c_uint(level), |
| 71 | c_uint(optname), |
| 72 | value, |
| 73 | by_mut(optlen) |
| 74 | )) |
| 75 | } |
| 76 | #[cfg (target_arch = "x86" )] |
| 77 | unsafe { |
| 78 | ret(syscall!( |
| 79 | __NR_socketcall, |
| 80 | x86_sys(SYS_GETSOCKOPT), |
| 81 | slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ |
| 82 | fd.into(), |
| 83 | c_uint(level), |
| 84 | c_uint(optname), |
| 85 | value.into(), |
| 86 | by_mut(optlen), |
| 87 | ]) |
| 88 | )) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | #[inline ] |
| 93 | fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: u32, optname: u32, value: T) -> io::Result<()> { |
| 94 | let optlen: u32 = core::mem::size_of::<T>().try_into().unwrap(); |
| 95 | debug_assert!( |
| 96 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
| 97 | "Socket APIs don't ever use `bool` directly" |
| 98 | ); |
| 99 | setsockopt_raw(fd, level, optname, &value, optlen) |
| 100 | } |
| 101 | |
| 102 | #[inline ] |
| 103 | fn setsockopt_raw<T>( |
| 104 | fd: BorrowedFd<'_>, |
| 105 | level: u32, |
| 106 | optname: u32, |
| 107 | ptr: *const T, |
| 108 | optlen: c::socklen_t, |
| 109 | ) -> io::Result<()> { |
| 110 | #[cfg (not(target_arch = "x86" ))] |
| 111 | unsafe { |
| 112 | ret(syscall_readonly!( |
| 113 | __NR_setsockopt, |
| 114 | fd, |
| 115 | c_uint(level), |
| 116 | c_uint(optname), |
| 117 | ptr, |
| 118 | socklen_t(optlen) |
| 119 | )) |
| 120 | } |
| 121 | #[cfg (target_arch = "x86" )] |
| 122 | unsafe { |
| 123 | ret(syscall_readonly!( |
| 124 | __NR_socketcall, |
| 125 | x86_sys(SYS_SETSOCKOPT), |
| 126 | slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ |
| 127 | fd.into(), |
| 128 | c_uint(level), |
| 129 | c_uint(optname), |
| 130 | ptr.into(), |
| 131 | socklen_t(optlen), |
| 132 | ]) |
| 133 | )) |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | #[inline ] |
| 138 | pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> { |
| 139 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_TYPE) |
| 140 | } |
| 141 | |
| 142 | #[inline ] |
| 143 | pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> { |
| 144 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_REUSEADDR, value:from_bool(reuseaddr)) |
| 145 | } |
| 146 | |
| 147 | #[inline ] |
| 148 | pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 149 | getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(op:to_bool) |
| 150 | } |
| 151 | |
| 152 | #[inline ] |
| 153 | pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> { |
| 154 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_BROADCAST, value:from_bool(broadcast)) |
| 155 | } |
| 156 | |
| 157 | #[inline ] |
| 158 | pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 159 | getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(op:to_bool) |
| 160 | } |
| 161 | |
| 162 | #[inline ] |
| 163 | pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> { |
| 164 | // Convert `linger` to seconds, rounding up. |
| 165 | let l_linger: i32 = if let Some(linger: Duration) = linger { |
| 166 | duration_to_secs(duration:linger)? |
| 167 | } else { |
| 168 | 0 |
| 169 | }; |
| 170 | let linger: linger = c::linger { |
| 171 | l_onoff: c::c_int::from(linger.is_some()), |
| 172 | l_linger, |
| 173 | }; |
| 174 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_LINGER, value:linger) |
| 175 | } |
| 176 | |
| 177 | #[inline ] |
| 178 | pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> { |
| 179 | let linger: c::linger = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_LINGER)?; |
| 180 | Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64))) |
| 181 | } |
| 182 | |
| 183 | #[inline ] |
| 184 | pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> { |
| 185 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_PASSCRED, value:from_bool(passcred)) |
| 186 | } |
| 187 | |
| 188 | #[inline ] |
| 189 | pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 190 | getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(op:to_bool) |
| 191 | } |
| 192 | |
| 193 | #[inline ] |
| 194 | pub(crate) fn set_socket_timeout( |
| 195 | fd: BorrowedFd<'_>, |
| 196 | id: Timeout, |
| 197 | timeout: Option<Duration>, |
| 198 | ) -> io::Result<()> { |
| 199 | let time: __kernel_sock_timeval = duration_to_linux_sock_timeval(timeout)?; |
| 200 | let optname: u32 = match id { |
| 201 | Timeout::Recv => c::SO_RCVTIMEO_NEW, |
| 202 | Timeout::Send => c::SO_SNDTIMEO_NEW, |
| 203 | }; |
| 204 | match setsockopt(fd, level:c::SOL_SOCKET, optname, value:time) { |
| 205 | Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { |
| 206 | set_socket_timeout_old(fd, id, timeout) |
| 207 | } |
| 208 | otherwise: Result<(), Errno> => otherwise, |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | /// Same as `set_socket_timeout` but uses `__kernel_old_timeval` instead of |
| 213 | /// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. |
| 214 | fn set_socket_timeout_old( |
| 215 | fd: BorrowedFd<'_>, |
| 216 | id: Timeout, |
| 217 | timeout: Option<Duration>, |
| 218 | ) -> io::Result<()> { |
| 219 | let time: __kernel_old_timeval = duration_to_linux_old_timeval(timeout)?; |
| 220 | let optname: u32 = match id { |
| 221 | Timeout::Recv => c::SO_RCVTIMEO_OLD, |
| 222 | Timeout::Send => c::SO_SNDTIMEO_OLD, |
| 223 | }; |
| 224 | setsockopt(fd, level:c::SOL_SOCKET, optname, value:time) |
| 225 | } |
| 226 | |
| 227 | #[inline ] |
| 228 | pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { |
| 229 | let optname: u32 = match id { |
| 230 | Timeout::Recv => c::SO_RCVTIMEO_NEW, |
| 231 | Timeout::Send => c::SO_SNDTIMEO_NEW, |
| 232 | }; |
| 233 | let time: __kernel_sock_timeval = match getsockopt(fd, level:c::SOL_SOCKET, optname) { |
| 234 | Err(io::Errno::NOPROTOOPT) if c::SO_RCVTIMEO_NEW != c::SO_RCVTIMEO_OLD => { |
| 235 | return get_socket_timeout_old(fd, id) |
| 236 | } |
| 237 | otherwise: Result<__kernel_sock_timeval, …> => otherwise?, |
| 238 | }; |
| 239 | Ok(duration_from_linux_sock_timeval(time)) |
| 240 | } |
| 241 | |
| 242 | /// Same as `get_socket_timeout` but uses `__kernel_old_timeval` instead of |
| 243 | /// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`. |
| 244 | fn get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { |
| 245 | let optname: u32 = match id { |
| 246 | Timeout::Recv => c::SO_RCVTIMEO_OLD, |
| 247 | Timeout::Send => c::SO_SNDTIMEO_OLD, |
| 248 | }; |
| 249 | let time: __kernel_old_timeval = getsockopt(fd, level:c::SOL_SOCKET, optname)?; |
| 250 | Ok(duration_from_linux_old_timeval(time)) |
| 251 | } |
| 252 | |
| 253 | /// Convert a `__linux_sock_timeval` to a Rust `Option<Duration>`. |
| 254 | #[inline ] |
| 255 | fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration> { |
| 256 | if time.tv_sec == 0 && time.tv_usec == 0 { |
| 257 | None |
| 258 | } else { |
| 259 | Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | /// Like `duration_from_linux` but uses Linux's old 32-bit |
| 264 | /// `__kernel_old_timeval`. |
| 265 | fn duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option<Duration> { |
| 266 | if time.tv_sec == 0 && time.tv_usec == 0 { |
| 267 | None |
| 268 | } else { |
| 269 | Some(Duration::from_secs(time.tv_sec as u64) + Duration::from_micros(time.tv_usec as u64)) |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | /// Convert a Rust `Option<Duration>` to a `__kernel_sock_timeval`. |
| 274 | #[inline ] |
| 275 | fn duration_to_linux_sock_timeval(timeout: Option<Duration>) -> io::Result<__kernel_sock_timeval> { |
| 276 | Ok(match timeout { |
| 277 | Some(timeout: Duration) => { |
| 278 | if timeout == Duration::ZERO { |
| 279 | return Err(io::Errno::INVAL); |
| 280 | } |
| 281 | // `subsec_micros` rounds down, so we use `subsec_nanos` and |
| 282 | // manually round up. |
| 283 | let mut timeout: __kernel_sock_timeval = __kernel_sock_timeval { |
| 284 | tv_sec: timeout.as_secs().try_into().unwrap_or(default:i64::MAX), |
| 285 | tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, |
| 286 | }; |
| 287 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
| 288 | timeout.tv_usec = 1; |
| 289 | } |
| 290 | timeout |
| 291 | } |
| 292 | None => __kernel_sock_timeval { |
| 293 | tv_sec: 0, |
| 294 | tv_usec: 0, |
| 295 | }, |
| 296 | }) |
| 297 | } |
| 298 | |
| 299 | /// Like `duration_to_linux` but uses Linux's old 32-bit |
| 300 | /// `__kernel_old_timeval`. |
| 301 | fn duration_to_linux_old_timeval(timeout: Option<Duration>) -> io::Result<__kernel_old_timeval> { |
| 302 | Ok(match timeout { |
| 303 | Some(timeout: Duration) => { |
| 304 | if timeout == Duration::ZERO { |
| 305 | return Err(io::Errno::INVAL); |
| 306 | } |
| 307 | |
| 308 | // `subsec_micros` rounds down, so we use `subsec_nanos` and |
| 309 | // manually round up. |
| 310 | let mut timeout: __kernel_old_timeval = __kernel_old_timeval { |
| 311 | tv_sec: timeout.as_secs().try_into().unwrap_or(default:c::c_long::MAX), |
| 312 | tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, |
| 313 | }; |
| 314 | if timeout.tv_sec == 0 && timeout.tv_usec == 0 { |
| 315 | timeout.tv_usec = 1; |
| 316 | } |
| 317 | timeout |
| 318 | } |
| 319 | None => __kernel_old_timeval { |
| 320 | tv_sec: 0, |
| 321 | tv_usec: 0, |
| 322 | }, |
| 323 | }) |
| 324 | } |
| 325 | |
| 326 | #[inline ] |
| 327 | pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> { |
| 328 | let err: c::c_int = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_ERROR)?; |
| 329 | Ok(if err == 0 { |
| 330 | Ok(()) |
| 331 | } else { |
| 332 | Err(io::Errno::from_raw_os_error(raw:err)) |
| 333 | }) |
| 334 | } |
| 335 | |
| 336 | #[inline ] |
| 337 | pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> { |
| 338 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_KEEPALIVE, value:from_bool(keepalive)) |
| 339 | } |
| 340 | |
| 341 | #[inline ] |
| 342 | pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 343 | getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(op:to_bool) |
| 344 | } |
| 345 | |
| 346 | #[inline ] |
| 347 | pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
| 348 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
| 349 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_RCVBUF, value:size) |
| 350 | } |
| 351 | |
| 352 | #[inline ] |
| 353 | pub(crate) fn set_socket_recv_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
| 354 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
| 355 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_RCVBUFFORCE, value:size) |
| 356 | } |
| 357 | |
| 358 | #[inline ] |
| 359 | pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { |
| 360 | getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize) |
| 361 | } |
| 362 | |
| 363 | #[inline ] |
| 364 | pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { |
| 365 | let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; |
| 366 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_SNDBUF, value:size) |
| 367 | } |
| 368 | |
| 369 | #[inline ] |
| 370 | pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { |
| 371 | getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize) |
| 372 | } |
| 373 | |
| 374 | #[inline ] |
| 375 | pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> { |
| 376 | let domain: c::c_int = getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_DOMAIN)?; |
| 377 | Ok(AddressFamily( |
| 378 | domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?, |
| 379 | )) |
| 380 | } |
| 381 | |
| 382 | #[inline ] |
| 383 | pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 384 | getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(op:to_bool) |
| 385 | } |
| 386 | |
| 387 | #[inline ] |
| 388 | pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 389 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_OOBINLINE, value:from_bool(value)) |
| 390 | } |
| 391 | |
| 392 | #[inline ] |
| 393 | pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 394 | getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(op:to_bool) |
| 395 | } |
| 396 | |
| 397 | #[inline ] |
| 398 | pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 399 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_REUSEPORT, value:from_bool(value)) |
| 400 | } |
| 401 | |
| 402 | #[inline ] |
| 403 | pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 404 | getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(op:to_bool) |
| 405 | } |
| 406 | |
| 407 | #[inline ] |
| 408 | pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> { |
| 409 | getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL) |
| 410 | .map(|raw: u32| RawProtocol::new(raw).map(Protocol::from_raw)) |
| 411 | } |
| 412 | |
| 413 | #[inline ] |
| 414 | pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> { |
| 415 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_COOKIE) |
| 416 | } |
| 417 | |
| 418 | #[inline ] |
| 419 | pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 420 | getsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_INCOMING_CPU) |
| 421 | } |
| 422 | |
| 423 | #[inline ] |
| 424 | pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 425 | setsockopt(fd, level:c::SOL_SOCKET, optname:c::SO_INCOMING_CPU, value) |
| 426 | } |
| 427 | |
| 428 | #[inline ] |
| 429 | pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> { |
| 430 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TTL, value:ttl) |
| 431 | } |
| 432 | |
| 433 | #[inline ] |
| 434 | pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 435 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TTL) |
| 436 | } |
| 437 | |
| 438 | #[inline ] |
| 439 | pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> { |
| 440 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_V6ONLY, value:from_bool(only_v6)) |
| 441 | } |
| 442 | |
| 443 | #[inline ] |
| 444 | pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 445 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(op:to_bool) |
| 446 | } |
| 447 | |
| 448 | #[inline ] |
| 449 | pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { |
| 450 | setsockopt( |
| 451 | fd, |
| 452 | level:c::IPPROTO_IP, |
| 453 | optname:c::IP_MULTICAST_LOOP, |
| 454 | value:from_bool(multicast_loop), |
| 455 | ) |
| 456 | } |
| 457 | |
| 458 | #[inline ] |
| 459 | pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 460 | getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(op:to_bool) |
| 461 | } |
| 462 | |
| 463 | #[inline ] |
| 464 | pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> { |
| 465 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_MULTICAST_TTL, value:multicast_ttl) |
| 466 | } |
| 467 | |
| 468 | #[inline ] |
| 469 | pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 470 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_MULTICAST_TTL) |
| 471 | } |
| 472 | |
| 473 | #[inline ] |
| 474 | pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { |
| 475 | setsockopt( |
| 476 | fd, |
| 477 | level:c::IPPROTO_IPV6, |
| 478 | optname:c::IPV6_MULTICAST_LOOP, |
| 479 | value:from_bool(multicast_loop), |
| 480 | ) |
| 481 | } |
| 482 | |
| 483 | #[inline ] |
| 484 | pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 485 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(op:to_bool) |
| 486 | } |
| 487 | |
| 488 | #[inline ] |
| 489 | pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> { |
| 490 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IPV6_MULTICAST_HOPS, value:multicast_hops) |
| 491 | } |
| 492 | |
| 493 | #[inline ] |
| 494 | pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 495 | getsockopt(fd, level:c::IPPROTO_IP, optname:c::IPV6_MULTICAST_HOPS) |
| 496 | } |
| 497 | |
| 498 | #[inline ] |
| 499 | pub(crate) fn set_ip_add_membership( |
| 500 | fd: BorrowedFd<'_>, |
| 501 | multiaddr: &Ipv4Addr, |
| 502 | interface: &Ipv4Addr, |
| 503 | ) -> io::Result<()> { |
| 504 | let mreq: ip_mreq = to_ip_mreq(multiaddr, interface); |
| 505 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_MEMBERSHIP, value:mreq) |
| 506 | } |
| 507 | |
| 508 | #[inline ] |
| 509 | pub(crate) fn set_ip_add_membership_with_ifindex( |
| 510 | fd: BorrowedFd<'_>, |
| 511 | multiaddr: &Ipv4Addr, |
| 512 | address: &Ipv4Addr, |
| 513 | ifindex: i32, |
| 514 | ) -> io::Result<()> { |
| 515 | let mreqn: ip_mreqn = to_ip_mreqn(multiaddr, address, ifindex); |
| 516 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_MEMBERSHIP, value:mreqn) |
| 517 | } |
| 518 | |
| 519 | #[inline ] |
| 520 | pub(crate) fn set_ip_add_source_membership( |
| 521 | fd: BorrowedFd<'_>, |
| 522 | multiaddr: &Ipv4Addr, |
| 523 | interface: &Ipv4Addr, |
| 524 | sourceaddr: &Ipv4Addr, |
| 525 | ) -> io::Result<()> { |
| 526 | let mreq_source: ip_mreq_source = to_imr_source(multiaddr, interface, sourceaddr); |
| 527 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_ADD_SOURCE_MEMBERSHIP, value:mreq_source) |
| 528 | } |
| 529 | |
| 530 | #[inline ] |
| 531 | pub(crate) fn set_ip_drop_source_membership( |
| 532 | fd: BorrowedFd<'_>, |
| 533 | multiaddr: &Ipv4Addr, |
| 534 | interface: &Ipv4Addr, |
| 535 | sourceaddr: &Ipv4Addr, |
| 536 | ) -> io::Result<()> { |
| 537 | let mreq_source: ip_mreq_source = to_imr_source(multiaddr, interface, sourceaddr); |
| 538 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_SOURCE_MEMBERSHIP, value:mreq_source) |
| 539 | } |
| 540 | |
| 541 | #[inline ] |
| 542 | pub(crate) fn set_ipv6_add_membership( |
| 543 | fd: BorrowedFd<'_>, |
| 544 | multiaddr: &Ipv6Addr, |
| 545 | interface: u32, |
| 546 | ) -> io::Result<()> { |
| 547 | let mreq: ipv6_mreq = to_ipv6mr(multiaddr, interface); |
| 548 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_ADD_MEMBERSHIP, value:mreq) |
| 549 | } |
| 550 | |
| 551 | #[inline ] |
| 552 | pub(crate) fn set_ip_drop_membership( |
| 553 | fd: BorrowedFd<'_>, |
| 554 | multiaddr: &Ipv4Addr, |
| 555 | interface: &Ipv4Addr, |
| 556 | ) -> io::Result<()> { |
| 557 | let mreq: ip_mreq = to_ip_mreq(multiaddr, interface); |
| 558 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_MEMBERSHIP, value:mreq) |
| 559 | } |
| 560 | |
| 561 | #[inline ] |
| 562 | pub(crate) fn set_ip_drop_membership_with_ifindex( |
| 563 | fd: BorrowedFd<'_>, |
| 564 | multiaddr: &Ipv4Addr, |
| 565 | address: &Ipv4Addr, |
| 566 | ifindex: i32, |
| 567 | ) -> io::Result<()> { |
| 568 | let mreqn: ip_mreqn = to_ip_mreqn(multiaddr, address, ifindex); |
| 569 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_DROP_MEMBERSHIP, value:mreqn) |
| 570 | } |
| 571 | |
| 572 | #[inline ] |
| 573 | pub(crate) fn set_ipv6_drop_membership( |
| 574 | fd: BorrowedFd<'_>, |
| 575 | multiaddr: &Ipv6Addr, |
| 576 | interface: u32, |
| 577 | ) -> io::Result<()> { |
| 578 | let mreq: ipv6_mreq = to_ipv6mr(multiaddr, interface); |
| 579 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_DROP_MEMBERSHIP, value:mreq) |
| 580 | } |
| 581 | |
| 582 | #[inline ] |
| 583 | pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> { |
| 584 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8) |
| 585 | } |
| 586 | |
| 587 | #[inline ] |
| 588 | pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> { |
| 589 | let hops: i32 = match hops { |
| 590 | Some(hops: u8) => hops.into(), |
| 591 | None => -1, |
| 592 | }; |
| 593 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_UNICAST_HOPS, value:hops) |
| 594 | } |
| 595 | |
| 596 | #[inline ] |
| 597 | pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> { |
| 598 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TOS, value:i32::from(value)) |
| 599 | } |
| 600 | |
| 601 | #[inline ] |
| 602 | pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> { |
| 603 | let value: i32 = getsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_TOS)?; |
| 604 | Ok(value as u8) |
| 605 | } |
| 606 | |
| 607 | #[inline ] |
| 608 | pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 609 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_RECVTOS, value:from_bool(value)) |
| 610 | } |
| 611 | |
| 612 | #[inline ] |
| 613 | pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 614 | getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(op:to_bool) |
| 615 | } |
| 616 | |
| 617 | #[inline ] |
| 618 | pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 619 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_RECVTCLASS, value:from_bool(value)) |
| 620 | } |
| 621 | |
| 622 | #[inline ] |
| 623 | pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 624 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(op:to_bool) |
| 625 | } |
| 626 | |
| 627 | #[inline ] |
| 628 | pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 629 | setsockopt(fd, level:c::IPPROTO_IP, optname:c::IP_FREEBIND, value:from_bool(value)) |
| 630 | } |
| 631 | |
| 632 | #[inline ] |
| 633 | pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 634 | getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(op:to_bool) |
| 635 | } |
| 636 | |
| 637 | #[inline ] |
| 638 | pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 639 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_FREEBIND, value:from_bool(value)) |
| 640 | } |
| 641 | |
| 642 | #[inline ] |
| 643 | pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 644 | getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(op:to_bool) |
| 645 | } |
| 646 | |
| 647 | #[inline ] |
| 648 | pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> { |
| 649 | let level: u32 = c::IPPROTO_IP; |
| 650 | let optname: u32 = c::SO_ORIGINAL_DST; |
| 651 | let mut value: MaybeUninit = MaybeUninit::<SocketAddrStorage>::uninit(); |
| 652 | let mut optlen: usize = core::mem::size_of_val(&value).try_into().unwrap(); |
| 653 | |
| 654 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
| 655 | |
| 656 | let any: SocketAddrAny = unsafe { SocketAddrAny::read(storage:value.as_ptr(), len:optlen as usize)? }; |
| 657 | match any { |
| 658 | SocketAddrAny::V4(v4: SocketAddrV4) => Ok(v4), |
| 659 | _ => unreachable!(), |
| 660 | } |
| 661 | } |
| 662 | |
| 663 | #[inline ] |
| 664 | pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> { |
| 665 | let level: u32 = c::IPPROTO_IPV6; |
| 666 | let optname: u32 = c::IP6T_SO_ORIGINAL_DST; |
| 667 | let mut value: MaybeUninit = MaybeUninit::<SocketAddrStorage>::uninit(); |
| 668 | let mut optlen: usize = core::mem::size_of_val(&value).try_into().unwrap(); |
| 669 | |
| 670 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
| 671 | |
| 672 | let any: SocketAddrAny = unsafe { SocketAddrAny::read(storage:value.as_ptr(), len:optlen as usize)? }; |
| 673 | match any { |
| 674 | SocketAddrAny::V6(v6: SocketAddrV6) => Ok(v6), |
| 675 | _ => unreachable!(), |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | #[inline ] |
| 680 | pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 681 | setsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_TCLASS, value) |
| 682 | } |
| 683 | |
| 684 | #[inline ] |
| 685 | pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 686 | getsockopt(fd, level:c::IPPROTO_IPV6, optname:c::IPV6_TCLASS) |
| 687 | } |
| 688 | |
| 689 | #[inline ] |
| 690 | pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> { |
| 691 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_NODELAY, value:from_bool(nodelay)) |
| 692 | } |
| 693 | |
| 694 | #[inline ] |
| 695 | pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 696 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(op:to_bool) |
| 697 | } |
| 698 | |
| 699 | #[inline ] |
| 700 | pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { |
| 701 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPCNT, value:count) |
| 702 | } |
| 703 | |
| 704 | #[inline ] |
| 705 | pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 706 | getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPCNT) |
| 707 | } |
| 708 | |
| 709 | #[inline ] |
| 710 | pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { |
| 711 | let secs: c::c_uint = duration_to_secs(duration)?; |
| 712 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPIDLE, value:secs) |
| 713 | } |
| 714 | |
| 715 | #[inline ] |
| 716 | pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> { |
| 717 | let secs: c::c_uint = getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPIDLE)?; |
| 718 | Ok(Duration::from_secs(secs.into())) |
| 719 | } |
| 720 | |
| 721 | #[inline ] |
| 722 | pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { |
| 723 | let secs: c::c_uint = duration_to_secs(duration)?; |
| 724 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPINTVL, value:secs) |
| 725 | } |
| 726 | |
| 727 | #[inline ] |
| 728 | pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> { |
| 729 | let secs: c::c_uint = getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_KEEPINTVL)?; |
| 730 | Ok(Duration::from_secs(secs.into())) |
| 731 | } |
| 732 | |
| 733 | #[inline ] |
| 734 | pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 735 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_USER_TIMEOUT, value) |
| 736 | } |
| 737 | |
| 738 | #[inline ] |
| 739 | pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> { |
| 740 | getsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_USER_TIMEOUT) |
| 741 | } |
| 742 | |
| 743 | #[inline ] |
| 744 | pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 745 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_QUICKACK, value:from_bool(value)) |
| 746 | } |
| 747 | |
| 748 | #[inline ] |
| 749 | pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 750 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(op:to_bool) |
| 751 | } |
| 752 | |
| 753 | #[inline ] |
| 754 | pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> { |
| 755 | let level: u32 = c::IPPROTO_TCP; |
| 756 | let optname: u32 = c::TCP_CONGESTION; |
| 757 | let optlen: u32 = value.len().try_into().unwrap(); |
| 758 | setsockopt_raw(fd, level, optname, value.as_ptr(), optlen) |
| 759 | } |
| 760 | |
| 761 | #[cfg (feature = "alloc" )] |
| 762 | #[inline ] |
| 763 | pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> { |
| 764 | const OPTLEN: c::socklen_t = 16; |
| 765 | |
| 766 | let level: u32 = c::IPPROTO_TCP; |
| 767 | let optname: u32 = c::TCP_CONGESTION; |
| 768 | let mut value: MaybeUninit<[MaybeUninit<…>; _]> = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit(); |
| 769 | let mut optlen: u32 = OPTLEN; |
| 770 | getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; |
| 771 | unsafe { |
| 772 | let value: [MaybeUninit; _] = value.assume_init(); |
| 773 | let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]); |
| 774 | assert!(slice.iter().any(|b| *b == b' \0' )); |
| 775 | Ok( |
| 776 | core&str::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes()) |
| 777 | .unwrap() |
| 778 | .to_owned(), |
| 779 | ) |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | #[inline ] |
| 784 | pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 785 | setsockopt( |
| 786 | fd, |
| 787 | level:c::IPPROTO_TCP, |
| 788 | optname:c::TCP_THIN_LINEAR_TIMEOUTS, |
| 789 | value:from_bool(value), |
| 790 | ) |
| 791 | } |
| 792 | |
| 793 | #[inline ] |
| 794 | pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 795 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(op:to_bool) |
| 796 | } |
| 797 | |
| 798 | #[inline ] |
| 799 | pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { |
| 800 | setsockopt(fd, level:c::IPPROTO_TCP, optname:c::TCP_CORK, value:from_bool(value)) |
| 801 | } |
| 802 | |
| 803 | #[inline ] |
| 804 | pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> { |
| 805 | getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(op:to_bool) |
| 806 | } |
| 807 | |
| 808 | #[inline ] |
| 809 | pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> { |
| 810 | getsockopt(fd, level:c::SOL_SOCKET, optname:linux_raw_sys::net::SO_PEERCRED) |
| 811 | } |
| 812 | |
| 813 | #[cfg (target_os = "linux" )] |
| 814 | #[inline ] |
| 815 | pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { |
| 816 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_REG, value) |
| 817 | } |
| 818 | |
| 819 | #[cfg (target_os = "linux" )] |
| 820 | #[inline ] |
| 821 | pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 822 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_FILL_RING, value) |
| 823 | } |
| 824 | |
| 825 | #[cfg (target_os = "linux" )] |
| 826 | #[inline ] |
| 827 | pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 828 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_UMEM_COMPLETION_RING, value) |
| 829 | } |
| 830 | |
| 831 | #[cfg (target_os = "linux" )] |
| 832 | #[inline ] |
| 833 | pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 834 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_TX_RING, value) |
| 835 | } |
| 836 | |
| 837 | #[cfg (target_os = "linux" )] |
| 838 | #[inline ] |
| 839 | pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { |
| 840 | setsockopt(fd, level:c::SOL_XDP, optname:c::XDP_RX_RING, value) |
| 841 | } |
| 842 | |
| 843 | #[cfg (target_os = "linux" )] |
| 844 | #[inline ] |
| 845 | pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> { |
| 846 | // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the |
| 847 | // supplied pointer, depending on the kernel version. Both structs only |
| 848 | // contain u64 values. By using the larger of both as the parameter, we can |
| 849 | // shuffle the values to the non-v1 version returned by |
| 850 | // `get_xdp_mmap_offsets` while keeping the return type unaffected by the |
| 851 | // kernel version. This works because C will layout all struct members one |
| 852 | // after the other. |
| 853 | |
| 854 | let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap(); |
| 855 | debug_assert!( |
| 856 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
| 857 | "Socket APIs don't ever use `bool` directly" |
| 858 | ); |
| 859 | let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed(); |
| 860 | getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?; |
| 861 | |
| 862 | if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() { |
| 863 | // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly |
| 864 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
| 865 | let xpd_mmap_offsets = unsafe { value.assume_init() }; |
| 866 | Ok(XdpMmapOffsets { |
| 867 | rx: XdpRingOffset { |
| 868 | producer: xpd_mmap_offsets.rx.producer, |
| 869 | consumer: xpd_mmap_offsets.rx.consumer, |
| 870 | desc: xpd_mmap_offsets.rx.desc, |
| 871 | flags: None, |
| 872 | }, |
| 873 | tx: XdpRingOffset { |
| 874 | producer: xpd_mmap_offsets.rx.flags, |
| 875 | consumer: xpd_mmap_offsets.tx.producer, |
| 876 | desc: xpd_mmap_offsets.tx.consumer, |
| 877 | flags: None, |
| 878 | }, |
| 879 | fr: XdpRingOffset { |
| 880 | producer: xpd_mmap_offsets.tx.desc, |
| 881 | consumer: xpd_mmap_offsets.tx.flags, |
| 882 | desc: xpd_mmap_offsets.fr.producer, |
| 883 | flags: None, |
| 884 | }, |
| 885 | cr: XdpRingOffset { |
| 886 | producer: xpd_mmap_offsets.fr.consumer, |
| 887 | consumer: xpd_mmap_offsets.fr.desc, |
| 888 | desc: xpd_mmap_offsets.fr.flags, |
| 889 | flags: None, |
| 890 | }, |
| 891 | }) |
| 892 | } else { |
| 893 | assert_eq!( |
| 894 | optlen as usize, |
| 895 | core::mem::size_of::<xdp_mmap_offsets>(), |
| 896 | "unexpected getsockopt size" |
| 897 | ); |
| 898 | // Safety: All members of xdp_mmap_offsets are u64 and thus are correctly |
| 899 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
| 900 | let xpd_mmap_offsets = unsafe { value.assume_init() }; |
| 901 | Ok(XdpMmapOffsets { |
| 902 | rx: XdpRingOffset { |
| 903 | producer: xpd_mmap_offsets.rx.producer, |
| 904 | consumer: xpd_mmap_offsets.rx.consumer, |
| 905 | desc: xpd_mmap_offsets.rx.desc, |
| 906 | flags: Some(xpd_mmap_offsets.rx.flags), |
| 907 | }, |
| 908 | tx: XdpRingOffset { |
| 909 | producer: xpd_mmap_offsets.tx.producer, |
| 910 | consumer: xpd_mmap_offsets.tx.consumer, |
| 911 | desc: xpd_mmap_offsets.tx.desc, |
| 912 | flags: Some(xpd_mmap_offsets.tx.flags), |
| 913 | }, |
| 914 | fr: XdpRingOffset { |
| 915 | producer: xpd_mmap_offsets.fr.producer, |
| 916 | consumer: xpd_mmap_offsets.fr.consumer, |
| 917 | desc: xpd_mmap_offsets.fr.desc, |
| 918 | flags: Some(xpd_mmap_offsets.fr.flags), |
| 919 | }, |
| 920 | cr: XdpRingOffset { |
| 921 | producer: xpd_mmap_offsets.cr.producer, |
| 922 | consumer: xpd_mmap_offsets.cr.consumer, |
| 923 | desc: xpd_mmap_offsets.cr.desc, |
| 924 | flags: Some(xpd_mmap_offsets.cr.flags), |
| 925 | }, |
| 926 | }) |
| 927 | } |
| 928 | } |
| 929 | |
| 930 | #[cfg (target_os = "linux" )] |
| 931 | #[inline ] |
| 932 | pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> { |
| 933 | let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap(); |
| 934 | debug_assert!( |
| 935 | optlen as usize >= core::mem::size_of::<c::c_int>(), |
| 936 | "Socket APIs don't ever use `bool` directly" |
| 937 | ); |
| 938 | let mut value = MaybeUninit::<xdp_statistics>::zeroed(); |
| 939 | getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?; |
| 940 | |
| 941 | if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() { |
| 942 | // Safety: All members of xdp_statistics are u64 and thus are correctly |
| 943 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
| 944 | let xdp_statistics = unsafe { value.assume_init() }; |
| 945 | Ok(XdpStatistics { |
| 946 | rx_dropped: xdp_statistics.rx_dropped, |
| 947 | rx_invalid_descs: xdp_statistics.rx_dropped, |
| 948 | tx_invalid_descs: xdp_statistics.rx_dropped, |
| 949 | rx_ring_full: None, |
| 950 | rx_fill_ring_empty_descs: None, |
| 951 | tx_ring_empty_descs: None, |
| 952 | }) |
| 953 | } else { |
| 954 | assert_eq!( |
| 955 | optlen as usize, |
| 956 | core::mem::size_of::<xdp_statistics>(), |
| 957 | "unexpected getsockopt size" |
| 958 | ); |
| 959 | // Safety: All members of xdp_statistics are u64 and thus are correctly |
| 960 | // initialized by `MaybeUninit::<xdp_statistics>::zeroed()` |
| 961 | let xdp_statistics = unsafe { value.assume_init() }; |
| 962 | Ok(XdpStatistics { |
| 963 | rx_dropped: xdp_statistics.rx_dropped, |
| 964 | rx_invalid_descs: xdp_statistics.rx_invalid_descs, |
| 965 | tx_invalid_descs: xdp_statistics.tx_invalid_descs, |
| 966 | rx_ring_full: Some(xdp_statistics.rx_ring_full), |
| 967 | rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs), |
| 968 | tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs), |
| 969 | }) |
| 970 | } |
| 971 | } |
| 972 | |
| 973 | #[cfg (target_os = "linux" )] |
| 974 | #[inline ] |
| 975 | pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> { |
| 976 | getsockopt(fd, level:c::SOL_XDP, optname:c::XDP_OPTIONS) |
| 977 | } |
| 978 | |
| 979 | #[inline ] |
| 980 | fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { |
| 981 | c::ip_mreq { |
| 982 | imr_multiaddr: to_imr_addr(multiaddr), |
| 983 | imr_interface: to_imr_addr(interface), |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | #[inline ] |
| 988 | fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn { |
| 989 | c::ip_mreqn { |
| 990 | imr_multiaddr: to_imr_addr(multiaddr), |
| 991 | imr_address: to_imr_addr(address), |
| 992 | imr_ifindex: ifindex, |
| 993 | } |
| 994 | } |
| 995 | |
| 996 | #[inline ] |
| 997 | fn to_imr_source( |
| 998 | multiaddr: &Ipv4Addr, |
| 999 | interface: &Ipv4Addr, |
| 1000 | sourceaddr: &Ipv4Addr, |
| 1001 | ) -> c::ip_mreq_source { |
| 1002 | c::ip_mreq_source { |
| 1003 | imr_multiaddr: to_imr_addr(multiaddr).s_addr, |
| 1004 | imr_interface: to_imr_addr(interface).s_addr, |
| 1005 | imr_sourceaddr: to_imr_addr(sourceaddr).s_addr, |
| 1006 | } |
| 1007 | } |
| 1008 | |
| 1009 | #[inline ] |
| 1010 | fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { |
| 1011 | c::in_addr { |
| 1012 | s_addr: u32::from_ne_bytes(addr.octets()), |
| 1013 | } |
| 1014 | } |
| 1015 | |
| 1016 | #[inline ] |
| 1017 | fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq { |
| 1018 | c::ipv6_mreq { |
| 1019 | ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr), |
| 1020 | ipv6mr_ifindex: to_ipv6mr_interface(interface), |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | #[inline ] |
| 1025 | fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { |
| 1026 | c::in6_addr { |
| 1027 | in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 { |
| 1028 | u6_addr8: multiaddr.octets(), |
| 1029 | }, |
| 1030 | } |
| 1031 | } |
| 1032 | |
| 1033 | #[inline ] |
| 1034 | fn to_ipv6mr_interface(interface: u32) -> c::c_int { |
| 1035 | interface as c::c_int |
| 1036 | } |
| 1037 | |
| 1038 | #[inline ] |
| 1039 | fn from_bool(value: bool) -> c::c_uint { |
| 1040 | c::c_uint::from(value) |
| 1041 | } |
| 1042 | |
| 1043 | #[inline ] |
| 1044 | fn to_bool(value: c::c_uint) -> bool { |
| 1045 | value != 0 |
| 1046 | } |
| 1047 | |
| 1048 | /// Convert to seconds, rounding up if necessary. |
| 1049 | #[inline ] |
| 1050 | fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> { |
| 1051 | let mut secs: u64 = duration.as_secs(); |
| 1052 | if duration.subsec_nanos() != 0 { |
| 1053 | secs = secs.checked_add(1).ok_or(err:io::Errno::INVAL)?; |
| 1054 | } |
| 1055 | T::try_from(secs).map_err(|_e: >::Error| io::Errno::INVAL) |
| 1056 | } |
| 1057 | |