1use super::*;
2
3impl TransportEngine {
4 pub fn handle_path_request(
5 &mut self,
6 data: &[u8],
7 interface_id: InterfaceId,
8 now: f64,
9 ) -> Vec<TransportAction> {
10 let Some(ctx) = self.parse_path_request(data, interface_id, now) else {
11 return Vec::new();
12 };
13 if self.local_destinations.contains_key(&ctx.destination_hash) {
14 return Vec::new();
15 }
16 if self.config.transport_enabled && self.handle_known_path_request(&ctx) {
17 return Vec::new();
18 }
19 if self.config.transport_enabled {
20 return self.handle_discovery_path_request(&ctx);
21 }
22 Vec::new()
23 }
24
25 fn parse_path_request<'a>(
26 &mut self,
27 data: &'a [u8],
28 interface_id: InterfaceId,
29 now: f64,
30 ) -> Option<PathRequestCtx<'a>> {
31 if data.len() < 16 {
32 return None;
33 }
34
35 let mut destination_hash = [0u8; 16];
36 destination_hash.copy_from_slice(&data[..16]);
37
38 let tag_bytes = if data.len() > 32 {
39 Some(&data[32..])
40 } else if data.len() > 16 {
41 Some(&data[16..])
42 } else {
43 None
44 }?;
45
46 let tag_len = tag_bytes.len().min(16);
47 let mut unique_tag = [0u8; 32];
48 unique_tag[..16].copy_from_slice(&destination_hash);
49 unique_tag[16..16 + tag_len].copy_from_slice(&tag_bytes[..tag_len]);
50 if !self.insert_discovery_pr_tag(unique_tag) {
51 return None;
52 }
53
54 Some(PathRequestCtx {
55 tag: &tag_bytes[..tag_len],
56 interface_id,
57 now,
58 destination_hash,
59 })
60 }
61
62 fn handle_known_path_request(&mut self, ctx: &PathRequestCtx<'_>) -> bool {
63 let Some(path) = self
64 .path_table
65 .get(&ctx.destination_hash)
66 .and_then(|ps| ps.primary())
67 .cloned()
68 else {
69 return false;
70 };
71
72 if let Some(recv_info) = self.interfaces.get(&ctx.interface_id) {
73 if recv_info.mode == constants::MODE_ROAMING
74 && path.receiving_interface == ctx.interface_id
75 {
76 return true;
77 }
78 }
79
80 let Some(raw) = path.announce_raw.as_ref() else {
81 return false;
82 };
83 if let Some(existing) = self.announce_table.remove(&ctx.destination_hash) {
84 self.insert_held_announce(ctx.destination_hash, existing, ctx.now);
85 }
86 let retransmit_timeout = if let Some(iface_info) = self.interfaces.get(&ctx.interface_id) {
87 let base = ctx.now + constants::PATH_REQUEST_GRACE;
88 if iface_info.mode == constants::MODE_ROAMING {
89 base + constants::PATH_REQUEST_RG
90 } else {
91 base
92 }
93 } else {
94 ctx.now + constants::PATH_REQUEST_GRACE
95 };
96
97 let Ok(parsed) = RawPacket::unpack(raw) else {
98 return false;
99 };
100
101 let entry = AnnounceEntry {
102 timestamp: ctx.now,
103 retransmit_timeout,
104 retries: constants::PATHFINDER_R,
105 received_from: path.next_hop,
106 hops: path.hops,
107 packet_raw: raw.clone(),
108 packet_data: parsed.data,
109 destination_hash: ctx.destination_hash,
110 context_flag: parsed.flags.context_flag,
111 local_rebroadcasts: 0,
112 block_rebroadcasts: true,
113 attached_interface: Some(ctx.interface_id),
114 };
115
116 self.insert_announce_entry(ctx.destination_hash, entry, ctx.now);
117 true
118 }
119
120 fn handle_discovery_path_request(&mut self, ctx: &PathRequestCtx<'_>) -> Vec<TransportAction> {
121 let Some((mode, recursive_prs, ingress_control, ip_freq, started)) =
122 self.interfaces.get(&ctx.interface_id).map(|info| {
123 (
124 info.mode,
125 info.recursive_prs,
126 info.ingress_control,
127 info.ip_freq,
128 info.started,
129 )
130 })
131 else {
132 return Vec::new();
133 };
134
135 let should_discover = recursive_prs || constants::DISCOVER_PATHS_FOR.contains(&mode);
136 if !should_discover {
137 return Vec::new();
138 }
139
140 if self.ingress_control.should_ingress_limit_pr(
141 ctx.interface_id,
142 &ingress_control,
143 ip_freq,
144 started,
145 ctx.now,
146 ) {
147 return Vec::new();
148 }
149
150 let egress_candidates: Vec<_> = self
151 .interfaces
152 .values()
153 .filter(|info| info.id != ctx.interface_id && info.out_capable)
154 .map(|info| {
155 (
156 info.id,
157 info.ingress_control,
158 info.op_freq,
159 info.op_samples,
160 info.bitrate,
161 info.airtime_profile,
162 info.announce_cap,
163 )
164 })
165 .collect();
166
167 let Some((path_request_raw, path_request_len)) = build_path_request_packet(
168 &ctx.destination_hash,
169 self.config.identity_hash.as_ref(),
170 ctx.tag,
171 ) else {
172 return Vec::new();
173 };
174
175 let mut actions = Vec::new();
176 for (id, ingress_control, op_freq, op_samples, bitrate, airtime_profile, announce_cap) in
177 egress_candidates
178 {
179 if self.ingress_control.should_egress_limit_pr(
180 id,
181 &ingress_control,
182 op_freq,
183 op_samples,
184 ) || self
185 .announce_queues
186 .blocks_recursive_path_request(id, ctx.now)
187 {
188 continue;
189 }
190
191 self.announce_queues.reserve_recursive_path_request(
192 id,
193 path_request_len + constants::HEADER_MINSIZE,
194 ctx.now,
195 bitrate,
196 airtime_profile,
197 announce_cap,
198 );
199 actions.push(TransportAction::SendOnInterface {
200 interface: id,
201 raw: path_request_raw.clone().into(),
202 });
203 }
204
205 if !actions.is_empty() {
206 self.discovery_path_requests.insert(
207 ctx.destination_hash,
208 DiscoveryPathRequest {
209 timestamp: ctx.now,
210 requesting_interface: ctx.interface_id,
211 },
212 );
213 }
214
215 actions
216 }
217}
218
219fn build_path_request_packet(
220 destination_hash: &[u8; 16],
221 transport_identity_hash: Option<&[u8; 16]>,
222 tag: &[u8],
223) -> Option<(Vec<u8>, usize)> {
224 let mut data = Vec::with_capacity(16 + transport_identity_hash.map_or(0, |_| 16) + tag.len());
225 data.extend_from_slice(destination_hash);
226 if let Some(identity_hash) = transport_identity_hash {
227 data.extend_from_slice(identity_hash);
228 }
229 data.extend_from_slice(tag);
230
231 let flags = crate::packet::PacketFlags {
232 header_type: constants::HEADER_1,
233 context_flag: constants::FLAG_UNSET,
234 transport_type: constants::TRANSPORT_BROADCAST,
235 destination_type: constants::DESTINATION_PLAIN,
236 packet_type: constants::PACKET_TYPE_DATA,
237 };
238 let path_request_dest =
239 crate::destination::destination_hash("rnstransport", &["path", "request"], None);
240
241 let data_len = data.len();
242 RawPacket::pack(
243 flags,
244 0,
245 &path_request_dest,
246 None,
247 constants::CONTEXT_NONE,
248 &data,
249 )
250 .ok()
251 .map(|packet| (packet.raw, data_len))
252}