use std::net::SocketAddr;
use std::time::Duration;
#[allow(dead_code)]
pub fn timestamp_now() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_micros() as u64
}
pub fn socket_addr_to_bytes(addr: SocketAddr) -> ([u8; 18], u8) {
let mut bytes = [0u8; 18];
match addr {
SocketAddr::V4(v4) => {
bytes[0..4].copy_from_slice(&v4.ip().octets());
bytes[4..6].copy_from_slice(&v4.port().to_be_bytes());
(bytes, 0)
}
SocketAddr::V6(v6) => {
bytes[0..16].copy_from_slice(&v6.ip().octets());
bytes[16..18].copy_from_slice(&v6.port().to_be_bytes());
(bytes, 1)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
#[derive(Default)]
pub struct TraceId(pub [u8; 16]);
impl TraceId {
#[allow(dead_code)]
pub fn new() -> Self {
let mut id = [0u8; 16];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut id);
Self(id)
}
#[allow(dead_code)]
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Event {
pub timestamp: u64,
pub trace_id: TraceId,
pub sequence: u32,
pub _padding: u32,
pub node_id: [u8; 32],
pub event_data: EventData,
}
#[derive(Debug, Clone)]
#[repr(C)]
#[allow(missing_docs)]
pub enum EventData {
ConnInit {
#[allow(dead_code)]
endpoint_bytes: [u8; 18],
#[allow(dead_code)]
addr_type: u8,
_padding: [u8; 45],
},
#[allow(dead_code)]
ConnEstablished {
rtt: u32,
_padding: [u8; 60],
},
#[allow(dead_code)]
StreamOpened {
stream_id: u64,
_padding: [u8; 56],
},
#[allow(dead_code)]
StreamClosed {
stream_id: u64,
error_code: u32,
_padding: [u8; 56],
},
PacketSent {
#[allow(dead_code)]
size: u32,
#[allow(dead_code)]
packet_num: u64,
_padding: [u8; 56],
},
PacketReceived {
#[allow(dead_code)]
size: u32,
#[allow(dead_code)]
packet_num: u64,
_padding: [u8; 56],
},
#[allow(dead_code)]
PacketLost {
packet_num: u64,
_padding: [u8; 56],
},
#[allow(dead_code)]
CandidateDiscovered {
addr_bytes: [u8; 18],
addr_type: u8,
priority: u32,
_padding: [u8; 41],
},
#[allow(dead_code)]
HolePunchingStarted { peer: [u8; 32], _padding: [u8; 32] },
#[allow(dead_code)]
HolePunchingSucceeded {
peer: [u8; 32],
rtt: u32,
_padding: [u8; 28],
},
#[allow(dead_code)]
ObservedAddressSent {
addr_bytes: [u8; 18],
addr_type: u8,
path_id: u32,
_padding: [u8; 41],
},
#[allow(dead_code)]
ObservedAddressReceived {
addr_bytes: [u8; 18],
addr_type: u8,
from_peer: [u8; 32],
_padding: [u8; 13],
},
#[cfg(feature = "trace")]
AppCommand {
app_id: [u8; 4],
cmd: u16,
data: [u8; 42],
_padding: [u8; 16],
},
Custom {
#[allow(dead_code)]
category: u16,
#[allow(dead_code)]
code: u16,
#[allow(dead_code)]
data: [u8; 44],
_padding: [u8; 16],
},
}
impl Default for EventData {
fn default() -> Self {
Self::ConnInit {
endpoint_bytes: [0u8; 18],
addr_type: 0,
_padding: [0u8; 45],
}
}
}
const _: () = {
assert!(std::mem::size_of::<TraceId>() == 16);
};
#[cfg(test)]
mod size_debug {
use super::*;
#[test]
fn print_sizes() {
println!("Event size: {} bytes", std::mem::size_of::<Event>());
println!("EventData size: {} bytes", std::mem::size_of::<EventData>());
println!("TraceId size: {} bytes", std::mem::size_of::<TraceId>());
println!("\nEvent fields:");
println!(" timestamp (u64): {} bytes", std::mem::size_of::<u64>());
println!(
" trace_id (TraceId): {} bytes",
std::mem::size_of::<TraceId>()
);
println!(" sequence (u32): {} bytes", std::mem::size_of::<u32>());
println!(" _padding (u32): {} bytes", std::mem::size_of::<u32>());
println!(
" node_id ([u8; 32]): {} bytes",
std::mem::size_of::<[u8; 32]>()
);
println!(
" event_data (EventData): {} bytes",
std::mem::size_of::<EventData>()
);
let expected = 8 + 16 + 4 + 4 + 32; println!("\nExpected size without EventData: {expected} bytes");
println!("Space for EventData: {} bytes", 128 - expected);
}
}
impl Default for Event {
fn default() -> Self {
Self {
timestamp: 0,
trace_id: TraceId::default(),
sequence: 0,
_padding: 0,
node_id: [0u8; 32],
event_data: EventData::Custom {
category: 0,
code: 0,
data: [0u8; 44],
_padding: [0u8; 16],
},
}
}
}
impl Event {
#[allow(dead_code)]
pub(super) fn new() -> Self {
Self::default()
}
}
impl Event {
#[allow(dead_code)]
pub(super) fn conn_init(endpoint: SocketAddr, trace_id: TraceId) -> Self {
let (endpoint_bytes, addr_type) = socket_addr_to_bytes(endpoint);
Self {
timestamp: crate::tracing::timestamp_now(),
trace_id,
event_data: EventData::ConnInit {
endpoint_bytes,
addr_type,
_padding: [0u8; 45],
},
..Default::default()
}
}
#[allow(dead_code)]
pub(super) fn packet_sent(size: u32, packet_num: u64, trace_id: TraceId) -> Self {
Self {
timestamp: crate::tracing::timestamp_now(),
trace_id,
event_data: EventData::PacketSent {
size,
packet_num,
_padding: [0u8; 56],
},
..Default::default()
}
}
#[allow(dead_code)]
pub(super) fn packet_received(size: u32, packet_num: u64, trace_id: TraceId) -> Self {
Self {
timestamp: crate::tracing::timestamp_now(),
trace_id,
event_data: EventData::PacketReceived {
size,
packet_num,
_padding: [0u8; 56],
},
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_size() {
assert_eq!(std::mem::size_of::<Event>(), 144);
assert_eq!(std::mem::size_of::<EventData>(), 80);
assert_eq!(std::mem::size_of::<TraceId>(), 16);
}
#[test]
fn test_event_creation() {
let trace_id = TraceId::new();
let event = Event::conn_init("127.0.0.1:8080".parse().unwrap(), trace_id);
assert_eq!(event.trace_id, trace_id);
#[cfg(feature = "trace")]
assert!(event.timestamp > 0);
#[cfg(not(feature = "trace"))]
assert_eq!(event.timestamp, 0); }
}