aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport/mod.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-30 17:36:23 +0100
committertv <tv@krebsco.de>2026-10-30 17:36:23 +0100
commit0bb7a35141c12a58001b481851d33291d2a6ec98 (patch)
treeefa4f97f3ffe8e26e8ff697be97b05d31f530d43 /src/transport/mod.rs
parent236a11a4798574b9c710dde4b97231ff4cade86d (diff)
add links messages
Introduce two new wire messages, LINKS and LINKS_REPLY (swarm::links). A member answers with its links: for each peer, the transport, whether it’s relayed, the RTT, and whether it’s dormant. It answers members only, at most every 5 s. This bumps wire::VERSION to 7. Every build answers, with or without the feature, so every node needs this patch before the map shows anything beyond its own node.
Diffstat (limited to 'src/transport/mod.rs')
-rw-r--r--src/transport/mod.rs50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index fd3651b..a15a1f7 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -218,6 +218,11 @@ pub(crate) enum ParsedMessage {
Ping(u64),
/// Answers a PING.
Pong(u64),
+ /// Asks a member how it is linked to its peers, for a map of the swarm
+ /// (`swarm::links`).
+ Links(LinksRequest),
+ /// Answers a LINKS.
+ LinksReply(LinksReply),
}
/// A node's proof that it holds the key of its node ID (`identity`), sending from its
@@ -343,6 +348,8 @@ pub(crate) enum MessageKind {
Me,
Ping,
Pong,
+ Links,
+ LinksReply,
}
impl ParsedMessage {
@@ -358,6 +365,8 @@ impl ParsedMessage {
ParsedMessage::Me(_) => MessageKind::Me,
ParsedMessage::Ping(_) => MessageKind::Ping,
ParsedMessage::Pong(_) => MessageKind::Pong,
+ ParsedMessage::Links(_) => MessageKind::Links,
+ ParsedMessage::LinksReply(_) => MessageKind::LinksReply,
}
}
@@ -372,6 +381,8 @@ impl ParsedMessage {
Some((status.epoch, status.epoch_seq))
}
ParsedMessage::Blob(blob) => Some((blob.epoch, blob.epoch_seq)),
+ ParsedMessage::Links(request) => Some((request.epoch, request.epoch_seq)),
+ ParsedMessage::LinksReply(reply) => Some((reply.epoch, reply.epoch_seq)),
ParsedMessage::Who(_)
| ParsedMessage::Me(_)
| ParsedMessage::Ping(_)
@@ -380,6 +391,45 @@ impl ParsedMessage {
}
}
+/// LINKS: what a member is asked for a map of the swarm (`swarm::links`).
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct LinksRequest {
+ #[serde(with = "crate::transport::wire::codec::epoch")]
+ pub(crate) epoch: Epoch,
+ /// See `SyncEnvelope::epoch_seq`.
+ pub(crate) epoch_seq: u32,
+ /// Repeated in the reply: which request it answers.
+ pub(crate) nonce: u64,
+}
+
+/// LINKS_REPLY: how the sender is linked to its neighbours, as it measures them.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct LinksReply {
+ #[serde(with = "crate::transport::wire::codec::epoch")]
+ pub(crate) epoch: Epoch,
+ /// See `SyncEnvelope::epoch_seq`.
+ pub(crate) epoch_seq: u32,
+ /// The request's.
+ pub(crate) nonce: u64,
+ /// At most `swarm::links::MAX_LINKS`.
+ pub(crate) links: Vec<Link>,
+}
+
+/// One of a member's links, in a LINKS_REPLY.
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub(crate) struct Link {
+ /// The peer's node ID.
+ pub(crate) peer: String,
+ /// The transport its messages go over now (the scheme of the path in use), if any.
+ pub(crate) transport: Option<String>,
+ /// Only through a relay (libp2p).
+ pub(crate) relayed: bool,
+ /// The round trip on that path, in microseconds, if measured.
+ pub(crate) rtt_us: Option<u32>,
+ /// Silent for long (`peers.md`, dormancy).
+ pub(crate) dormant: bool,
+}
+
#[derive(Clone, Serialize, Deserialize)]
pub(crate) struct HelloRequest {
/// The sender's swarm, if it knows it (a new node doesn't yet). A receiver in