Fix option behavior in UNIX/netlink sockets
This commit is contained in:
parent
f522bd72b2
commit
cf772b804e
|
|
@ -9,14 +9,15 @@ use super::{GroupIdSet, NetlinkSocketAddr};
|
|||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::utils::Inode,
|
||||
match_sock_option_ref,
|
||||
match_sock_option_mut, match_sock_option_ref,
|
||||
net::socket::{
|
||||
netlink::{table::SupportedNetlinkProtocol, AddMembership, DropMembership},
|
||||
new_pseudo_inode,
|
||||
options::SocketOption,
|
||||
options::{Error as SocketError, SocketOption},
|
||||
private::SocketPrivate,
|
||||
util::{
|
||||
datagram_common::{select_remote_and_bind, Bound, Inner},
|
||||
options::{GetSocketLevelOption, SetSocketLevelOption, SocketOptionSet},
|
||||
MessageHeader, SendRecvFlags, SocketAddr,
|
||||
},
|
||||
Socket,
|
||||
|
|
@ -31,12 +32,26 @@ mod unbound;
|
|||
|
||||
pub struct NetlinkSocket<P: SupportedNetlinkProtocol> {
|
||||
inner: RwMutex<Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>>,
|
||||
options: RwLock<OptionSet>,
|
||||
|
||||
is_nonblocking: AtomicBool,
|
||||
pollee: Pollee,
|
||||
pseudo_inode: Arc<dyn Inode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct OptionSet {
|
||||
socket: SocketOptionSet,
|
||||
}
|
||||
|
||||
impl OptionSet {
|
||||
pub(self) fn new() -> Self {
|
||||
Self {
|
||||
socket: SocketOptionSet::new_netlink(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: SupportedNetlinkProtocol> NetlinkSocket<P>
|
||||
where
|
||||
BoundNetlink<P::Message>: Bound<Endpoint = NetlinkSocketAddr>,
|
||||
|
|
@ -45,6 +60,7 @@ where
|
|||
let unbound = UnboundNetlink::new();
|
||||
Arc::new(Self {
|
||||
inner: RwMutex::new(Inner::Unbound(unbound)),
|
||||
options: RwLock::new(OptionSet::new()),
|
||||
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||
pollee: Pollee::new(),
|
||||
pseudo_inode: new_pseudo_inode(),
|
||||
|
|
@ -169,17 +185,39 @@ where
|
|||
Ok((received_len, message_header))
|
||||
}
|
||||
|
||||
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
|
||||
match_sock_option_mut!(option, {
|
||||
socket_errors: SocketError => {
|
||||
// TODO: Support socket errors for netlink sockets
|
||||
socket_errors.set(None);
|
||||
return Ok(());
|
||||
},
|
||||
_ => ()
|
||||
});
|
||||
|
||||
let inner = self.inner.read();
|
||||
let options = self.options.read();
|
||||
|
||||
// Deal with socket-level options
|
||||
options.socket.get_option(option, &*inner)
|
||||
|
||||
// TODO: Deal with netlink-level options
|
||||
}
|
||||
|
||||
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
|
||||
match do_set_netlink_option(&self.inner, option) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"We currently ignore set option errors to pass libnl test: {:?}",
|
||||
e
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
let mut inner = self.inner.write();
|
||||
|
||||
// Deal with socket-level options
|
||||
let mut options = self.options.write();
|
||||
match options.socket.set_option(option, &*inner) {
|
||||
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
||||
res => return res.map(|_need_iface_poll| ()),
|
||||
}
|
||||
// `options` must be dropped here because `do_netlink_setsockopt` may lock other mutexes.
|
||||
drop(options);
|
||||
|
||||
// Deal with netlink-level options
|
||||
do_netlink_setsockopt(option, &mut inner)
|
||||
}
|
||||
|
||||
fn pseudo_inode(&self) -> &Arc<dyn Inode> {
|
||||
|
|
@ -210,6 +248,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: SupportedNetlinkProtocol> GetSocketLevelOption
|
||||
for Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>
|
||||
{
|
||||
fn is_listening(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: SupportedNetlinkProtocol> SetSocketLevelOption
|
||||
for Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>
|
||||
{
|
||||
}
|
||||
|
||||
impl<P: SupportedNetlinkProtocol> Inner<UnboundNetlink<P>, BoundNetlink<P::Message>> {
|
||||
fn add_groups(&mut self, groups: GroupIdSet) {
|
||||
match self {
|
||||
|
|
@ -226,18 +277,18 @@ impl<P: SupportedNetlinkProtocol> Inner<UnboundNetlink<P>, BoundNetlink<P::Messa
|
|||
}
|
||||
}
|
||||
|
||||
fn do_set_netlink_option<P: SupportedNetlinkProtocol>(
|
||||
inner: &RwMutex<Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>>,
|
||||
fn do_netlink_setsockopt<P: SupportedNetlinkProtocol>(
|
||||
option: &dyn SocketOption,
|
||||
inner: &mut Inner<UnboundNetlink<P>, BoundNetlink<P::Message>>,
|
||||
) -> Result<()> {
|
||||
match_sock_option_ref!(option, {
|
||||
add_membership: AddMembership => {
|
||||
let groups = add_membership.get().unwrap();
|
||||
inner.write().add_groups(GroupIdSet::new(*groups));
|
||||
inner.add_groups(GroupIdSet::new(*groups));
|
||||
},
|
||||
drop_membership: DropMembership => {
|
||||
let groups = drop_membership.get().unwrap();
|
||||
inner.write().drop_groups(GroupIdSet::new(*groups));
|
||||
inner.drop_groups(GroupIdSet::new(*groups));
|
||||
},
|
||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ mod table;
|
|||
pub use addr::{GroupIdSet, NetlinkSocketAddr};
|
||||
pub use kobject_uevent::NetlinkUeventSocket;
|
||||
pub use options::{AddMembership, DropMembership};
|
||||
pub(super) use receiver::NETLINK_DEFAULT_BUF_SIZE;
|
||||
pub use route::NetlinkRouteSocket;
|
||||
pub use table::{is_valid_protocol, StandardNetlinkProtocol};
|
||||
|
||||
|
|
|
|||
|
|
@ -112,4 +112,4 @@ impl<Message: QueueableMessage> MessageReceiver<Message> {
|
|||
}
|
||||
}
|
||||
|
||||
const NETLINK_DEFAULT_BUF_SIZE: usize = 65536;
|
||||
pub(in crate::net) const NETLINK_DEFAULT_BUF_SIZE: usize = 65536;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ use super::message::{MessageQueue, MessageReceiver};
|
|||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::utils::Inode,
|
||||
match_sock_option_mut,
|
||||
net::socket::{
|
||||
new_pseudo_inode,
|
||||
options::SocketOption,
|
||||
options::{Error as SocketError, SocketOption},
|
||||
private::SocketPrivate,
|
||||
unix::{ctrl_msg::AuxiliaryData, UnixSocketAddr},
|
||||
util::{
|
||||
|
|
@ -199,6 +200,15 @@ impl Socket for UnixDatagramSocket {
|
|||
}
|
||||
|
||||
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
|
||||
match_sock_option_mut!(option, {
|
||||
socket_errors: SocketError => {
|
||||
// TODO: Support socket errors for UNIX sockets
|
||||
socket_errors.set(None);
|
||||
return Ok(());
|
||||
},
|
||||
_ => ()
|
||||
});
|
||||
|
||||
let options = self.options.read();
|
||||
|
||||
// Deal with socket-level options
|
||||
|
|
@ -217,7 +227,6 @@ impl Socket for UnixDatagramSocket {
|
|||
let mut options = self.options.write();
|
||||
|
||||
match options.socket.set_option(option, &self.local_receiver) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
||||
// TODO: Deal with socket options from other levels
|
||||
warn!("only socket-level options are supported");
|
||||
|
|
@ -226,7 +235,7 @@ impl Socket for UnixDatagramSocket {
|
|||
"the socket option to get is unknown"
|
||||
)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
res => res.map(|_need_iface_poll| ()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
match_sock_option_mut,
|
||||
net::socket::{
|
||||
new_pseudo_inode,
|
||||
options::{PeerCred, PeerGroups, SocketOption},
|
||||
options::{Error as SocketError, PeerCred, PeerGroups, SocketOption},
|
||||
private::SocketPrivate,
|
||||
unix::{cred::SocketCred, ctrl_msg::AuxiliaryData, CUserCred, UnixSocketAddr},
|
||||
util::{
|
||||
|
|
@ -387,6 +387,15 @@ impl Socket for UnixStreamSocket {
|
|||
}
|
||||
|
||||
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
|
||||
match_sock_option_mut!(option, {
|
||||
socket_errors: SocketError => {
|
||||
// TODO: Support socket errors for UNIX sockets
|
||||
socket_errors.set(None);
|
||||
return Ok(());
|
||||
},
|
||||
_ => ()
|
||||
});
|
||||
|
||||
let state = self.state.read();
|
||||
let options = self.options.read();
|
||||
|
||||
|
|
@ -413,7 +422,6 @@ impl Socket for UnixStreamSocket {
|
|||
let mut options = self.options.write();
|
||||
|
||||
match options.socket.set_option(option, state.as_ref()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
||||
// TODO: Deal with socket options from other levels
|
||||
warn!("only socket-level options are supported");
|
||||
|
|
@ -422,7 +430,7 @@ impl Socket for UnixStreamSocket {
|
|||
"the socket option to get is unknown"
|
||||
)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
res => res.map(|_need_iface_poll| ()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use super::LingerOption;
|
|||
use crate::{
|
||||
match_sock_option_mut, match_sock_option_ref,
|
||||
net::socket::{
|
||||
netlink::NETLINK_DEFAULT_BUF_SIZE,
|
||||
options::{
|
||||
AcceptConn, Broadcast, KeepAlive, Linger, PassCred, PeerCred, PeerGroups, Priority,
|
||||
RecvBuf, RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption,
|
||||
|
|
@ -88,11 +89,19 @@ impl SocketOptionSet {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the default socket level options for netlink socket.
|
||||
pub(in crate::net) fn new_netlink() -> Self {
|
||||
Self {
|
||||
send_buf: NETLINK_DEFAULT_BUF_SIZE as u32,
|
||||
recv_buf: NETLINK_DEFAULT_BUF_SIZE as u32,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets socket-level options.
|
||||
///
|
||||
/// Note that the socket error has to be handled separately, because it is automatically
|
||||
/// cleared after reading. This method does not handle it. Instead,
|
||||
/// [`Self::get_and_clear_socket_errors`] should be used.
|
||||
/// Note that the socket error has to be handled separately. This method does not handle it
|
||||
/// because it is automatically cleared after reading.
|
||||
pub fn get_option(
|
||||
&self,
|
||||
option: &mut dyn SocketOption,
|
||||
|
|
|
|||
|
|
@ -257,3 +257,39 @@ FN_TEST(add_and_drop_membership)
|
|||
TEST_SUCC(close(sk_new));
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_TEST(getsockopt_membership)
|
||||
{
|
||||
int group;
|
||||
socklen_t group_size = sizeof(group);
|
||||
|
||||
TEST_ERRNO(getsockopt(sk_unbound, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||
&group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
TEST_ERRNO(getsockopt(sk_unbound, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
|
||||
&group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
|
||||
TEST_ERRNO(getsockopt(sk_bound, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||
&group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
TEST_ERRNO(getsockopt(sk_bound, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
|
||||
&group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
|
||||
TEST_ERRNO(getsockopt(sk_connected, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||
&group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
TEST_ERRNO(getsockopt(sk_connected, SOL_NETLINK,
|
||||
NETLINK_DROP_MEMBERSHIP, &group, &group_size),
|
||||
ENOPROTOOPT);
|
||||
}
|
||||
END_TEST()
|
||||
|
||||
FN_SETUP(cleanup)
|
||||
{
|
||||
CHECK(close(sk_unbound));
|
||||
CHECK(close(sk_bound));
|
||||
CHECK(close(sk_connected));
|
||||
}
|
||||
END_SETUP()
|
||||
|
|
|
|||
Loading…
Reference in New Issue