feat: --debug-tap for relay packet header logging
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:
@@ -63,6 +63,9 @@ pub struct RelayConfig {
|
|||||||
/// Federation peer relays.
|
/// Federation peer relays.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub peers: Vec<PeerConfig>,
|
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 {
|
impl Default for RelayConfig {
|
||||||
@@ -82,6 +85,7 @@ impl Default for RelayConfig {
|
|||||||
ws_port: None,
|
ws_port: None,
|
||||||
static_dir: None,
|
static_dir: None,
|
||||||
peers: Vec::new(),
|
peers: Vec::new(),
|
||||||
|
debug_tap: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ fn parse_args() -> RelayConfig {
|
|||||||
args.get(i).expect("--static-dir requires a directory path").to_string(),
|
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" => {
|
"--mesh-status" => {
|
||||||
// Print mesh table from a fresh registry and exit.
|
// Print mesh table from a fresh registry and exit.
|
||||||
// In practice this is useful after the relay has been running;
|
// 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!(" --probe-mesh Enable mesh mode (mark config flag, probes all --probe targets).");
|
||||||
eprintln!(" --mesh-status Print mesh health table and exit (diagnostic).");
|
eprintln!(" --mesh-status Print mesh health table and exit (diagnostic).");
|
||||||
eprintln!(" --trunking Enable trunk batching for outgoing media in room mode.");
|
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!(" --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!(" --static-dir <dir> Directory to serve static files from (HTML/JS/WASM).");
|
||||||
eprintln!();
|
eprintln!();
|
||||||
@@ -372,6 +379,9 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
info!("auth disabled — any client can connect (use --auth-url to enable)");
|
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...");
|
info!("Listening for connections...");
|
||||||
|
|
||||||
@@ -388,6 +398,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let relay_seed_bytes = relay_seed.0;
|
let relay_seed_bytes = relay_seed.0;
|
||||||
let metrics = metrics.clone();
|
let metrics = metrics.clone();
|
||||||
let trunking_enabled = config.trunking_enabled;
|
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 presence = presence.clone();
|
||||||
let route_resolver = route_resolver.clone();
|
let route_resolver = route_resolver.clone();
|
||||||
let federation_mgr = federation_mgr.clone();
|
let federation_mgr = federation_mgr.clone();
|
||||||
@@ -675,6 +686,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
metrics.clone(),
|
metrics.clone(),
|
||||||
&session_id_str,
|
&session_id_str,
|
||||||
trunking_enabled,
|
trunking_enabled,
|
||||||
|
debug_tap,
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
// Participant disconnected — clean up presence + per-session metrics
|
// Participant disconnected — clean up presence + per-session metrics
|
||||||
|
|||||||
@@ -18,6 +18,38 @@ use wzp_proto::MediaTransport;
|
|||||||
use crate::metrics::RelayMetrics;
|
use crate::metrics::RelayMetrics;
|
||||||
use crate::trunk::TrunkBatcher;
|
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.
|
/// Unique participant ID within a room.
|
||||||
pub type ParticipantId = u64;
|
pub type ParticipantId = u64;
|
||||||
|
|
||||||
@@ -477,6 +509,7 @@ pub async fn run_participant(
|
|||||||
metrics: Arc<RelayMetrics>,
|
metrics: Arc<RelayMetrics>,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
trunking_enabled: bool,
|
trunking_enabled: bool,
|
||||||
|
debug_tap: Option<DebugTap>,
|
||||||
) {
|
) {
|
||||||
if trunking_enabled {
|
if trunking_enabled {
|
||||||
run_participant_trunked(
|
run_participant_trunked(
|
||||||
@@ -485,7 +518,7 @@ pub async fn run_participant(
|
|||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
run_participant_plain(
|
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;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -499,6 +532,7 @@ async fn run_participant_plain(
|
|||||||
transport: Arc<wzp_transport::QuinnTransport>,
|
transport: Arc<wzp_transport::QuinnTransport>,
|
||||||
metrics: Arc<RelayMetrics>,
|
metrics: Arc<RelayMetrics>,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
|
debug_tap: Option<DebugTap>,
|
||||||
) {
|
) {
|
||||||
let addr = transport.connection().remote_address();
|
let addr = transport.connection().remote_address();
|
||||||
let mut packets_forwarded = 0u64;
|
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
|
// Forward to all others
|
||||||
let fwd_start = std::time::Instant::now();
|
let fwd_start = std::time::Instant::now();
|
||||||
let pkt_bytes = pkt.payload.len() as u64;
|
let pkt_bytes = pkt.payload.len() as u64;
|
||||||
|
|||||||
Reference in New Issue
Block a user