diff --git a/src/backend/libc/net/msghdr.rs b/src/backend/libc/net/msghdr.rs index d212c65a6..1b997323f 100644 --- a/src/backend/libc/net/msghdr.rs +++ b/src/backend/libc/net/msghdr.rs @@ -6,12 +6,12 @@ use crate::backend::c; use crate::backend::conv::{msg_control_len, msg_iov_len}; #[cfg(target_os = "linux")] -use crate::backend::net::write_sockaddr::encode_sockaddr_xdp; +use crate::backend::net::write_sockaddr::{encode_sockaddr_nl, encode_sockaddr_xdp}; use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; use crate::io::{self, IoSlice, IoSliceMut}; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6}; use crate::utils::as_ptr; @@ -78,7 +78,7 @@ pub(crate) fn with_v4_msghdr<R>( f({ let mut h = zero_msghdr(); h.msg_name = as_ptr(&encoded) as _; - h.msg_namelen = size_of::<SocketAddrV4>() as _; + h.msg_namelen = size_of::<c::sockaddr_in>() as _; h.msg_iov = iov.as_ptr() as _; h.msg_iovlen = msg_iov_len(iov.len()); h.msg_control = control.as_control_ptr().cast(); @@ -99,7 +99,7 @@ pub(crate) fn with_v6_msghdr<R>( f({ let mut h = zero_msghdr(); h.msg_name = as_ptr(&encoded) as _; - h.msg_namelen = size_of::<SocketAddrV6>() as _; + h.msg_namelen = size_of::<c::sockaddr_in6>() as _; h.msg_iov = iov.as_ptr() as _; h.msg_iovlen = msg_iov_len(iov.len()); h.msg_control = control.as_control_ptr().cast(); @@ -128,7 +128,7 @@ pub(crate) fn with_unix_msghdr<R>( }) } -/// Create a message header intended to send with an IPv6 address. +/// Create a message header intended to send with an XDP address. #[cfg(target_os = "linux")] pub(crate) fn with_xdp_msghdr<R>( addr: &SocketAddrXdp, @@ -141,7 +141,29 @@ pub(crate) fn with_xdp_msghdr<R>( f({ let mut h = zero_msghdr(); h.msg_name = as_ptr(&encoded) as _; - h.msg_namelen = size_of::<SocketAddrXdp>() as _; + h.msg_namelen = size_of::<c::sockaddr_xdp>() as _; + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }) +} + +/// Create a message header intended to send with an Netlink address. +#[cfg(target_os = "linux")] +pub(crate) fn with_nl_msghdr<R>( + addr: &SocketAddrNl, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + let encoded = encode_sockaddr_nl(addr); + + f({ + let mut h = zero_msghdr(); + h.msg_name = as_ptr(&encoded) as _; + h.msg_namelen = size_of::<c::sockaddr_nl>() as _; h.msg_iov = iov.as_ptr() as _; h.msg_iovlen = msg_iov_len(iov.len()); h.msg_control = control.as_control_ptr().cast(); diff --git a/src/backend/libc/net/read_sockaddr.rs b/src/backend/libc/net/read_sockaddr.rs index d2c9b69e6..4f4c44e4d 100644 --- a/src/backend/libc/net/read_sockaddr.rs +++ b/src/backend/libc/net/read_sockaddr.rs @@ -9,7 +9,10 @@ use crate::backend::c; use crate::ffi::CStr; use crate::io; #[cfg(target_os = "linux")] -use crate::net::xdp::{SockaddrXdpFlags, SocketAddrXdp}; +use crate::net::{ + netlink::SocketAddrNl, + xdp::{SockaddrXdpFlags, SocketAddrXdp}, +}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; use core::mem::size_of; @@ -332,6 +335,12 @@ unsafe fn inner_read_sockaddr_os( u32::from_be(decode.sxdp_shared_umem_fd), )) } + #[cfg(target_os = "linux")] + c::AF_NETLINK => { + assert!(len >= size_of::<c::sockaddr_nl>()); + let decode = &*storage.cast::<c::sockaddr_nl>(); + SocketAddrAny::Nl(SocketAddrNl::new(decode.nl_pid, decode.nl_groups)) + } other => unimplemented!("{:?}", other), } } diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 3fdb7766b..a65a55861 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -3,15 +3,15 @@ #[cfg(unix)] use super::addr::SocketAddrUnix; #[cfg(target_os = "linux")] -use super::msghdr::with_xdp_msghdr; +use super::msghdr::{with_nl_msghdr, with_xdp_msghdr}; #[cfg(target_os = "linux")] -use super::write_sockaddr::encode_sockaddr_xdp; +use super::write_sockaddr::{encode_sockaddr_nl, encode_sockaddr_xdp}; use crate::backend::c; use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; use crate::utils::as_ptr; use core::mem::{size_of, MaybeUninit}; @@ -170,6 +170,25 @@ pub(crate) fn sendto_xdp( } } +#[cfg(target_os = "linux")] +pub(crate) fn sendto_nl( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrNl, +) -> io::Result<usize> { + unsafe { + ret_send_recv(c::sendto( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + as_ptr(&encode_sockaddr_nl(addr)).cast::<c::sockaddr>(), + size_of::<c::sockaddr_nl>() as _, + )) + } +} + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] pub(crate) fn socket( domain: AddressFamily, @@ -253,6 +272,17 @@ pub(crate) fn bind_xdp(sockfd: BorrowedFd<'_>, addr: &SocketAddrXdp) -> io::Resu } } +#[cfg(target_os = "linux")] +pub(crate) fn bind_nl(sockfd: BorrowedFd<'_>, addr: &SocketAddrNl) -> io::Result<()> { + unsafe { + ret(c::bind( + borrowed_fd(sockfd), + as_ptr(&encode_sockaddr_nl(addr)).cast(), + size_of::<c::sockaddr_nl>() as c::socklen_t, + )) + } +} + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { unsafe { @@ -455,6 +485,23 @@ pub(crate) fn sendmsg_xdp( }) } +#[cfg(target_os = "linux")] +pub(crate) fn sendmsg_nl( + sockfd: BorrowedFd<'_>, + addr: &SocketAddrNl, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + with_nl_msghdr(addr, iov, control, |msghdr| unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + }) +} + #[cfg(not(any( apple, windows, diff --git a/src/backend/libc/net/write_sockaddr.rs b/src/backend/libc/net/write_sockaddr.rs index 29e69e141..e08f2b0df 100644 --- a/src/backend/libc/net/write_sockaddr.rs +++ b/src/backend/libc/net/write_sockaddr.rs @@ -7,7 +7,7 @@ use super::addr::SocketAddrUnix; use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new}; use crate::backend::c; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; use core::mem::size_of; @@ -22,6 +22,8 @@ pub(crate) unsafe fn write_sockaddr( SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage), #[cfg(target_os = "linux")] SocketAddrAny::Xdp(xdp) => write_sockaddr_xdp(xdp, storage), + #[cfg(target_os = "linux")] + SocketAddrAny::Nl(nl) => write_sockaddr_nl(nl, storage), } } @@ -126,3 +128,19 @@ unsafe fn write_sockaddr_xdp(xdp: &SocketAddrXdp, storage: *mut SocketAddrStorag core::ptr::write(storage.cast(), encoded); size_of::<c::sockaddr_xdp>() } + +#[cfg(target_os = "linux")] +pub(crate) fn encode_sockaddr_nl(nl: &SocketAddrNl) -> c::sockaddr_nl { + let mut addr: c::sockaddr_nl = unsafe { std::mem::zeroed() }; + addr.nl_family = c::AF_NETLINK as _; + addr.nl_pid = nl.pid(); + addr.nl_groups = nl.groups(); + addr +} + +#[cfg(target_os = "linux")] +unsafe fn write_sockaddr_nl(nl: &SocketAddrNl, storage: *mut SocketAddrStorage) -> usize { + let encoded = encode_sockaddr_nl(nl); + core::ptr::write(storage.cast(), encoded); + size_of::<c::sockaddr_nl>() +} diff --git a/src/backend/linux_raw/net/msghdr.rs b/src/backend/linux_raw/net/msghdr.rs index 3ccce04c9..accc38252 100644 --- a/src/backend/linux_raw/net/msghdr.rs +++ b/src/backend/linux_raw/net/msghdr.rs @@ -7,12 +7,12 @@ use crate::backend::c; #[cfg(target_os = "linux")] -use crate::backend::net::write_sockaddr::encode_sockaddr_xdp; +use crate::backend::net::write_sockaddr::{encode_sockaddr_nl, encode_sockaddr_xdp}; use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; use crate::io::{self, IoSlice, IoSliceMut}; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6}; use crate::utils::as_ptr; @@ -89,7 +89,7 @@ pub(crate) fn with_v4_msghdr<R>( f(c::msghdr { msg_name: as_ptr(&encoded) as _, - msg_namelen: size_of::<SocketAddrV4>() as _, + msg_namelen: size_of::<c::sockaddr_in>() as _, msg_iov: iov.as_ptr() as _, msg_iovlen: msg_iov_len(iov.len()), msg_control: control.as_control_ptr().cast(), @@ -109,7 +109,7 @@ pub(crate) fn with_v6_msghdr<R>( f(c::msghdr { msg_name: as_ptr(&encoded) as _, - msg_namelen: size_of::<SocketAddrV6>() as _, + msg_namelen: size_of::<c::sockaddr_in6>() as _, msg_iov: iov.as_ptr() as _, msg_iovlen: msg_iov_len(iov.len()), msg_control: control.as_control_ptr().cast(), @@ -148,7 +148,28 @@ pub(crate) fn with_xdp_msghdr<R>( f(c::msghdr { msg_name: as_ptr(&encoded) as _, - msg_namelen: size_of::<SocketAddrXdp>() as _, + msg_namelen: size_of::<c::sockaddr_xdp>() as _, + msg_iov: iov.as_ptr() as _, + msg_iovlen: msg_iov_len(iov.len()), + msg_control: control.as_control_ptr().cast(), + msg_controllen: msg_control_len(control.control_len()), + msg_flags: 0, + }) +} + +/// Create a message header intended to send with an Netlink address. +#[cfg(target_os = "linux")] +pub(crate) fn with_nl_msghdr<R>( + addr: &SocketAddrNl, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + let encoded = encode_sockaddr_nl(addr); + + f(c::msghdr { + msg_name: as_ptr(&encoded) as _, + msg_namelen: size_of::<c::sockaddr_xdp>() as _, msg_iov: iov.as_ptr() as _, msg_iovlen: msg_iov_len(iov.len()), msg_control: control.as_control_ptr().cast(), diff --git a/src/backend/linux_raw/net/read_sockaddr.rs b/src/backend/linux_raw/net/read_sockaddr.rs index 23e1d641d..119660da4 100644 --- a/src/backend/linux_raw/net/read_sockaddr.rs +++ b/src/backend/linux_raw/net/read_sockaddr.rs @@ -5,6 +5,8 @@ use crate::backend::c; use crate::io; #[cfg(target_os = "linux")] +use crate::net::netlink::SocketAddrNl; +#[cfg(target_os = "linux")] use crate::net::xdp::{SockaddrXdpFlags, SocketAddrXdp}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrUnix, SocketAddrV4, SocketAddrV6}; use core::mem::size_of; @@ -216,6 +218,12 @@ pub(crate) unsafe fn read_sockaddr_os(storage: *const c::sockaddr, len: usize) - u32::from_be(decode.sxdp_shared_umem_fd), )) } + #[cfg(target_os = "linux")] + c::AF_NETLINK => { + assert!(len >= size_of::<c::sockaddr_nl>()); + let decode = &*storage.cast::<c::sockaddr_nl>(); + SocketAddrAny::Nl(SocketAddrNl::new(decode.nl_pid, decode.nl_groups)) + } other => unimplemented!("{:?}", other), } } diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index 4d4427a40..bd6805810 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -6,14 +6,14 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] #[cfg(target_os = "linux")] -use super::msghdr::with_xdp_msghdr; +use super::msghdr::{with_nl_msghdr, with_xdp_msghdr}; use super::msghdr::{ with_noaddr_msghdr, with_recv_msghdr, with_unix_msghdr, with_v4_msghdr, with_v6_msghdr, }; use super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}; use super::send_recv::{RecvFlags, SendFlags}; #[cfg(target_os = "linux")] -use super::write_sockaddr::encode_sockaddr_xdp; +use super::write_sockaddr::{encode_sockaddr_nl, encode_sockaddr_xdp}; use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; use crate::backend::c; use crate::backend::conv::{ @@ -23,7 +23,7 @@ use crate::backend::conv::{ use crate::fd::{BorrowedFd, OwnedFd}; use crate::io::{self, IoSlice, IoSliceMut}; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{ AddressFamily, Protocol, RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer, Shutdown, SocketAddrAny, SocketAddrUnix, SocketAddrV4, SocketAddrV6, SocketFlags, SocketType, @@ -439,6 +439,37 @@ pub(crate) fn sendmsg_xdp( }) } +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn sendmsg_nl( + sockfd: BorrowedFd<'_>, + addr: &SocketAddrNl, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + with_nl_msghdr(addr, iov, control, |msghdr| { + #[cfg(not(target_arch = "x86"))] + let result = + unsafe { ret_usize(syscall!(__NR_sendmsg, sockfd, by_ref(&msghdr), msg_flags)) }; + + #[cfg(target_arch = "x86")] + let result = unsafe { + ret_usize(syscall!( + __NR_socketcall, + x86_sys(SYS_SENDMSG), + slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ + sockfd.into(), + by_ref(&msghdr), + msg_flags.into() + ]) + )) + }; + + result + }) +} + #[inline] pub(crate) fn shutdown(fd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] @@ -660,6 +691,44 @@ pub(crate) fn sendto_xdp( } } +#[inline] +pub(crate) fn sendto_nl( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrNl, +) -> io::Result<usize> { + let (buf_addr, buf_len) = slice(buf); + + #[cfg(not(target_arch = "x86"))] + unsafe { + ret_usize(syscall_readonly!( + __NR_sendto, + fd, + buf_addr, + buf_len, + flags, + by_ref(&encode_sockaddr_nl(addr)), + size_of::<c::sockaddr_nl, _>() + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret_usize(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_SENDTO), + slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ + fd.into(), + buf_addr, + buf_len, + flags.into(), + by_ref(&encode_sockaddr_nl(addr)), + size_of::<c::sockaddr_nl, _>(), + ]) + )) + } +} + #[inline] pub(crate) unsafe fn recv( fd: BorrowedFd<'_>, @@ -931,6 +1000,32 @@ pub(crate) fn bind_xdp(fd: BorrowedFd<'_>, addr: &SocketAddrXdp) -> io::Result<( } } +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn bind_nl(fd: BorrowedFd<'_>, addr: &SocketAddrNl) -> io::Result<()> { + #[cfg(not(target_arch = "x86"))] + unsafe { + ret(syscall_readonly!( + __NR_bind, + fd, + by_ref(&encode_sockaddr_nl(addr)), + size_of::<c::sockaddr_nl, _>() + )) + } + #[cfg(target_arch = "x86")] + unsafe { + ret(syscall_readonly!( + __NR_socketcall, + x86_sys(SYS_BIND), + slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[ + fd.into(), + by_ref(&encode_sockaddr_nl(addr)), + size_of::<c::sockaddr_nl, _>(), + ]) + )) + } +} + #[inline] pub(crate) fn connect_v4(fd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { #[cfg(not(target_arch = "x86"))] diff --git a/src/backend/linux_raw/net/write_sockaddr.rs b/src/backend/linux_raw/net/write_sockaddr.rs index fb6e51edb..d3a3a45cd 100644 --- a/src/backend/linux_raw/net/write_sockaddr.rs +++ b/src/backend/linux_raw/net/write_sockaddr.rs @@ -4,7 +4,7 @@ use crate::backend::c; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrUnix, SocketAddrV4, SocketAddrV6}; use core::mem::size_of; @@ -18,6 +18,8 @@ pub(crate) unsafe fn write_sockaddr( SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage), #[cfg(target_os = "linux")] SocketAddrAny::Xdp(xdp) => write_sockaddr_xdp(xdp, storage), + #[cfg(target_os = "linux")] + SocketAddrAny::Nl(nl) => write_sockaddr_nl(nl, storage), } } @@ -80,3 +82,20 @@ unsafe fn write_sockaddr_xdp(xdp: &SocketAddrXdp, storage: *mut SocketAddrStorag core::ptr::write(storage.cast(), encoded); size_of::<c::sockaddr_xdp>() } + +#[cfg(target_os = "linux")] +pub(crate) fn encode_sockaddr_nl(nl: &SocketAddrNl) -> c::sockaddr_nl { + c::sockaddr_nl { + nl_family: c::AF_NETLINK as _, + nl_pid: nl.pid(), + nl_groups: nl.groups(), + nl_pad: 0, + } +} + +#[cfg(target_os = "linux")] +unsafe fn write_sockaddr_nl(nl: &SocketAddrNl, storage: *mut SocketAddrStorage) -> usize { + let encoded = encode_sockaddr_nl(nl); + core::ptr::write(storage.cast(), encoded); + size_of::<c::sockaddr_nl>() +} diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index 1ae4fdb39..69e3d3b0f 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -3,10 +3,10 @@ #![allow(unsafe_code)] use crate::buffer::split_init; -#[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; #[cfg(unix)] use crate::net::SocketAddrUnix; +#[cfg(target_os = "linux")] +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{SocketAddr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; use crate::{backend, io}; use backend::fd::{AsFd, BorrowedFd}; @@ -265,6 +265,8 @@ fn _sendto_any( SocketAddrAny::Unix(unix) => backend::net::syscalls::sendto_unix(fd, buf, flags, unix), #[cfg(target_os = "linux")] SocketAddrAny::Xdp(xdp) => backend::net::syscalls::sendto_xdp(fd, buf, flags, xdp), + #[cfg(target_os = "linux")] + SocketAddrAny::Nl(nl) => backend::net::syscalls::sendto_nl(fd, buf, flags, nl), } } @@ -401,3 +403,22 @@ pub fn sendto_xdp<Fd: AsFd>( ) -> io::Result<usize> { backend::net::syscalls::sendto_xdp(fd.as_fd(), buf, flags, addr) } + +/// `sendto(fd, buf, flags, addr, sizeof(struct sockaddr_nl))`—Writes data +/// to a socket to a specific Netlink address. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/sendto.2.html +#[cfg(target_os = "linux")] +#[inline] +#[doc(alias = "sendto")] +pub fn sendto_nl<Fd: AsFd>( + fd: Fd, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrNl, +) -> io::Result<usize> { + backend::net::syscalls::sendto_nl(fd.as_fd(), buf, flags, addr) +} diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 8b81c8d8f..a20cc8f4d 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -776,6 +776,10 @@ pub fn sendmsg_any( Some(SocketAddrAny::Xdp(addr)) => { backend::net::syscalls::sendmsg_xdp(socket.as_fd(), addr, iov, control, flags) } + #[cfg(target_os = "linux")] + Some(SocketAddrAny::Nl(addr)) => { + backend::net::syscalls::sendmsg_nl(socket.as_fd(), addr, iov, control, flags) + } } } diff --git a/src/net/socket.rs b/src/net/socket.rs index 1a2157776..39d4678be 100644 --- a/src/net/socket.rs +++ b/src/net/socket.rs @@ -4,7 +4,7 @@ use crate::{backend, io}; use backend::fd::{AsFd, BorrowedFd}; #[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; pub use crate::net::{AddressFamily, Protocol, Shutdown, SocketFlags, SocketType}; #[cfg(unix)] pub use backend::net::addr::SocketAddrUnix; @@ -172,6 +172,8 @@ fn _bind_any(sockfd: BorrowedFd<'_>, addr: &SocketAddrAny) -> io::Result<()> { SocketAddrAny::Unix(unix) => backend::net::syscalls::bind_unix(sockfd, unix), #[cfg(target_os = "linux")] SocketAddrAny::Xdp(xdp) => backend::net::syscalls::bind_xdp(sockfd, xdp), + #[cfg(target_os = "linux")] + SocketAddrAny::Nl(nl) => backend::net::syscalls::bind_nl(sockfd, nl), } } @@ -289,6 +291,20 @@ pub fn bind_xdp<Fd: AsFd>(sockfd: Fd, addr: &SocketAddrXdp) -> io::Result<()> { backend::net::syscalls::bind_xdp(sockfd.as_fd(), addr) } +/// `bind(sockfd, addr, sizeof(struct sockaddr_nl))`—Binds a socket to a Netlink +/// address. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/bind.2.html +#[cfg(target_os = "linux")] +#[inline] +#[doc(alias = "bind")] +pub fn bind_nl<Fd: AsFd>(sockfd: Fd, addr: &SocketAddrNl) -> io::Result<()> { + backend::net::syscalls::bind_nl(sockfd.as_fd(), addr) +} + /// `connect(sockfd, addr)`—Initiates a connection to an IP address. /// /// On Windows, a non-blocking socket returns [`Errno::WOULDBLOCK`] if the @@ -370,6 +386,8 @@ fn _connect_any(sockfd: BorrowedFd<'_>, addr: &SocketAddrAny) -> io::Result<()> SocketAddrAny::Unix(unix) => backend::net::syscalls::connect_unix(sockfd, unix), #[cfg(target_os = "linux")] SocketAddrAny::Xdp(_) => Err(io::Errno::OPNOTSUPP), + #[cfg(target_os = "linux")] + SocketAddrAny::Nl(_) => Err(io::Errno::OPNOTSUPP), } } diff --git a/src/net/socket_addr_any.rs b/src/net/socket_addr_any.rs index 3be80a3ad..86e22ddc4 100644 --- a/src/net/socket_addr_any.rs +++ b/src/net/socket_addr_any.rs @@ -9,10 +9,10 @@ //! OS-specific socket address representations in memory. #![allow(unsafe_code)] -#[cfg(target_os = "linux")] -use crate::net::xdp::SocketAddrXdp; #[cfg(unix)] use crate::net::SocketAddrUnix; +#[cfg(target_os = "linux")] +use crate::net::{netlink::SocketAddrNl, xdp::SocketAddrXdp}; use crate::net::{AddressFamily, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::{backend, io}; #[cfg(feature = "std")] @@ -35,6 +35,9 @@ pub enum SocketAddrAny { /// `struct sockaddr_xdp` #[cfg(target_os = "linux")] Xdp(SocketAddrXdp), + /// `struct sockaddr_nl` + #[cfg(target_os = "linux")] + Nl(SocketAddrNl), } impl From<SocketAddr> for SocketAddrAny { @@ -80,6 +83,8 @@ impl SocketAddrAny { Self::Unix(_) => AddressFamily::UNIX, #[cfg(target_os = "linux")] Self::Xdp(_) => AddressFamily::XDP, + #[cfg(target_os = "linux")] + Self::Nl(_) => AddressFamily::NETLINK, } } @@ -117,6 +122,8 @@ impl fmt::Debug for SocketAddrAny { Self::Unix(unix) => unix.fmt(fmt), #[cfg(target_os = "linux")] Self::Xdp(xdp) => xdp.fmt(fmt), + #[cfg(target_os = "linux")] + Self::Nl(nl) => nl.fmt(fmt), } } } diff --git a/src/net/types.rs b/src/net/types.rs index 12e228e10..0450ba687 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -1051,6 +1051,52 @@ pub mod netlink { /// `NETLINK_GET_STRICT_CHK` #[cfg(linux_kernel)] pub const GET_STRICT_CHK: Protocol = Protocol(new_raw_protocol(c::NETLINK_GET_STRICT_CHK as _)); + + /// A Netlink socket address. + /// + /// Used to bind to Netlink socket. + /// + /// Not ABI compatible with `struct sockaddr_nl` + #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] + pub struct SocketAddrNl { + nl_pid: u32, + nl_groups: u32, + } + + impl SocketAddrNl { + /// Construct a new NETLINK address. + #[inline] + pub fn new(pid: u32, groups: u32) -> Self { + Self { + nl_pid: pid, + nl_groups: groups, + } + } + + /// Return port ID. + #[inline] + pub fn pid(&self) -> u32 { + self.nl_pid + } + + /// Set port ID. + #[inline] + pub fn set_pid(&mut self, pid: u32) { + self.nl_pid = pid; + } + + /// Return multicast group mask. + #[inline] + pub fn groups(&self) -> u32 { + self.nl_groups + } + + /// Set multicast group mask. + #[inline] + pub fn set_groups(&mut self, groups: u32) { + self.nl_groups = groups; + } + } } /// `ETH_P_*` constants.