Set keepalive and tcp_nodelay on underlying sockets

This commit is contained in:
jiangjianfeng
2024-12-05 08:40:20 +00:00
committed by Tate, Hongliang Tian
parent 8b07a68e9e
commit 58cf8ea681
13 changed files with 293 additions and 35 deletions

View File

@ -10,12 +10,13 @@ use ostd::sync::{LocalIrqDisabled, RwLock, SpinLock, SpinLockGuard, WriteIrqDisa
use smoltcp::{ use smoltcp::{
iface::Context, iface::Context,
socket::{tcp::State, udp::UdpMetadata, PollAt}, socket::{tcp::State, udp::UdpMetadata, PollAt},
time::Instant, time::{Duration, Instant},
wire::{IpAddress, IpEndpoint, IpRepr, TcpControl, TcpRepr, UdpRepr}, wire::{IpAddress, IpEndpoint, IpRepr, TcpControl, TcpRepr, UdpRepr},
}; };
use super::{ use super::{
event::{SocketEventObserver, SocketEvents}, event::{SocketEventObserver, SocketEvents},
option::RawTcpSetOption,
RawTcpSocket, RawUdpSocket, TcpStateCheck, RawTcpSocket, RawUdpSocket, TcpStateCheck,
}; };
use crate::{ext::Ext, iface::Iface}; use crate::{ext::Ext, iface::Iface};
@ -251,6 +252,7 @@ pub enum ConnectState {
pub struct NeedIfacePoll(bool); pub struct NeedIfacePoll(bool);
impl NeedIfacePoll { impl NeedIfacePoll {
pub const TRUE: Self = Self(true);
pub const FALSE: Self = Self(false); pub const FALSE: Self = Self(false);
} }
@ -371,6 +373,25 @@ impl<E: Ext> BoundTcpSocket<E> {
} }
} }
impl<E: Ext> RawTcpSetOption for BoundTcpSocket<E> {
fn set_keep_alive(&mut self, interval: Option<Duration>) -> NeedIfacePoll {
let mut socket = self.0.socket.lock();
socket.set_keep_alive(interval);
if interval.is_some() {
self.0.update_next_poll_at_ms(PollAt::Now);
NeedIfacePoll::TRUE
} else {
NeedIfacePoll::FALSE
}
}
fn set_nagle_enabled(&mut self, enabled: bool) {
let mut socket = self.0.socket.lock();
socket.set_nagle_enabled(enabled);
}
}
impl<E: Ext> BoundUdpSocket<E> { impl<E: Ext> BoundUdpSocket<E> {
/// Binds to a specified endpoint. /// Binds to a specified endpoint.
/// ///

View File

@ -2,13 +2,15 @@
mod bound; mod bound;
mod event; mod event;
mod option;
mod state; mod state;
mod unbound; mod unbound;
pub use bound::{BoundTcpSocket, BoundUdpSocket, ConnectState, NeedIfacePoll}; pub use bound::{BoundTcpSocket, BoundUdpSocket, ConnectState, NeedIfacePoll};
pub(crate) use bound::{BoundTcpSocketInner, BoundUdpSocketInner, TcpProcessResult}; pub(crate) use bound::{BoundTcpSocketInner, BoundUdpSocketInner, TcpProcessResult};
pub use event::{SocketEventObserver, SocketEvents}; pub use event::{SocketEventObserver, SocketEvents};
pub use state::TcpStateCheck; pub use option::RawTcpSetOption;
pub use state::{TcpState, TcpStateCheck};
pub use unbound::{ pub use unbound::{
UnboundTcpSocket, UnboundUdpSocket, TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UnboundTcpSocket, UnboundUdpSocket, TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN,
UDP_SEND_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN,

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
use smoltcp::time::Duration;
use super::NeedIfacePoll;
/// A trait defines setting socket options on a raw socket.
///
/// TODO: When `UnboundSocket` is removed, all methods in this trait can accept
/// `&self` instead of `&mut self` as parameter.
pub trait RawTcpSetOption {
/// Sets the keep alive interval.
///
/// Polling the iface _may_ be required after this method succeeds.
fn set_keep_alive(&mut self, interval: Option<Duration>) -> NeedIfacePoll;
/// Enables or disables Nagles Algorithm.
///
/// Polling the iface is not required after this method succeeds.
fn set_nagle_enabled(&mut self, enabled: bool);
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use smoltcp::socket::tcp::State as TcpState; pub use smoltcp::socket::tcp::State as TcpState;
use super::RawTcpSocket; use super::RawTcpSocket;

View File

@ -2,7 +2,7 @@
use alloc::{boxed::Box, vec}; use alloc::{boxed::Box, vec};
use super::{RawTcpSocket, RawUdpSocket}; use super::{option::RawTcpSetOption, NeedIfacePoll, RawTcpSocket, RawUdpSocket};
pub struct UnboundSocket<T> { pub struct UnboundSocket<T> {
socket: Box<T>, socket: Box<T>,
@ -30,6 +30,17 @@ impl Default for UnboundTcpSocket {
} }
} }
impl RawTcpSetOption for UnboundTcpSocket {
fn set_keep_alive(&mut self, interval: Option<smoltcp::time::Duration>) -> NeedIfacePoll {
self.socket.set_keep_alive(interval);
NeedIfacePoll::FALSE
}
fn set_nagle_enabled(&mut self, enabled: bool) {
self.socket.set_nagle_enabled(enabled);
}
}
impl UnboundUdpSocket { impl UnboundUdpSocket {
pub fn new() -> Self { pub fn new() -> Self {
let raw_udp_socket = { let raw_udp_socket = {

View File

@ -1,3 +1,3 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
pub use smoltcp::time::Instant; pub use smoltcp::time::{Duration, Instant};

View File

@ -18,7 +18,9 @@ use crate::{
net::socket::{ net::socket::{
options::{Error as SocketError, SocketOption}, options::{Error as SocketError, SocketOption},
util::{ util::{
options::SocketOptionSet, send_recv_flags::SendRecvFlags, socket_addr::SocketAddr, options::{SetSocketLevelOption, SocketOptionSet},
send_recv_flags::SendRecvFlags,
socket_addr::SocketAddr,
MessageHeader, MessageHeader,
}, },
Socket, Socket,
@ -393,6 +395,30 @@ impl Socket for DatagramSocket {
} }
fn set_option(&self, option: &dyn SocketOption) -> Result<()> { fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
self.options.write().socket.set_option(option) let mut options = self.options.write();
let mut inner = self.inner.write();
match options.socket.set_option(option, inner.as_mut()) {
Err(e) => Err(e),
Ok(need_iface_poll) => {
let iface_to_poll = need_iface_poll
.then(|| match inner.as_ref() {
Inner::Unbound(_) => None,
Inner::Bound(bound_datagram) => Some(bound_datagram.iface().clone()),
})
.flatten();
drop(inner);
drop(options);
if let Some(iface) = iface_to_poll {
iface.poll();
}
Ok(())
} }
} }
}
}
impl SetSocketLevelOption for Inner {}

View File

@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicBool, Ordering};
use aster_bigtcp::{ use aster_bigtcp::{
errors::tcp::{RecvError, SendError}, errors::tcp::{RecvError, SendError},
socket::{NeedIfacePoll, TcpStateCheck}, socket::{NeedIfacePoll, RawTcpSetOption, RawTcpSocket, TcpStateCheck},
wire::IpEndpoint, wire::IpEndpoint,
}; };
@ -205,4 +205,15 @@ impl ConnectedStream {
pub(super) fn set_observer(&self, observer: StreamObserver) { pub(super) fn set_observer(&self, observer: StreamObserver) {
self.bound_socket.set_observer(observer) self.bound_socket.set_observer(observer)
} }
pub(super) fn set_raw_option<R>(
&mut self,
set_option: impl Fn(&mut dyn RawTcpSetOption) -> R,
) -> R {
set_option(&mut self.bound_socket)
}
pub(super) fn raw_with<R>(&self, f: impl FnOnce(&RawTcpSocket) -> R) -> R {
self.bound_socket.raw_with(f)
}
} }

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_bigtcp::{socket::ConnectState, wire::IpEndpoint}; use aster_bigtcp::{
socket::{ConnectState, RawTcpSetOption},
wire::IpEndpoint,
};
use super::{connected::ConnectedStream, init::InitStream}; use super::{connected::ConnectedStream, init::InitStream};
use crate::{ use crate::{
@ -83,4 +86,11 @@ impl ConnectingStream {
pub(super) fn check_io_events(&self) -> IoEvents { pub(super) fn check_io_events(&self) -> IoEvents {
IoEvents::empty() IoEvents::empty()
} }
pub(super) fn set_raw_option<R>(
&mut self,
set_option: impl Fn(&mut dyn RawTcpSetOption) -> R,
) -> R {
set_option(&mut self.bound_socket)
}
} }

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_bigtcp::{socket::UnboundTcpSocket, wire::IpEndpoint}; use aster_bigtcp::{
socket::{RawTcpSetOption, UnboundTcpSocket},
wire::IpEndpoint,
};
use super::{connecting::ConnectingStream, listen::ListenStream, StreamObserver}; use super::{connecting::ConnectingStream, listen::ListenStream, StreamObserver};
use crate::{ use crate::{
@ -108,4 +111,14 @@ impl InitStream {
// Linux adds OUT and HUP events for a newly created socket // Linux adds OUT and HUP events for a newly created socket
IoEvents::OUT | IoEvents::HUP IoEvents::OUT | IoEvents::HUP
} }
pub(super) fn set_raw_option<R>(
&mut self,
set_option: impl Fn(&mut dyn RawTcpSetOption) -> R,
) -> R {
match self {
InitStream::Unbound(unbound_socket) => set_option(unbound_socket.as_mut()),
InitStream::Bound(bound_socket) => set_option(bound_socket),
}
}
} }

View File

@ -1,7 +1,10 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_bigtcp::{ use aster_bigtcp::{
errors::tcp::ListenError, iface::BindPortConfig, socket::UnboundTcpSocket, wire::IpEndpoint, errors::tcp::ListenError,
iface::BindPortConfig,
socket::{RawTcpSetOption, TcpState, UnboundTcpSocket},
wire::IpEndpoint,
}; };
use ostd::sync::PreemptDisabled; use ostd::sync::PreemptDisabled;
@ -104,6 +107,30 @@ impl ListenStream {
IoEvents::empty() IoEvents::empty()
} }
} }
/// Calls `f` to set socket option on raw socket.
///
/// This method will call `f` on the bound socket and each backlog socket that is in `Listen` state .
pub(super) fn set_raw_option<R>(
&mut self,
set_option: impl Fn(&mut dyn RawTcpSetOption) -> R,
) -> R {
self.backlog_sockets.write().iter_mut().for_each(|socket| {
if socket
.bound_socket
.raw_with(|raw_tcp_socket| raw_tcp_socket.state() != TcpState::Listen)
{
return;
}
// If the socket receives SYN after above check,
// we will also set keep alive on the socket that is not in `Listen` state.
// But such a race doesn't matter, we just let it happen.
set_option(&mut socket.bound_socket);
});
set_option(&mut self.bound_socket)
}
} }
struct BacklogSocket { struct BacklogSocket {
@ -119,7 +146,15 @@ impl BacklogSocket {
"the socket is not bound", "the socket is not bound",
))?; ))?;
let unbound_socket = Box::new(UnboundTcpSocket::new()); let unbound_socket = {
let mut unbound = UnboundTcpSocket::new();
unbound.set_keep_alive(bound_socket.raw_with(|socket| socket.keep_alive()));
unbound.set_nagle_enabled(bound_socket.raw_with(|socket| socket.nagle_enabled()));
// TODO: Inherit other options that can be set via `setsockopt` from bound socket
Box::new(unbound)
};
let bound_socket = { let bound_socket = {
let iface = bound_socket.iface(); let iface = bound_socket.iface();
let bind_port_config = BindPortConfig::new(local_endpoint.port, true); let bind_port_config = BindPortConfig::new(local_endpoint.port, true);

View File

@ -2,7 +2,10 @@
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, Ordering};
use aster_bigtcp::wire::IpEndpoint; use aster_bigtcp::{
socket::{NeedIfacePoll, RawTcpSetOption},
wire::IpEndpoint,
};
use connected::ConnectedStream; use connected::ConnectedStream;
use connecting::{ConnResult, ConnectingStream}; use connecting::{ConnResult, ConnectingStream};
use init::InitStream; use init::InitStream;
@ -20,14 +23,20 @@ use crate::{
utils::{InodeMode, Metadata, StatusFlags}, utils::{InodeMode, Metadata, StatusFlags},
}, },
match_sock_option_mut, match_sock_option_ref, match_sock_option_mut, match_sock_option_ref,
net::socket::{ net::{
iface::Iface,
socket::{
options::{Error as SocketError, SocketOption}, options::{Error as SocketError, SocketOption},
util::{ util::{
options::SocketOptionSet, send_recv_flags::SendRecvFlags, options::{SetSocketLevelOption, SocketOptionSet},
shutdown_cmd::SockShutdownCmd, socket_addr::SocketAddr, MessageHeader, send_recv_flags::SendRecvFlags,
shutdown_cmd::SockShutdownCmd,
socket_addr::SocketAddr,
MessageHeader,
}, },
Socket, Socket,
}, },
},
prelude::*, prelude::*,
process::signal::{PollHandle, Pollable, Pollee}, process::signal::{PollHandle, Pollable, Pollee},
util::{MultiRead, MultiWrite}, util::{MultiRead, MultiWrite},
@ -87,11 +96,28 @@ impl StreamSocket {
}) })
} }
fn new_connected(connected_stream: ConnectedStream) -> Arc<Self> { fn new_accepted(connected_stream: ConnectedStream) -> Arc<Self> {
let options = connected_stream.raw_with(|raw_tcp_socket| {
let mut options = OptionSet::new();
if raw_tcp_socket.keep_alive().is_some() {
options.socket.set_keep_alive(true);
}
if !raw_tcp_socket.nagle_enabled() {
options.tcp.set_no_delay(true);
}
// TODO: Update other options for a newly-accepted socket
options
});
let pollee = Pollee::new(); let pollee = Pollee::new();
connected_stream.set_observer(StreamObserver::new(pollee.clone())); connected_stream.set_observer(StreamObserver::new(pollee.clone()));
Arc::new(Self { Arc::new(Self {
options: RwLock::new(OptionSet::new()), options: RwLock::new(options),
state: RwLock::new(Takeable::new(State::Connected(connected_stream))), state: RwLock::new(Takeable::new(State::Connected(connected_stream))),
is_nonblocking: AtomicBool::new(false), is_nonblocking: AtomicBool::new(false),
pollee, pollee,
@ -276,7 +302,7 @@ impl StreamSocket {
.try_accept(&self.pollee) .try_accept(&self.pollee)
.map(|connected_stream| { .map(|connected_stream| {
let remote_endpoint = connected_stream.remote_endpoint(); let remote_endpoint = connected_stream.remote_endpoint();
let accepted_socket = Self::new_connected(connected_stream); let accepted_socket = Self::new_accepted(connected_stream);
(accepted_socket as _, remote_endpoint.into()) (accepted_socket as _, remote_endpoint.into())
}); });
let iface_to_poll = listen_stream.iface().clone(); let iface_to_poll = listen_stream.iface().clone();
@ -650,11 +676,23 @@ impl Socket for StreamSocket {
} }
fn set_option(&self, option: &dyn SocketOption) -> Result<()> { fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
let mut options = self.options.write(); let (mut options, mut state) = self.update_connecting();
match options.socket.set_option(option) { match options.socket.set_option(option, state.as_mut()) {
Err(err) if err.error() == Errno::ENOPROTOOPT => (), Err(err) if err.error() == Errno::ENOPROTOOPT => (),
res => return res, Err(err) => return Err(err),
Ok(need_iface_poll) => {
let iface_to_poll = need_iface_poll.then(|| state.iface().cloned()).flatten();
drop(state);
drop(options);
if let Some(iface) = iface_to_poll {
iface.poll();
}
return Ok(());
}
} }
// FIXME: Here we have only set the value of the option, without actually // FIXME: Here we have only set the value of the option, without actually
@ -663,6 +701,7 @@ impl Socket for StreamSocket {
tcp_no_delay: NoDelay => { tcp_no_delay: NoDelay => {
let no_delay = tcp_no_delay.get().unwrap(); let no_delay = tcp_no_delay.get().unwrap();
options.tcp.set_no_delay(*no_delay); options.tcp.set_no_delay(*no_delay);
state.set_raw_option(|raw_socket: &mut dyn RawTcpSetOption| raw_socket.set_nagle_enabled(!no_delay));
}, },
tcp_congestion: Congestion => { tcp_congestion: Congestion => {
let congestion = tcp_congestion.get().unwrap(); let congestion = tcp_congestion.get().unwrap();
@ -694,18 +733,62 @@ impl Socket for StreamSocket {
} }
} }
impl State {
/// Calls `f` to set raw socket option.
///
/// Note that for listening socket, `f` is called on all backlog sockets in `Listen` State.
/// That is to say, `f` won't be called on backlog sockets in `SynReceived` or `Established` state.
fn set_raw_option<R>(&mut self, set_option: impl Fn(&mut dyn RawTcpSetOption) -> R) -> R {
match self {
State::Init(init_stream) => init_stream.set_raw_option(set_option),
State::Connecting(connecting_stream) => connecting_stream.set_raw_option(set_option),
State::Connected(connected_stream) => connected_stream.set_raw_option(set_option),
State::Listen(listen_stream) => listen_stream.set_raw_option(set_option),
}
}
fn iface(&self) -> Option<&Arc<Iface>> {
match self {
State::Init(_) => None,
State::Connecting(ref connecting_stream) => Some(connecting_stream.iface()),
State::Connected(ref connected_stream) => Some(connected_stream.iface()),
State::Listen(ref listen_stream) => Some(listen_stream.iface()),
}
}
}
impl SetSocketLevelOption for State {
fn set_keep_alive(&mut self, keep_alive: bool) -> NeedIfacePoll {
/// The keepalive interval.
///
/// The linux value can be found at `/proc/sys/net/ipv4/tcp_keepalive_intvl`,
/// which is by default 75 seconds for most Linux distributions.
const KEEPALIVE_INTERVAL: aster_bigtcp::time::Duration =
aster_bigtcp::time::Duration::from_secs(75);
let interval = if keep_alive {
Some(KEEPALIVE_INTERVAL)
} else {
None
};
let set_keepalive =
|raw_socket: &mut dyn RawTcpSetOption| raw_socket.set_keep_alive(interval);
self.set_raw_option(set_keepalive)
}
}
impl Drop for StreamSocket { impl Drop for StreamSocket {
fn drop(&mut self) { fn drop(&mut self) {
let state = self.state.get_mut().take(); let state = self.state.get_mut().take();
let iface_to_poll = match state { let iface_to_poll = state.iface().cloned();
State::Init(_) => None,
State::Connecting(ref connecting_stream) => Some(connecting_stream.iface().clone()),
State::Connected(ref connected_stream) => Some(connected_stream.iface().clone()),
State::Listen(ref listen_stream) => Some(listen_stream.iface().clone()),
};
// Dropping the state will drop the sockets. This will trigger the socket close process (if
// needed) and require immediate iface polling afterwards.
drop(state); drop(state);
if let Some(iface) = iface_to_poll { if let Some(iface) = iface_to_poll {
iface.poll(); iface.poll();
} }

View File

@ -3,13 +3,14 @@
use core::time::Duration; use core::time::Duration;
use aster_bigtcp::socket::{ use aster_bigtcp::socket::{
TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN, NeedIfacePoll, TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN, UDP_SEND_PAYLOAD_LEN,
}; };
use crate::{ use crate::{
match_sock_option_mut, match_sock_option_ref, match_sock_option_mut, match_sock_option_ref,
net::socket::options::{ net::socket::options::{
Error as SocketError, Linger, RecvBuf, ReuseAddr, ReusePort, SendBuf, SocketOption, Error as SocketError, KeepAlive, Linger, RecvBuf, ReuseAddr, ReusePort, SendBuf,
SocketOption,
}, },
prelude::*, prelude::*,
}; };
@ -24,6 +25,7 @@ pub struct SocketOptionSet {
send_buf: u32, send_buf: u32,
recv_buf: u32, recv_buf: u32,
linger: LingerOption, linger: LingerOption,
keep_alive: bool,
} }
impl SocketOptionSet { impl SocketOptionSet {
@ -36,6 +38,7 @@ impl SocketOptionSet {
send_buf: TCP_SEND_BUF_LEN as u32, send_buf: TCP_SEND_BUF_LEN as u32,
recv_buf: TCP_RECV_BUF_LEN as u32, recv_buf: TCP_RECV_BUF_LEN as u32,
linger: LingerOption::default(), linger: LingerOption::default(),
keep_alive: false,
} }
} }
@ -48,6 +51,7 @@ impl SocketOptionSet {
send_buf: UDP_SEND_PAYLOAD_LEN as u32, send_buf: UDP_SEND_PAYLOAD_LEN as u32,
recv_buf: UDP_RECV_PAYLOAD_LEN as u32, recv_buf: UDP_RECV_PAYLOAD_LEN as u32,
linger: LingerOption::default(), linger: LingerOption::default(),
keep_alive: false,
} }
} }
@ -87,13 +91,21 @@ impl SocketOptionSet {
let linger = self.linger(); let linger = self.linger();
socket_linger.set(linger); socket_linger.set(linger);
}, },
socket_keepalive: KeepAlive => {
let keep_alive = self.keep_alive();
socket_keepalive.set(keep_alive);
},
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown") _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown")
}); });
Ok(()) Ok(())
} }
/// Sets socket-level options. /// Sets socket-level options.
pub fn set_option(&mut self, option: &dyn SocketOption) -> Result<()> { pub fn set_option(
&mut self,
option: &dyn SocketOption,
socket: &mut dyn SetSocketLevelOption,
) -> Result<NeedIfacePoll> {
match_sock_option_ref!(option, { match_sock_option_ref!(option, {
socket_recv_buf: RecvBuf => { socket_recv_buf: RecvBuf => {
let recv_buf = socket_recv_buf.get().unwrap(); let recv_buf = socket_recv_buf.get().unwrap();
@ -123,10 +135,15 @@ impl SocketOptionSet {
let linger = socket_linger.get().unwrap(); let linger = socket_linger.get().unwrap();
self.set_linger(*linger); self.set_linger(*linger);
}, },
socket_keepalive: KeepAlive => {
let keep_alive = socket_keepalive.get().unwrap();
self.set_keep_alive(*keep_alive);
return Ok(socket.set_keep_alive(*keep_alive));
},
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown") _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
}); });
Ok(()) Ok(NeedIfacePoll::FALSE)
} }
} }
@ -152,3 +169,11 @@ impl LingerOption {
self.timeout self.timeout
} }
} }
/// A trait used for setting socket level options on actual sockets.
pub(in crate::net) trait SetSocketLevelOption {
/// Sets whether keepalive messages are enabled.
fn set_keep_alive(&mut self, _keep_alive: bool) -> NeedIfacePoll {
NeedIfacePoll::FALSE
}
}