manycastr/worker/inbound/
trace.rs1use crate::custom_module::manycastr::reply::ReplyData;
2use crate::custom_module::manycastr::{Address, Reply, TraceReply};
3use crate::net::{ICMPPacket, IPv4Packet};
4use crate::worker::inbound::ReplyMeta;
5use crate::worker::inbound::ping::parse_icmp;
6use crate::worker::trace_codec::TraceTag;
7
8pub fn parse_trace(packet_bytes: &[u8], m_id: u32, meta: ReplyMeta) -> Option<Reply> {
28 let ReplyMeta { src, ttl, rx_time } = meta;
29 let is_v6 = src.is_v6();
30
31 let (time_exceeded, dest_unreachable, icmp_start) = if is_v6 {
33 (3u8, 1u8, 0usize)
34 } else {
35 (11u8, 3u8, 20usize)
36 };
37
38 match packet_bytes.get(icmp_start) {
40 Some(&t) if t == time_exceeded || t == dest_unreachable => {}
41 _ => return parse_icmp(packet_bytes, m_id, true, meta),
42 }
43
44 let (hop_addr, hop_ttl) = if is_v6 {
46 (src, ttl)
47 } else {
48 let ip = IPv4Packet::from(packet_bytes);
49 (Address::from(ip.src), ip.ttl as u32)
50 };
51
52 if packet_bytes.len() < icmp_start + 8 {
54 return parse_icmp(packet_bytes, m_id, true, meta);
55 }
56
57 let icmp = ICMPPacket::from(&packet_bytes[icmp_start..]);
59 let quoted = parse_quoted_probe(&icmp.payload, is_v6)?;
60
61 let t = quoted.transport;
63 let tag = match quoted.protocol {
64 1 | 58 => TraceTag::decode_split(
66 u16::from_be_bytes([t[4], t[5]]),
67 u16::from_be_bytes([t[6], t[7]]),
68 ),
69 17 => TraceTag::decode_split(quoted.id_field, u16::from_be_bytes([t[6], t[7]])),
71 6 => TraceTag::decode_tcp_seq(u32::from_be_bytes([t[4], t[5], t[6], t[7]])),
73 _ => return None,
74 };
75
76 Some(make_trace_reply(
77 hop_addr,
78 hop_ttl,
79 rx_time,
80 tag.ts14 as u64,
81 tag.worker_id,
82 quoted.dst,
83 tag.ttl as u32,
84 ))
85}
86
87struct QuotedProbe<'a> {
89 protocol: u8,
91 dst: Address,
93 id_field: u16,
95 transport: &'a [u8],
97}
98
99fn parse_quoted_probe(payload: &[u8], is_v6: bool) -> Option<QuotedProbe<'_>> {
101 if is_v6 {
102 if payload.len() < 48 {
104 return None;
105 }
106 let id_field = (u32::from_be_bytes(payload[0..4].try_into().ok()?) & 0x000F_FFFF) as u16;
107 Some(QuotedProbe {
108 protocol: payload[6], dst: Address::from(u128::from_be_bytes(payload[24..40].try_into().ok()?)),
110 id_field,
111 transport: &payload[40..],
112 })
113 } else {
114 let ihl = ((*payload.first()? & 0x0F) as usize) * 4;
115 let transport = payload.get(ihl..)?;
116 if transport.len() < 8 {
117 return None;
118 }
119 Some(QuotedProbe {
120 protocol: *payload.get(9)?, dst: Address::from(u32::from_be_bytes(payload.get(16..20)?.try_into().ok()?)),
122 id_field: u16::from_be_bytes([*payload.get(4)?, *payload.get(5)?]), transport,
124 })
125 }
126}
127
128fn make_trace_reply(
130 hop_addr: Address,
131 ttl: u32,
132 rx_time: u64,
133 tx_time: u64,
134 tx_id: u32,
135 trace_dst: Address,
136 hop_count: u32,
137) -> Reply {
138 Reply {
139 reply_data: Some(ReplyData::Trace(TraceReply {
140 hop_addr: Some(hop_addr),
141 ttl,
142 rtt: super::rtt_ms(rx_time, tx_time, super::TxEncoding::Trace14),
143 tx_id,
144 trace_dst: Some(trace_dst),
145 hop_count,
146 })),
147 }
148}