Support SO_TYPE socket option
Report the concrete socket type through getsockopt(SOL_SOCKET, SO_TYPE) for IP, UNIX, and netlink sockets. This lets user space inspect sockets without relying on family-specific behavior.
This commit is contained in:
parent
003568118b
commit
e68b9218d4
|
|
@ -26,7 +26,7 @@ use crate::{
|
|||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
util::{MultiRead, MultiWrite},
|
||||
util::{MultiRead, MultiWrite, net::SockType},
|
||||
};
|
||||
|
||||
mod bound;
|
||||
|
|
@ -298,6 +298,10 @@ impl Socket for DatagramSocket {
|
|||
}
|
||||
|
||||
impl GetSocketLevelOption for Inner<UnboundDatagram, BoundDatagram> {
|
||||
fn socket_type(&self) -> SockType {
|
||||
SockType::SOCK_DGRAM
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ use crate::{
|
|||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
util::{MultiRead, MultiWrite},
|
||||
util::{MultiRead, MultiWrite, net::SockType},
|
||||
};
|
||||
|
||||
mod connected;
|
||||
|
|
@ -913,6 +913,10 @@ impl State {
|
|||
}
|
||||
|
||||
impl GetSocketLevelOption for State {
|
||||
fn socket_type(&self) -> SockType {
|
||||
SockType::SOCK_STREAM
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> bool {
|
||||
matches!(self, Self::Listen(_))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use crate::{
|
|||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
util::{MultiRead, MultiWrite},
|
||||
util::{MultiRead, MultiWrite, net::SockType},
|
||||
};
|
||||
|
||||
mod bound;
|
||||
|
|
@ -34,6 +34,7 @@ mod unbound;
|
|||
pub struct NetlinkSocket<P: SupportedNetlinkProtocol> {
|
||||
inner: RwMutex<Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>>,
|
||||
options: RwLock<OptionSet>,
|
||||
socket_type: SockType,
|
||||
|
||||
is_nonblocking: AtomicBool,
|
||||
pollee: Pollee,
|
||||
|
|
@ -57,11 +58,14 @@ impl<P: SupportedNetlinkProtocol> NetlinkSocket<P>
|
|||
where
|
||||
BoundNetlink<P::Message>: Bound<Endpoint = NetlinkSocketAddr>,
|
||||
{
|
||||
pub fn new(is_nonblocking: bool) -> Arc<Self> {
|
||||
pub fn new(is_nonblocking: bool, socket_type: SockType) -> Arc<Self> {
|
||||
debug_assert!(socket_type == SockType::SOCK_RAW || socket_type == SockType::SOCK_DGRAM);
|
||||
|
||||
let unbound = UnboundNetlink::new();
|
||||
Arc::new(Self {
|
||||
inner: RwMutex::new(Inner::Unbound(unbound)),
|
||||
options: RwLock::new(OptionSet::new()),
|
||||
socket_type,
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
pollee: Pollee::new(),
|
||||
pseudo_path: SockFs::new_path(),
|
||||
|
|
@ -200,7 +204,9 @@ where
|
|||
let options = self.options.read();
|
||||
|
||||
// Deal with socket-level options
|
||||
options.socket.get_option(option, &*inner)
|
||||
options
|
||||
.socket
|
||||
.get_option(option, &(&*inner, self.socket_type))
|
||||
|
||||
// TODO: Deal with netlink-level options
|
||||
}
|
||||
|
|
@ -250,8 +256,15 @@ where
|
|||
}
|
||||
|
||||
impl<P: SupportedNetlinkProtocol> GetSocketLevelOption
|
||||
for Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>
|
||||
for (
|
||||
&Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>,
|
||||
SockType,
|
||||
)
|
||||
{
|
||||
fn socket_type(&self) -> SockType {
|
||||
self.1
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use crate::{
|
|||
util::{SendRecvFlags, SocketAddr},
|
||||
},
|
||||
prelude::*,
|
||||
util::net::SockType,
|
||||
};
|
||||
|
||||
#[ktest]
|
||||
|
|
@ -53,7 +54,7 @@ fn multicast_synthetic_uevent() {
|
|||
crate::net::socket::netlink::init();
|
||||
|
||||
// Creates a new netlink uevent socket and joins the group for kobject uevents.
|
||||
let socket = NetlinkUeventSocket::new(true);
|
||||
let socket = NetlinkUeventSocket::new(true, SockType::SOCK_DGRAM);
|
||||
let socket_addr = SocketAddr::Netlink(NetlinkSocketAddr::new(100, GroupIdSet::new(0x1)));
|
||||
socket.bind(socket_addr).unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use macros::impl_socket_options;
|
||||
|
||||
use super::util::LingerOption;
|
||||
use crate::{net::socket::unix::CUserCred, prelude::*, process::Gid};
|
||||
use crate::{net::socket::unix::CUserCred, prelude::*, process::Gid, util::net::SockType};
|
||||
|
||||
pub(in crate::net) mod macros;
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ pub trait SocketOption: Any + Send + Sync + Debug {
|
|||
|
||||
impl_socket_options!(
|
||||
pub struct ReuseAddr(bool);
|
||||
pub struct SocketType(SockType);
|
||||
pub struct Error(Option<crate::error::Error>);
|
||||
pub struct Broadcast(bool);
|
||||
pub struct SendBuf(u32);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::{
|
|||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
util::{MultiRead, MultiWrite},
|
||||
util::{MultiRead, MultiWrite, net::SockType},
|
||||
};
|
||||
|
||||
pub struct UnixDatagramSocket {
|
||||
|
|
@ -323,6 +323,10 @@ fn do_unix_getsockopt(option: &mut dyn SocketOption, socket: &UnixDatagramSocket
|
|||
}
|
||||
|
||||
impl GetSocketLevelOption for MessageReceiver {
|
||||
fn socket_type(&self) -> SockType {
|
||||
SockType::SOCK_DGRAM
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
},
|
||||
prelude::*,
|
||||
process::signal::Pollee,
|
||||
util::net::SockType,
|
||||
};
|
||||
|
||||
pub(super) struct Listener {
|
||||
|
|
@ -55,13 +56,20 @@ impl Listener {
|
|||
self.backlog.addr()
|
||||
}
|
||||
|
||||
pub(super) fn try_accept(&self, is_seqpacket: bool) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
pub(super) fn try_accept(
|
||||
&self,
|
||||
socket_type: SockType,
|
||||
) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
debug_assert!(
|
||||
socket_type == SockType::SOCK_STREAM || socket_type == SockType::SOCK_SEQPACKET
|
||||
);
|
||||
|
||||
let connected = self.backlog.pop_incoming()?;
|
||||
|
||||
let peer_addr = connected.peer_addr().into();
|
||||
let options = OptionSet::new_accepted(connected.is_pass_cred());
|
||||
|
||||
let socket = UnixStreamSocket::new_connected(connected, options, false, is_seqpacket);
|
||||
let socket = UnixStreamSocket::new_connected(connected, options, false, socket_type);
|
||||
Ok((socket, peer_addr))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
Gid,
|
||||
signal::{PollHandle, Pollable, Pollee},
|
||||
},
|
||||
util::{MultiRead, MultiWrite},
|
||||
util::{MultiRead, MultiWrite, net::SockType},
|
||||
};
|
||||
|
||||
pub struct UnixStreamSocket {
|
||||
|
|
@ -41,7 +41,7 @@ pub struct UnixStreamSocket {
|
|||
pollee: Pollee,
|
||||
is_nonblocking: AtomicBool,
|
||||
|
||||
is_seqpacket: bool,
|
||||
socket_type: SockType,
|
||||
pseudo_path: Path,
|
||||
}
|
||||
|
||||
|
|
@ -163,22 +163,30 @@ impl OptionSet {
|
|||
}
|
||||
|
||||
impl UnixStreamSocket {
|
||||
pub fn new(is_nonblocking: bool, is_seqpacket: bool) -> Arc<Self> {
|
||||
Self::new_init(Init::new(), is_nonblocking, is_seqpacket)
|
||||
pub fn new(is_nonblocking: bool, socket_type: SockType) -> Arc<Self> {
|
||||
debug_assert!(
|
||||
socket_type == SockType::SOCK_STREAM || socket_type == SockType::SOCK_SEQPACKET
|
||||
);
|
||||
|
||||
Self::new_init(Init::new(), is_nonblocking, socket_type)
|
||||
}
|
||||
|
||||
fn new_init(init: Init, is_nonblocking: bool, is_seqpacket: bool) -> Arc<Self> {
|
||||
fn new_init(init: Init, is_nonblocking: bool, socket_type: SockType) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
state: RwMutex::new(Takeable::new(State::Init(init))),
|
||||
options: RwLock::new(OptionSet::new()),
|
||||
pollee: Pollee::new(),
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
is_seqpacket,
|
||||
socket_type,
|
||||
pseudo_path: SockFs::new_path(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_pair(is_nonblocking: bool, is_seqpacket: bool) -> (Arc<Self>, Arc<Self>) {
|
||||
pub fn new_pair(is_nonblocking: bool, socket_type: SockType) -> (Arc<Self>, Arc<Self>) {
|
||||
debug_assert!(
|
||||
socket_type == SockType::SOCK_STREAM || socket_type == SockType::SOCK_SEQPACKET
|
||||
);
|
||||
|
||||
let cred = SocketCred::<ReadDupOp>::new_current();
|
||||
|
||||
let (conn_a, conn_b) = Connected::new_pair(
|
||||
|
|
@ -190,8 +198,8 @@ impl UnixStreamSocket {
|
|||
cred.restrict(),
|
||||
);
|
||||
(
|
||||
Self::new_connected(conn_a, OptionSet::new(), is_nonblocking, is_seqpacket),
|
||||
Self::new_connected(conn_b, OptionSet::new(), is_nonblocking, is_seqpacket),
|
||||
Self::new_connected(conn_a, OptionSet::new(), is_nonblocking, socket_type),
|
||||
Self::new_connected(conn_b, OptionSet::new(), is_nonblocking, socket_type),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -199,7 +207,7 @@ impl UnixStreamSocket {
|
|||
connected: Connected,
|
||||
options: OptionSet,
|
||||
is_nonblocking: bool,
|
||||
is_seqpacket: bool,
|
||||
socket_type: SockType,
|
||||
) -> Arc<Self> {
|
||||
let cloned_pollee = connected.cloned_pollee();
|
||||
Arc::new(Self {
|
||||
|
|
@ -207,7 +215,7 @@ impl UnixStreamSocket {
|
|||
options: RwLock::new(options),
|
||||
pollee: cloned_pollee,
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
is_seqpacket,
|
||||
socket_type,
|
||||
pseudo_path: SockFs::new_path(),
|
||||
})
|
||||
}
|
||||
|
|
@ -219,7 +227,7 @@ impl UnixStreamSocket {
|
|||
_flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Connected(connected) => connected.try_write(buf, aux_data, self.is_seqpacket),
|
||||
State::Connected(connected) => connected.try_write(buf, aux_data, self.is_seqpacket()),
|
||||
State::Init(_) | State::Listen(_) => {
|
||||
return_errno_with_message!(Errno::ENOTCONN, "the socket is not connected")
|
||||
}
|
||||
|
|
@ -232,7 +240,7 @@ impl UnixStreamSocket {
|
|||
flags: SendRecvFlags,
|
||||
) -> Result<(usize, Vec<ControlMessage>)> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Connected(connected) => connected.try_read(buf, self.is_seqpacket, flags),
|
||||
State::Connected(connected) => connected.try_read(buf, self.is_seqpacket(), flags),
|
||||
State::Init(_) | State::Listen(_) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is not connected")
|
||||
}
|
||||
|
|
@ -269,7 +277,7 @@ impl UnixStreamSocket {
|
|||
init,
|
||||
self.pollee.clone(),
|
||||
&self.options.read(),
|
||||
self.is_seqpacket,
|
||||
self.is_seqpacket(),
|
||||
) {
|
||||
Ok(connected) => connected,
|
||||
Err((err, init)) => return (State::Init(init), Err(err)),
|
||||
|
|
@ -281,12 +289,16 @@ impl UnixStreamSocket {
|
|||
|
||||
fn try_accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
match self.state.read().as_ref() {
|
||||
State::Listen(listen) => listen.try_accept(self.is_seqpacket) as _,
|
||||
State::Listen(listen) => listen.try_accept(self.socket_type) as _,
|
||||
State::Init(_) | State::Connected(_) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is not listening")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_seqpacket(&self) -> bool {
|
||||
self.socket_type == SockType::SOCK_SEQPACKET
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) const SHUT_READ_EVENTS: IoEvents =
|
||||
|
|
@ -363,7 +375,7 @@ impl Socket for UnixStreamSocket {
|
|||
}
|
||||
};
|
||||
|
||||
let listener = match init.listen(backlog, self.pollee.clone(), self.is_seqpacket) {
|
||||
let listener = match init.listen(backlog, self.pollee.clone(), self.is_seqpacket()) {
|
||||
Ok(listener) => listener,
|
||||
Err((err, init)) => {
|
||||
return (State::Init(init), Err(err));
|
||||
|
|
@ -421,7 +433,6 @@ impl Socket for UnixStreamSocket {
|
|||
});
|
||||
|
||||
let state = self.state.read();
|
||||
let options = self.options.read();
|
||||
|
||||
// Deal with UNIX-socket-specific socket-level options
|
||||
match do_unix_getsockopt(option, state.as_ref()) {
|
||||
|
|
@ -430,7 +441,11 @@ impl Socket for UnixStreamSocket {
|
|||
}
|
||||
|
||||
// Deal with socket-level options
|
||||
match options.socket.get_option(option, state.as_ref()) {
|
||||
let options = self.options.read();
|
||||
match options
|
||||
.socket
|
||||
.get_option(option, &(state.as_ref(), self.socket_type))
|
||||
{
|
||||
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
||||
res => return res,
|
||||
}
|
||||
|
|
@ -477,7 +492,7 @@ impl Socket for UnixStreamSocket {
|
|||
// According to the Linux man pages, `EISCONN` _may_ be returned when the destination
|
||||
// address is specified for a connection-mode socket. In practice, `sendmsg` on UNIX stream
|
||||
// sockets will fail due to that. We follow the same behavior as the Linux implementation.
|
||||
if !self.is_seqpacket && addr.is_some() {
|
||||
if !self.is_seqpacket() && addr.is_some() {
|
||||
match self.state.read().as_ref() {
|
||||
State::Init(_) | State::Listen(_) => return_errno_with_message!(
|
||||
Errno::EOPNOTSUPP,
|
||||
|
|
@ -538,9 +553,13 @@ fn do_unix_getsockopt(option: &mut dyn SocketOption, state: &State) -> Result<()
|
|||
Ok(())
|
||||
}
|
||||
|
||||
impl GetSocketLevelOption for State {
|
||||
impl GetSocketLevelOption for (&State, SockType) {
|
||||
fn socket_type(&self) -> SockType {
|
||||
self.1
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> bool {
|
||||
matches!(self, Self::Listen(_))
|
||||
matches!(self.0, State::Listen(_))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::{
|
|||
options::{
|
||||
AcceptConn, Broadcast, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority,
|
||||
RecvBuf, RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
|
||||
SocketType,
|
||||
macros::{sock_option_mut, sock_option_ref},
|
||||
},
|
||||
unix::{CUserCred, UNIX_DATAGRAM_DEFAULT_BUF_SIZE, UNIX_STREAM_DEFAULT_BUF_SIZE},
|
||||
|
|
@ -20,6 +21,7 @@ use crate::{
|
|||
prelude::*,
|
||||
process::{UserNamespace, credentials::capabilities::CapSet, posix_thread::AsPosixThread},
|
||||
security::lsm::hooks as lsm_hooks,
|
||||
util::net::SockType,
|
||||
};
|
||||
|
||||
#[derive(Clone, CopyGetters, Debug, Setters)]
|
||||
|
|
@ -113,6 +115,9 @@ impl SocketOptionSet {
|
|||
let reuse_addr = self.reuse_addr();
|
||||
socket_reuse_addr.set(reuse_addr);
|
||||
}
|
||||
socket_type @ SocketType => {
|
||||
socket_type.set(socket.socket_type());
|
||||
}
|
||||
socket_broadcast @ Broadcast => {
|
||||
let broadcast = self.broadcast();
|
||||
socket_broadcast.set(broadcast);
|
||||
|
|
@ -286,6 +291,9 @@ pub const MIN_RECVBUF: u32 = 2304;
|
|||
|
||||
/// A trait used for getting socket level options on actual sockets.
|
||||
pub(in crate::net) trait GetSocketLevelOption {
|
||||
/// Returns the socket type.
|
||||
fn socket_type(&self) -> SockType;
|
||||
|
||||
/// Returns whether the socket is in listening state.
|
||||
fn is_listening(&self) -> bool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||
let is_nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||
let file_like = match (domain, sock_type) {
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM) => {
|
||||
UnixStreamSocket::new(is_nonblocking, false) as Arc<dyn FileLike>
|
||||
UnixStreamSocket::new(is_nonblocking, sock_type) as Arc<dyn FileLike>
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_SEQPACKET) => {
|
||||
UnixStreamSocket::new(is_nonblocking, true) as Arc<dyn FileLike>
|
||||
UnixStreamSocket::new(is_nonblocking, sock_type) as Arc<dyn FileLike>
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_RAW | SockType::SOCK_DGRAM) => {
|
||||
UnixDatagramSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||
|
|
@ -65,10 +65,10 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||
debug!("netlink family = {:?}", netlink_family);
|
||||
match netlink_family {
|
||||
Ok(StandardNetlinkProtocol::ROUTE) => {
|
||||
NetlinkRouteSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||
NetlinkRouteSocket::new(is_nonblocking, sock_type) as Arc<dyn FileLike>
|
||||
}
|
||||
Ok(StandardNetlinkProtocol::KOBJECT_UEVENT) => {
|
||||
NetlinkUeventSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||
NetlinkUeventSocket::new(is_nonblocking, sock_type) as Arc<dyn FileLike>
|
||||
}
|
||||
Ok(_) => {
|
||||
return_errno_with_message!(
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ pub fn sys_socketpair(
|
|||
let nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||
let (socket_a, socket_b) = match (domain, sock_type) {
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM) => {
|
||||
file_pair!(UnixStreamSocket::new_pair(nonblocking, false))
|
||||
file_pair!(UnixStreamSocket::new_pair(nonblocking, sock_type))
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_SEQPACKET) => {
|
||||
file_pair!(UnixStreamSocket::new_pair(nonblocking, true))
|
||||
file_pair!(UnixStreamSocket::new_pair(nonblocking, sock_type))
|
||||
}
|
||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_RAW | SockType::SOCK_DGRAM) => {
|
||||
file_pair!(UnixDatagramSocket::new_pair(nonblocking))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use crate::{
|
|||
net::socket::options::{
|
||||
AcceptConn, Broadcast, Error, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority,
|
||||
RecvBuf, RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
|
||||
SocketType,
|
||||
},
|
||||
prelude::*,
|
||||
process::Gid,
|
||||
|
|
@ -53,6 +54,7 @@ pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
|||
let name = CSocketOptionName::try_from(name).map_err(|_| Errno::ENOPROTOOPT)?;
|
||||
match name {
|
||||
CSocketOptionName::REUSEADDR => Ok(Box::new(ReuseAddr::new())),
|
||||
CSocketOptionName::TYPE => Ok(Box::new(SocketType::new())),
|
||||
CSocketOptionName::ERROR => Ok(Box::new(Error::new())),
|
||||
CSocketOptionName::BROADCAST => Ok(Box::new(Broadcast::new())),
|
||||
CSocketOptionName::SNDBUF => Ok(Box::new(SendBuf::new())),
|
||||
|
|
@ -72,6 +74,7 @@ pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
|||
}
|
||||
|
||||
impl_raw_socket_option!(ReuseAddr);
|
||||
impl_raw_sock_option_get_only!(SocketType);
|
||||
impl_raw_sock_option_get_only!(Error);
|
||||
impl_raw_socket_option!(Broadcast);
|
||||
impl_raw_socket_option!(SendBuf);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
util::LingerOption,
|
||||
},
|
||||
prelude::*,
|
||||
util::net::SockType,
|
||||
};
|
||||
|
||||
/// Create an object by reading its C counterpart from the user space.
|
||||
|
|
@ -200,6 +201,12 @@ impl WriteToUser for CongestionControl {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteToUser for SockType {
|
||||
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||
(*self as i32).write_to_user(addr, max_len)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
struct CLinger {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ pub enum Protocol {
|
|||
/// From <https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/net.h>.
|
||||
#[expect(non_camel_case_types)]
|
||||
#[repr(i32)]
|
||||
#[derive(Clone, Copy, Debug, TryFromInt)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum SockType {
|
||||
/// Stream socket
|
||||
SOCK_STREAM = 1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../common/test.h"
|
||||
|
|
@ -79,6 +80,22 @@ FN_TEST(getpeername)
|
|||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(socket_type)
|
||||
{
|
||||
int type = -1;
|
||||
socklen_t type_len = sizeof(type);
|
||||
|
||||
TEST_RES(getsockopt(sk_unbound, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_DGRAM && type_len == sizeof(type));
|
||||
|
||||
int sk_raw = TEST_SUCC(
|
||||
socket(PF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE));
|
||||
TEST_RES(getsockopt(sk_raw, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_RAW && type_len == sizeof(type));
|
||||
TEST_SUCC(close(sk_raw));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(send)
|
||||
{
|
||||
char buf[1] = { 'z' };
|
||||
|
|
|
|||
|
|
@ -122,6 +122,22 @@ FN_TEST(socket_error)
|
|||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(socket_type)
|
||||
{
|
||||
int type = -1;
|
||||
socklen_t type_len = sizeof(type);
|
||||
|
||||
TEST_ERRNO(setsockopt(sk_unbound, SOL_SOCKET, SO_TYPE, &type, type_len),
|
||||
ENOPROTOOPT);
|
||||
|
||||
TEST_RES(getsockopt(sk_unbound, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_STREAM && type_len == sizeof(type));
|
||||
|
||||
TEST_RES(getsockopt(sk_udp, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_DGRAM && type_len == sizeof(type));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(nagle)
|
||||
{
|
||||
int option = 1;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,34 @@ FN_TEST(acceptconn)
|
|||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(socket_type)
|
||||
{
|
||||
int type = -1;
|
||||
socklen_t type_len = sizeof(type);
|
||||
|
||||
TEST_ERRNO(setsockopt(sk_unbound, SOL_SOCKET, SO_TYPE, &type, type_len),
|
||||
ENOPROTOOPT);
|
||||
|
||||
TEST_RES(getsockopt(sk_unbound, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_STREAM && type_len == sizeof(type));
|
||||
|
||||
int sk_dgram =
|
||||
TEST_SUCC(socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0));
|
||||
TEST_RES(getsockopt(sk_dgram, SOL_SOCKET, SO_TYPE, &type, &type_len),
|
||||
type == SOCK_DGRAM && type_len == sizeof(type));
|
||||
TEST_SUCC(close(sk_dgram));
|
||||
|
||||
int seqpacket_fds[2];
|
||||
TEST_SUCC(socketpair(PF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0,
|
||||
seqpacket_fds));
|
||||
TEST_RES(getsockopt(seqpacket_fds[0], SOL_SOCKET, SO_TYPE, &type,
|
||||
&type_len),
|
||||
type == SOCK_SEQPACKET && type_len == sizeof(type));
|
||||
TEST_SUCC(close(seqpacket_fds[0]));
|
||||
TEST_SUCC(close(seqpacket_fds[1]));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(pass_cred)
|
||||
{
|
||||
int val = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue