#[non_exhaustive]pub struct PooledConnection<'p, P: ProtocolState> { /* private fields */ }Expand description
A pooled connection borrowed from a ConnectionPool.
Implements Deref<Target = Connection<P>> so every typed
connection method is callable directly on the guard. On drop the
underlying Connection is returned to the pool. Use
Self::invalidate to mark the connection as bad and have it
dropped (instead of returned) on guard drop.
Implementations§
Source§impl<'p, P: ProtocolState + Send + Sync + 'static> PooledConnection<'p, P>
impl<'p, P: ProtocolState + Send + Sync + 'static> PooledConnection<'p, P>
Sourcepub fn invalidate(self)
pub fn invalidate(self)
Mark this connection as unhealthy. On drop, the connection
is closed AND the pool schedules an async task to build a
replacement and put it back into the pool (0.19 Finding C
— closes the silent capacity decay where every invalidate
permanently shrank the pool by one connection).
Use when you’ve observed a recoverable error that suggests the socket may be in a bad state (rare; most kernel errors are per-request, not per-socket).
Replenish race window. The replacement is built on a
tokio::spawn (off the drop path because it’s async).
Between invalidate and the new connection landing in the
pool there is a brief window where acquire() may block —
up to the acquire_timeout. If replacement construction
itself fails (kernel module missing, namespace gone,
permission revoked), the failure is tracing::error!-logged
and the pool’s effective capacity drops by one. Subsequent
invalidates that succeed restore capacity.
Consumes the guard (Plan 162) so a subsequent &*p
is a compile error (E0382 — use of moved value) instead
of a runtime panic.
p.invalidate();
let _ = &*p; // compile error: borrow of moved value `p`Methods from Deref<Target = Connection<P>>§
Sourcepub fn subscribe_group(&self, name: &str) -> Result<()>
pub fn subscribe_group(&self, name: &str) -> Result<()>
Send a typed GENL request and parse the typed response.
Builds a netlink message from request: M, sends it,
receives the response, and parses the first non-ACK reply
into R: GenlMessage + Default. Returns the parsed R.
R::default() is the parse seed: missing attributes leave
fields at their type-default values (matches the
#[derive(GenlMessage)] from_bytes semantics — see its
docs for the rationale).
§Example
use nlink::macros::*;
use nlink::Connection;
#[genl_family(name = "my_family", version = 1)]
pub struct MyFamily;
#[derive(GenlCommand, Debug, Clone, Copy)]
#[genl_command(repr = "u8")]
pub enum MyCmd { Get = 1 }
#[derive(GenlAttribute, Debug, Clone, Copy)]
#[genl_attribute(repr = "u16")]
pub enum MyAttr { Id = 1, Name = 2 }
#[derive(GenlMessage, Debug, Default)]
#[genl_message(cmd = MyCmd::Get)]
pub struct GetReq { #[genl_attr(MyAttr::Id)] pub id: u32 }
#[derive(GenlMessage, Debug, Default)]
#[genl_message(cmd = MyCmd::Get)]
pub struct GetReply {
#[genl_attr(MyAttr::Id)] pub id: u32,
#[genl_attr(MyAttr::Name)] pub name: String,
}
let reply: GetReply = conn.send_typed(GetReq { id: 0 }).await?;
println!("got id={} name={}", reply.id, reply.name);Subscribe to a named multicast group exposed by this family.
Looks up the group ID via
GenlFamily::mcast_group (the
map was populated at construction time by
#[genl_family]’s resolve_async impl, parsing
CTRL_ATTR_MCAST_GROUPS out of CTRL_CMD_GETFAMILY).
Returns
Error::FamilyNotFound when the named group isn’t
registered on this kernel —
e.g., asking for "monitor" on a kernel too old to ship
that group, or a binary/kernel mismatch.
Pair with the EventSource-driven
events() (when the
family implements EventSource) to consume typed
notifications from the kernel.
§Example
use nlink::netlink::{Connection, genl::dpll::Dpll};
use tokio_stream::StreamExt;
let conn = Connection::<Dpll>::new_async().await?;
conn.subscribe_group("monitor")?;
let mut events = conn.events().await;
while let Some(evt) = events.next().await {
println!("{:?}", evt?);
}pub async fn send_typed<M, R>(&self, request: M) -> Result<R>
Sourcepub async fn dump_typed_stream<M, R>(
&self,
request: M,
) -> Result<GenlTypedDumpStream<'_, F, R>>
pub async fn dump_typed_stream<M, R>( &self, request: M, ) -> Result<GenlTypedDumpStream<'_, F, R>>
Stream a typed GENL dump (multi-frame response).
Builds a NLM_F_REQUEST | NLM_F_DUMP request from
request: M, awaits the send, and returns a Stream
that yields each kernel frame parsed into R.
Compare with Self::send_typed (one request, one
response). For the canonical kernel dump shape (*_CMD_GET
with NLM_F_DUMP returning many frames) this is the right
helper.
Sourcepub fn socket(&self) -> &NetlinkSocket
pub fn socket(&self) -> &NetlinkSocket
Get the underlying socket.
Sourcepub fn is_dispatcher_mode(&self) -> bool
pub fn is_dispatcher_mode(&self) -> bool
Whether this connection is in dispatcher mode (#134).
Sourcepub fn get_timeout(&self) -> Option<Duration>
pub fn get_timeout(&self) -> Option<Duration>
Get the configured timeout.
Sourcepub fn enable_strict_checking(&self, on: bool) -> Result<()>
pub fn enable_strict_checking(&self, on: bool) -> Result<()>
Enable kernel-side strict checking (NETLINK_GET_STRICT_CHK,
kernel 5.0+). When enabled, the kernel validates dump request
filters strictly and returns an error if they reference
unknown attributes — useful for catching client/kernel-version
mismatches early during development.
Off by default for backwards compatibility with older
kernels. The setsockopt is silently a no-op on pre-5.0
kernels (returns Ok(()) on ENOPROTOOPT), so calling
enable_strict_checking(true) unconditionally is safe.
§Example
use nlink::{Connection, Route};
let conn = Connection::<Route>::new()?;
conn.enable_strict_checking(true)?;Sourcepub fn set_ext_ack(&self, on: bool) -> Result<()>
pub fn set_ext_ack(&self, on: bool) -> Result<()>
Toggle extended-ack reception (NETLINK_EXT_ACK, kernel
4.12+). Enabled by default during socket construction —
disabling is rarely useful in practice. Exposed for parity
with neli’s API and for callers that explicitly want to
suppress the trailing TLVs in error responses.
Silently a no-op on pre-4.12 kernels (returns Ok(()) on
ENOPROTOOPT).
See Error::Kernel::ext_ack for what these TLVs contain
once parsed.
§Example
use nlink::{Connection, Route};
let conn = Connection::<Route>::new()?;
conn.set_ext_ack(false)?; // disable; rarely usefulSourcepub fn dispatcher(&self) -> &Dispatcher
pub fn dispatcher(&self) -> &Dispatcher
Plan 234 — access this Connection’s dispatcher.
The dispatcher routes ENOBUFS into
ResyncMarker::ResyncStart
for every active multicast subscriber and fans out multicast
frames to per-group broadcast channels. Public so subscriber
constructors (events, conntrack, nftables, devlink, dpll,
nl80211 multicast) can register their broadcast receivers
alongside the kernel-side add_membership call.
The dispatcher is cheap to clone (Arc-wrapped internally);
returning &Dispatcher keeps the ownership story simple
(the Connection owns the canonical handle).
Sourcepub async fn dump_stream<T>(
&self,
msg_type: u16,
) -> Result<DumpStream<'_, P, T>>where
T: FromNetlink + Unpin,
pub async fn dump_stream<T>(
&self,
msg_type: u16,
) -> Result<DumpStream<'_, P, T>>where
T: FromNetlink + Unpin,
Stream a dump response as it arrives, one typed message per
next().await. Terminates on NLMSG_DONE.
Compared to Self::dump_typed, this method does not
buffer the full response. On large dumps (BGP-scale route
tables, conntrack tables on a busy gateway) this avoids
materializing gigabytes of intermediate buffers.
§Cancellation
Dropping the stream is safe. The kernel terminates the dump when no more frames are read; any in-flight frames sit in the kernel socket buffer briefly until they age out. The next request on this connection has a different sequence number so stale frames are skipped.
§Errors
The stream yields Err for per-message parse failures but
keeps iterating — kernel sometimes ships partially-parseable
frames in long dumps and dropping them silently would mask
real bugs. NLMSG_ERROR and socket-level errors terminate
the stream after yielding the error.
§Example
use tokio_stream::StreamExt;
use nlink::{Connection, Route};
use nlink::netlink::messages::LinkMessage;
use nlink::netlink::message::NlMsgType;
let conn = Connection::<Route>::new()?;
let mut stream = conn.dump_stream::<LinkMessage>(NlMsgType::RTM_GETLINK).await?;
while let Some(link) = stream.next().await {
let link = link?;
println!("{}: {}", link.ifindex(), link.name_or("?"));
}Sourcepub async fn dump_stream_with_body<T>(
&self,
msg_type: u16,
body: &[u8],
) -> Result<DumpStream<'_, P, T>>where
T: FromNetlink + Unpin,
pub async fn dump_stream_with_body<T>(
&self,
msg_type: u16,
body: &[u8],
) -> Result<DumpStream<'_, P, T>>where
T: FromNetlink + Unpin,
Like dump_stream, but the caller
supplies the body bytes that follow the nlmsghdr. Bypasses
T::write_dump_header entirely — use this when the dump
request body is runtime-parameterized (e.g.
nfgenmsg.family for conntrack, or a fixed body + filter
attribute for nft rules).
T still parses each frame’s body via
FromNetlink::from_bytes; the per-frame body shape is
whatever the kernel emits (independent of the request body).
Sourcepub async fn events(&self) -> EventSubscription<'_, P>
pub async fn events(&self) -> EventSubscription<'_, P>
Create an event stream that borrows this connection.
Returns a Stream that borrows the connection. The connection
remains usable for non-recv operations (set_strict_checking,
subscribe to add more groups, etc.) while the stream is active.
0.19 Finding B — now async. Acquires the connection’s
request lock for the subscription’s lifetime so concurrent
streams (multiple events(), events() + dump_stream())
no longer race on poll_recv and steal each other’s frames.
Concurrent dumps on a connection with an active events stream
will block until the events stream is dropped — use a second
Connection (or ConnectionPool) for query-in-parallel
patterns.
§Example
use nlink::netlink::{Connection, KobjectUevent};
use tokio_stream::StreamExt;
let conn = Connection::<KobjectUevent>::new()?;
// Borrow connection for streaming (0.19: now async).
let mut events = conn.events().await;
while let Some(event) = events.try_next().await? {
if event.is_add() {
println!("Device added: {}", event.devpath);
}
}
// Connection still usable
drop(events);