aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-30 18:29:01 +0100
committertv <tv@krebsco.de>2026-10-30 18:29:01 +0100
commitc33ba31c0d27c3f2531fe9f5ac5dbe55326c8349 (patch)
tree36411002a2a66b0c83893923736bf387255409aa /src
parente3b386c2aa9970f2f999f07ac43b7eb8fe319d8d (diff)
links answers connection type
Diffstat (limited to 'src')
-rw-r--r--src/api/dashboard/dashboard.html4
-rw-r--r--src/api/dashboard/mod.rs1
-rw-r--r--src/swarm/links.rs11
-rw-r--r--src/transport/mod.rs2
-rw-r--r--src/transport/wire.rs15
5 files changed, 22 insertions, 11 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 9a17d6b..5bed5fe 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -366,13 +366,13 @@ function renderGraph() {
if (link.dormant) {
drawn.setAttribute("stroke-dasharray", line.used ? "1 5" : "1 3");
drawn.setAttribute("stroke-linecap", "round");
- } else if (line.used && link.relayed) {
+ } else if (line.used ? link.relayed : line.relayed) {
drawn.setAttribute("stroke-dasharray", "7 4");
}
const title = svg("title");
title.textContent = name(a) + " – " + name(b) +
(line.transport ? " via " + line.transport : "") + (line.used ? " (in use)" : "") +
- (line.used && link.relayed ? ", relayed" : "") + (link.dormant ? ", dormant" : "") +
+ ((line.used ? link.relayed : line.relayed) ? ", relayed" : "") + (link.dormant ? ", dormant" : "") +
(typeof line.rttMs === "number" ? ", " + line.rttMs.toFixed(1) + " ms" : "") +
", measured " + age(secsAgo + elapsed()) + " ago";
drawn.append(title);
diff --git a/src/api/dashboard/mod.rs b/src/api/dashboard/mod.rs
index 92271ef..1de21cd 100644
--- a/src/api/dashboard/mod.rs
+++ b/src/api/dashboard/mod.rs
@@ -240,6 +240,7 @@ fn links_json(links: &[Link]) -> JsonValue {
"others": link.others.iter().map(|other| json!({
"transport": other.transport,
"rttMs": other.rtt_us.map(|us| f64::from(us) / 1000.0),
+ "relayed": other.relayed,
})).collect::<Vec<_>>(),
})
})
diff --git a/src/swarm/links.rs b/src/swarm/links.rs
index d7df0d5..04f2e2d 100644
--- a/src/swarm/links.rs
+++ b/src/swarm/links.rs
@@ -105,7 +105,8 @@ fn scheme_of(address: &str) -> Option<String> {
}
/// The transports besides `used` the peer proved itself at, with a path up: each with
-/// the round trip of its fastest path, if any is measured.
+/// the round trip of its fastest path, if any is measured, and whether all of them go
+/// through a relay.
fn others(destination: Option<&Destination>, used: Option<&str>) -> Vec<OtherPath> {
let mut others: Vec<OtherPath> = Vec::new();
for path in destination.map_or(&[][..], |destination| &destination.paths[..]) {
@@ -119,14 +120,20 @@ fn others(destination: Option<&Destination>, used: Option<&str>) -> Vec<OtherPat
.rtt_ms
.filter(|ms| ms.is_finite() && *ms >= 0.0)
.map(|ms| (ms * 1000.0).min(f64::from(u32::MAX)) as u32);
+ let relayed = path.connection == Some(Connection::Relayed);
match others.iter_mut().find(|other| other.transport == transport) {
Some(other) => {
other.rtt_us = match (other.rtt_us, rtt_us) {
(Some(a), Some(b)) => Some(a.min(b)),
(a, b) => a.or(b),
};
+ other.relayed &= relayed;
}
- None => others.push(OtherPath { transport, rtt_us }),
+ None => others.push(OtherPath {
+ transport,
+ rtt_us,
+ relayed,
+ }),
}
}
others.sort_by(|a, b| a.transport.cmp(&b.transport));
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index 9d28212..d774402 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -440,6 +440,8 @@ pub(crate) struct OtherPath {
pub(crate) transport: String,
/// The round trip of its fastest path, in microseconds, if any is measured.
pub(crate) rtt_us: Option<u32>,
+ /// Only through a relay (libp2p): no direct connection at any of its paths.
+ pub(crate) relayed: bool,
}
#[derive(Clone, Serialize, Deserialize)]
diff --git a/src/transport/wire.rs b/src/transport/wire.rs
index 87063d2..817437a 100644
--- a/src/transport/wire.rs
+++ b/src/transport/wire.rs
@@ -14,7 +14,7 @@ use super::{MessageKind, ParsedMessage};
/// The wire format's version, the first byte of every payload. Bump it with every change
/// to a message type or a codec (`golden_bytes` fails then).
-pub(crate) const VERSION: u8 = 8;
+pub(crate) const VERSION: u8 = 9;
/// What the second byte of a payload says.
fn tag(kind: MessageKind) -> u8 {
@@ -422,6 +422,7 @@ pub(crate) fn samples() -> Vec<ParsedMessage> {
others: vec![crate::transport::OtherPath {
transport: "mycelium".into(),
rtt_us: None,
+ relayed: false,
}],
}],
}),
@@ -595,7 +596,7 @@ mod tests {
});
assert_eq!(
hex(&encode(&hello).unwrap()),
- format!("080097a5737761726d{epoch_hex}01c403010203c3{}07", run("04"))
+ format!("090097a5737761726d{epoch_hex}01c403010203c3{}07", run("04"))
);
let reply = ParsedMessage::HelloReply(HelloReply {
@@ -615,7 +616,7 @@ mod tests {
assert_eq!(
hex(&encode(&reply).unwrap()),
format!(
- "080199a5737761726d9192a17091a161{epoch_hex}01c400c2{}05{}",
+ "090199a5737761726d9192a17091a161{epoch_hex}01c400c2{}05{}",
run("04"),
run("06")
)
@@ -630,7 +631,7 @@ mod tests {
});
assert_eq!(
hex(&encode(&status).unwrap()),
- format!("0803959290c400{epoch_hex}01c3c2")
+ format!("0903959290c400{epoch_hex}01c3c2")
);
let who = ParsedMessage::Who(IdentityProof {
@@ -639,7 +640,7 @@ mod tests {
});
assert_eq!(
hex(&encode(&who).unwrap()),
- format!("080692c420{}c440{}", "08".repeat(32), "09".repeat(64))
+ format!("090692c420{}c440{}", "08".repeat(32), "09".repeat(64))
);
let ParsedMessage::Me(proof) = decode(
&encode(&ParsedMessage::Me(IdentityProof {
@@ -653,10 +654,10 @@ mod tests {
};
assert_eq!((proof.key, proof.signature), ([8; 32], [9; 64]));
- assert_eq!(hex(&encode(&ParsedMessage::Ping(7)).unwrap()), "080807");
+ assert_eq!(hex(&encode(&ParsedMessage::Ping(7)).unwrap()), "090807");
assert_eq!(
hex(&encode(&ParsedMessage::Pong(u64::MAX)).unwrap()),
- "0809cfffffffffffffffff"
+ "0909cfffffffffffffffff"
);
let ParsedMessage::Pong(nonce) = decode(&[VERSION, 9, 0x2a]).unwrap() else {
panic!("not a PONG");