diff --git a/kernel/src/net/socket/ip/datagram/mod.rs b/kernel/src/net/socket/ip/datagram/mod.rs index 489ca6c0e..643c8e3e1 100644 --- a/kernel/src/net/socket/ip/datagram/mod.rs +++ b/kernel/src/net/socket/ip/datagram/mod.rs @@ -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 { + fn socket_type(&self) -> SockType { + SockType::SOCK_DGRAM + } + fn is_listening(&self) -> bool { false } diff --git a/kernel/src/net/socket/ip/stream/mod.rs b/kernel/src/net/socket/ip/stream/mod.rs index 92b1942f3..8ad44dc76 100644 --- a/kernel/src/net/socket/ip/stream/mod.rs +++ b/kernel/src/net/socket/ip/stream/mod.rs @@ -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(_)) } diff --git a/kernel/src/net/socket/netlink/common/mod.rs b/kernel/src/net/socket/netlink/common/mod.rs index 2c4fe9284..2815703d7 100644 --- a/kernel/src/net/socket/netlink/common/mod.rs +++ b/kernel/src/net/socket/netlink/common/mod.rs @@ -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 { inner: RwMutex, BoundNetlink>>, options: RwLock, + socket_type: SockType, is_nonblocking: AtomicBool, pollee: Pollee, @@ -57,11 +58,14 @@ impl NetlinkSocket

where BoundNetlink: Bound, { - pub fn new(is_nonblocking: bool) -> Arc { + pub fn new(is_nonblocking: bool, socket_type: SockType) -> Arc { + 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 GetSocketLevelOption - for Inner, BoundNetlink> + for ( + &Inner, BoundNetlink>, + SockType, + ) { + fn socket_type(&self) -> SockType { + self.1 + } + fn is_listening(&self) -> bool { false } diff --git a/kernel/src/net/socket/netlink/kobject_uevent/message/test.rs b/kernel/src/net/socket/netlink/kobject_uevent/message/test.rs index d4fad6c00..f1666b0fe 100644 --- a/kernel/src/net/socket/netlink/kobject_uevent/message/test.rs +++ b/kernel/src/net/socket/netlink/kobject_uevent/message/test.rs @@ -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(); diff --git a/kernel/src/net/socket/options/mod.rs b/kernel/src/net/socket/options/mod.rs index e72bd952e..169d7f578 100644 --- a/kernel/src/net/socket/options/mod.rs +++ b/kernel/src/net/socket/options/mod.rs @@ -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); pub struct Broadcast(bool); pub struct SendBuf(u32); diff --git a/kernel/src/net/socket/unix/datagram/socket.rs b/kernel/src/net/socket/unix/datagram/socket.rs index 4240277e8..ff404c2e8 100644 --- a/kernel/src/net/socket/unix/datagram/socket.rs +++ b/kernel/src/net/socket/unix/datagram/socket.rs @@ -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 } diff --git a/kernel/src/net/socket/unix/stream/listener.rs b/kernel/src/net/socket/unix/stream/listener.rs index 982f8ab0f..e31635c28 100644 --- a/kernel/src/net/socket/unix/stream/listener.rs +++ b/kernel/src/net/socket/unix/stream/listener.rs @@ -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, SocketAddr)> { + pub(super) fn try_accept( + &self, + socket_type: SockType, + ) -> Result<(Arc, 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)) } diff --git a/kernel/src/net/socket/unix/stream/socket.rs b/kernel/src/net/socket/unix/stream/socket.rs index 96230c76c..ac04db2dd 100644 --- a/kernel/src/net/socket/unix/stream/socket.rs +++ b/kernel/src/net/socket/unix/stream/socket.rs @@ -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::new_init(Init::new(), is_nonblocking, is_seqpacket) + pub fn new(is_nonblocking: bool, socket_type: SockType) -> Arc { + 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 { + fn new_init(init: Init, is_nonblocking: bool, socket_type: SockType) -> Arc { 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, Arc) { + pub fn new_pair(is_nonblocking: bool, socket_type: SockType) -> (Arc, Arc) { + debug_assert!( + socket_type == SockType::SOCK_STREAM || socket_type == SockType::SOCK_SEQPACKET + ); + let cred = SocketCred::::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 { 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 { 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)> { 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, 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(_)) } } diff --git a/kernel/src/net/socket/util/options.rs b/kernel/src/net/socket/util/options.rs index 0790be671..3ff2f0436 100644 --- a/kernel/src/net/socket/util/options.rs +++ b/kernel/src/net/socket/util/options.rs @@ -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; } diff --git a/kernel/src/syscall/socket.rs b/kernel/src/syscall/socket.rs index f7cb56a2f..4988a1c49 100644 --- a/kernel/src/syscall/socket.rs +++ b/kernel/src/syscall/socket.rs @@ -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 + UnixStreamSocket::new(is_nonblocking, sock_type) as Arc } (CSocketAddrFamily::AF_UNIX, SockType::SOCK_SEQPACKET) => { - UnixStreamSocket::new(is_nonblocking, true) as Arc + UnixStreamSocket::new(is_nonblocking, sock_type) as Arc } (CSocketAddrFamily::AF_UNIX, SockType::SOCK_RAW | SockType::SOCK_DGRAM) => { UnixDatagramSocket::new(is_nonblocking) as Arc @@ -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 + NetlinkRouteSocket::new(is_nonblocking, sock_type) as Arc } Ok(StandardNetlinkProtocol::KOBJECT_UEVENT) => { - NetlinkUeventSocket::new(is_nonblocking) as Arc + NetlinkUeventSocket::new(is_nonblocking, sock_type) as Arc } Ok(_) => { return_errno_with_message!( diff --git a/kernel/src/syscall/socketpair.rs b/kernel/src/syscall/socketpair.rs index 42a739714..247424262 100644 --- a/kernel/src/syscall/socketpair.rs +++ b/kernel/src/syscall/socketpair.rs @@ -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)) diff --git a/kernel/src/util/net/options/socket.rs b/kernel/src/util/net/options/socket.rs index e771ce25c..d1ec00621 100644 --- a/kernel/src/util/net/options/socket.rs +++ b/kernel/src/util/net/options/socket.rs @@ -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> { 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> { } 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); diff --git a/kernel/src/util/net/options/utils.rs b/kernel/src/util/net/options/utils.rs index d67e6d60f..31212a433 100644 --- a/kernel/src/util/net/options/utils.rs +++ b/kernel/src/util/net/options/utils.rs @@ -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 { + (*self as i32).write_to_user(addr, max_len) + } +} + #[repr(C)] #[derive(Clone, Copy, Debug, Pod)] struct CLinger { diff --git a/kernel/src/util/net/socket.rs b/kernel/src/util/net/socket.rs index 1f90fb8a7..98caec3a4 100644 --- a/kernel/src/util/net/socket.rs +++ b/kernel/src/util/net/socket.rs @@ -48,7 +48,7 @@ pub enum Protocol { /// From . #[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, diff --git a/test/initramfs/src/regression/network/rtnl_err.c b/test/initramfs/src/regression/network/rtnl_err.c index f74fdbdd3..18baeb187 100644 --- a/test/initramfs/src/regression/network/rtnl_err.c +++ b/test/initramfs/src/regression/network/rtnl_err.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 #include +#include #include #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' }; diff --git a/test/initramfs/src/regression/network/sockoption.c b/test/initramfs/src/regression/network/sockoption.c index 5557e1969..23e1b9b3c 100644 --- a/test/initramfs/src/regression/network/sockoption.c +++ b/test/initramfs/src/regression/network/sockoption.c @@ -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; diff --git a/test/initramfs/src/regression/network/sockoption_unix.c b/test/initramfs/src/regression/network/sockoption_unix.c index 8cffa9a0d..ffc4d5e95 100644 --- a/test/initramfs/src/regression/network/sockoption_unix.c +++ b/test/initramfs/src/regression/network/sockoption_unix.c @@ -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;