feat(obs): distinguish one-way broadcast flow
The client previously reported "Broadcast traffic flowing" as soon as either broadcast TX or RX was nonzero. During the MVP DHCP/ARP proof, one-way broadcast is useful but weaker evidence than bidirectional broadcast. Keep the existing healthy message for two-way broadcast, but report outbound-only broadcast as a warning that the client is still waiting for a LAN broadcast reply, and report inbound-only broadcast separately. This makes the Windows client status lines more precise when DHCP is stuck. Test Plan: - cargo test -p lanparty-obs - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check - git diff --cached --check Refs: MVP Windows DHCP diagnostics
This commit is contained in:
@@ -271,7 +271,8 @@ after bridging starts become visible in later status lines, preferring a
|
|||||||
non-link-local IPv4 address when Windows reports several TAP addresses. Each
|
non-link-local IPv4 address when Windows reports several TAP addresses. Each
|
||||||
snapshot also emits short user-facing lines such as relay/gateway connection status,
|
snapshot also emits short user-facing lines such as relay/gateway connection status,
|
||||||
relay-route and TAP readiness warnings, DHCP address presence, relay RTT, and
|
relay-route and TAP readiness warnings, DHCP address presence, relay RTT, and
|
||||||
broadcast-flow confirmation when those signals are observed. Malformed frames
|
broadcast-flow confirmation. One-way broadcast diagnostics distinguish frames
|
||||||
|
sent toward the LAN from broadcast frames received back from the LAN. Malformed frames
|
||||||
read from TAP, invalid or unauthorized source-MAC frames, L2 control-plane
|
read from TAP, invalid or unauthorized source-MAC frames, L2 control-plane
|
||||||
traffic, remote VLAN tags, DHCP server replies, IPv6 Router Advertisements, IPv6
|
traffic, remote VLAN tags, DHCP server replies, IPv6 Router Advertisements, IPv6
|
||||||
fragments, jumbo frames, and TAP frames whose encoded datagrams exceed the
|
fragments, jumbo frames, and TAP frames whose encoded datagrams exceed the
|
||||||
|
|||||||
@@ -246,6 +246,8 @@ Client health:
|
|||||||
```text
|
```text
|
||||||
Relay RTT: 23 ms
|
Relay RTT: 23 ms
|
||||||
Broadcast traffic flowing
|
Broadcast traffic flowing
|
||||||
|
Broadcast sent toward LAN; waiting for LAN broadcast reply
|
||||||
|
LAN broadcast received
|
||||||
client frame direction=TapToRelay ... action=Forwarded drop_reason=-
|
client frame direction=TapToRelay ... action=Forwarded drop_reason=-
|
||||||
client frame direction=RelayToTap ... action=Forwarded drop_reason=-
|
client frame direction=RelayToTap ... action=Forwarded drop_reason=-
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -447,11 +447,23 @@ impl ClientDiagnostics {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.stats.broadcast_frames_tx() > 0 || self.stats.broadcast_frames_rx() > 0 {
|
match (
|
||||||
diagnostics.push(UserDiagnostic::new(
|
self.stats.broadcast_frames_tx() > 0,
|
||||||
|
self.stats.broadcast_frames_rx() > 0,
|
||||||
|
) {
|
||||||
|
(true, true) => diagnostics.push(UserDiagnostic::new(
|
||||||
UserDiagnosticLevel::Info,
|
UserDiagnosticLevel::Info,
|
||||||
"Broadcast traffic flowing",
|
"Broadcast traffic flowing",
|
||||||
));
|
)),
|
||||||
|
(true, false) => diagnostics.push(UserDiagnostic::new(
|
||||||
|
UserDiagnosticLevel::Warning,
|
||||||
|
"Broadcast sent toward LAN; waiting for LAN broadcast reply",
|
||||||
|
)),
|
||||||
|
(false, true) => diagnostics.push(UserDiagnostic::new(
|
||||||
|
UserDiagnosticLevel::Info,
|
||||||
|
"LAN broadcast received",
|
||||||
|
)),
|
||||||
|
(false, false) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics
|
diagnostics
|
||||||
@@ -723,6 +735,50 @@ mod tests {
|
|||||||
assert_eq!(user_diagnostics[2].level(), UserDiagnosticLevel::Warning);
|
assert_eq!(user_diagnostics[2].level(), UserDiagnosticLevel::Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn distinguishes_one_way_broadcast_diagnostics() {
|
||||||
|
let mut diagnostics = ClientDiagnostics::new(
|
||||||
|
RelayDiagnostics::new(true, true, true),
|
||||||
|
QuicDiagnostics::new(true, Some(1400)),
|
||||||
|
TapDiagnostics::new(
|
||||||
|
true,
|
||||||
|
Some(MacAddr::new([0x02, 1, 2, 3, 4, 5])),
|
||||||
|
Some(1200),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
TunnelStats::default().with_broadcast_frames(1, 0),
|
||||||
|
);
|
||||||
|
let broadcast = diagnostics
|
||||||
|
.user_diagnostics()
|
||||||
|
.into_iter()
|
||||||
|
.find(|diagnostic| diagnostic.message().contains("Broadcast"))
|
||||||
|
.expect("outbound broadcast should produce a diagnostic");
|
||||||
|
assert_eq!(broadcast.level(), UserDiagnosticLevel::Warning);
|
||||||
|
assert_eq!(
|
||||||
|
broadcast.message(),
|
||||||
|
"Broadcast sent toward LAN; waiting for LAN broadcast reply"
|
||||||
|
);
|
||||||
|
|
||||||
|
diagnostics = ClientDiagnostics::new(
|
||||||
|
RelayDiagnostics::new(true, true, true),
|
||||||
|
QuicDiagnostics::new(true, Some(1400)),
|
||||||
|
TapDiagnostics::new(
|
||||||
|
true,
|
||||||
|
Some(MacAddr::new([0x02, 1, 2, 3, 4, 5])),
|
||||||
|
Some(1200),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
TunnelStats::default().with_broadcast_frames(0, 1),
|
||||||
|
);
|
||||||
|
let broadcast = diagnostics
|
||||||
|
.user_diagnostics()
|
||||||
|
.into_iter()
|
||||||
|
.find(|diagnostic| diagnostic.message().contains("broadcast"))
|
||||||
|
.expect("inbound broadcast should produce a diagnostic");
|
||||||
|
assert_eq!(broadcast.level(), UserDiagnosticLevel::Info);
|
||||||
|
assert_eq!(broadcast.message(), "LAN broadcast received");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reports_link_local_tap_ipv4_as_waiting_for_lan_dhcp() {
|
fn reports_link_local_tap_ipv4_as_waiting_for_lan_dhcp() {
|
||||||
let diagnostic = tap_ip_user_diagnostic(Some("169.254.10.20".parse().unwrap())).unwrap();
|
let diagnostic = tap_ip_user_diagnostic(Some("169.254.10.20".parse().unwrap())).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user