feat: --debug-tap for relay packet header logging
Some checks failed
Mirror to GitHub / mirror (push) Failing after 38s
Build Release Binaries / build-amd64 (push) Failing after 1m57s

Adds --debug-tap <room> flag (or debug_tap in TOML config) that logs
every media packet's header metadata passing through a room. Use '*'
for all rooms.

Output (via tracing target "debug_tap"):
  TAP room=... dir=in addr=... seq=31 codec=Opus24k ts=520
      fec_block=5 fec_sym=1 repair=false len=65 fan_out=1

Shows: direction, source address, sequence number, codec ID, timestamp,
FEC block/symbol, repair flag, payload size, and fan-out count.
No decryption needed — headers are not encrypted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-08 06:34:22 +04:00
parent 7271942c6a
commit ea51d068e6
3 changed files with 58 additions and 1 deletions

View File

@@ -63,6 +63,9 @@ pub struct RelayConfig {
/// Federation peer relays.
#[serde(default)]
pub peers: Vec<PeerConfig>,
/// Debug tap: log packet headers for matching rooms ("*" = all rooms).
/// Activated via --debug-tap <room> or debug_tap = "room" in TOML.
pub debug_tap: Option<String>,
}
impl Default for RelayConfig {
@@ -82,6 +85,7 @@ impl Default for RelayConfig {
ws_port: None,
static_dir: None,
peers: Vec::new(),
debug_tap: None,
}
}
}

View File

@@ -104,6 +104,12 @@ fn parse_args() -> RelayConfig {
args.get(i).expect("--static-dir requires a directory path").to_string(),
);
}
"--debug-tap" => {
i += 1;
config.debug_tap = Some(
args.get(i).expect("--debug-tap requires a room name (or '*' for all)").to_string(),
);
}
"--mesh-status" => {
// Print mesh table from a fresh registry and exit.
// In practice this is useful after the relay has been running;
@@ -126,6 +132,7 @@ fn parse_args() -> RelayConfig {
eprintln!(" --probe-mesh Enable mesh mode (mark config flag, probes all --probe targets).");
eprintln!(" --mesh-status Print mesh health table and exit (diagnostic).");
eprintln!(" --trunking Enable trunk batching for outgoing media in room mode.");
eprintln!(" --debug-tap <room> Log packet headers for a room ('*' for all rooms).");
eprintln!(" --ws-port <port> WebSocket listener port for browser clients (e.g., 8080).");
eprintln!(" --static-dir <dir> Directory to serve static files from (HTML/JS/WASM).");
eprintln!();
@@ -372,6 +379,9 @@ async fn main() -> anyhow::Result<()> {
} else {
info!("auth disabled — any client can connect (use --auth-url to enable)");
}
if let Some(ref tap) = config.debug_tap {
info!(filter = %tap, "debug tap enabled — logging packet headers");
}
info!("Listening for connections...");
@@ -388,6 +398,7 @@ async fn main() -> anyhow::Result<()> {
let relay_seed_bytes = relay_seed.0;
let metrics = metrics.clone();
let trunking_enabled = config.trunking_enabled;
let debug_tap = config.debug_tap.as_ref().map(|filter| room::DebugTap { room_filter: filter.clone() });
let presence = presence.clone();
let route_resolver = route_resolver.clone();
let federation_mgr = federation_mgr.clone();
@@ -675,6 +686,7 @@ async fn main() -> anyhow::Result<()> {
metrics.clone(),
&session_id_str,
trunking_enabled,
debug_tap,
).await;
// Participant disconnected — clean up presence + per-session metrics

View File

@@ -18,6 +18,38 @@ use wzp_proto::MediaTransport;
use crate::metrics::RelayMetrics;
use crate::trunk::TrunkBatcher;
/// Debug tap: logs packet metadata for matching rooms.
#[derive(Clone)]
pub struct DebugTap {
/// Room name filter ("*" = all rooms, or specific room name/hash).
pub room_filter: String,
}
impl DebugTap {
pub fn matches(&self, room_name: &str) -> bool {
self.room_filter == "*" || self.room_filter == room_name
}
pub fn log_packet(&self, room: &str, dir: &str, addr: &std::net::SocketAddr, pkt: &wzp_proto::MediaPacket, fan_out: usize) {
let h = &pkt.header;
info!(
target: "debug_tap",
room = %room,
dir = dir,
addr = %addr,
seq = h.seq,
codec = ?h.codec_id,
ts = h.timestamp,
fec_block = h.fec_block,
fec_sym = h.fec_symbol,
repair = h.is_repair,
len = pkt.payload.len(),
fan_out,
"TAP"
);
}
}
/// Unique participant ID within a room.
pub type ParticipantId = u64;
@@ -477,6 +509,7 @@ pub async fn run_participant(
metrics: Arc<RelayMetrics>,
session_id: &str,
trunking_enabled: bool,
debug_tap: Option<DebugTap>,
) {
if trunking_enabled {
run_participant_trunked(
@@ -485,7 +518,7 @@ pub async fn run_participant(
.await;
} else {
run_participant_plain(
room_mgr, room_name, participant_id, transport, metrics, session_id,
room_mgr, room_name, participant_id, transport, metrics, session_id, debug_tap,
)
.await;
}
@@ -499,6 +532,7 @@ async fn run_participant_plain(
transport: Arc<wzp_transport::QuinnTransport>,
metrics: Arc<RelayMetrics>,
session_id: &str,
debug_tap: Option<DebugTap>,
) {
let addr = transport.connection().remote_address();
let mut packets_forwarded = 0u64;
@@ -572,6 +606,13 @@ async fn run_participant_plain(
);
}
// Debug tap: log packet metadata
if let Some(ref tap) = debug_tap {
if tap.matches(&room_name) {
tap.log_packet(&room_name, "in", &addr, &pkt, others.len());
}
}
// Forward to all others
let fwd_start = std::time::Instant::now();
let pkt_bytes = pkt.payload.len() as u64;