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, ingress_control, ip_freq, started)) = self
122 .interfaces
123 .get(&ctx.interface_id)
124 .map(|info| (info.mode, info.ingress_control, info.ip_freq, info.started))
125 else {
126 return Vec::new();
127 };
128
129 let should_discover = constants::DISCOVER_PATHS_FOR.contains(&mode);
130 if !should_discover {
131 return Vec::new();
132 }
133
134 if self.ingress_control.should_ingress_limit_pr(
135 ctx.interface_id,
136 &ingress_control,
137 ip_freq,
138 started,
139 ctx.now,
140 ) {
141 return Vec::new();
142 }
143
144 let egress_candidates: Vec<_> = self
145 .interfaces
146 .values()
147 .filter(|info| info.id != ctx.interface_id && info.out_capable)
148 .map(|info| {
149 (
150 info.id,
151 info.ingress_control,
152 info.op_freq,
153 info.op_samples,
154 info.bitrate,
155 info.airtime_profile,
156 info.announce_cap,
157 )
158 })
159 .collect();
160
161 let Some((path_request_raw, path_request_len)) = build_path_request_packet(
162 &ctx.destination_hash,
163 self.config.identity_hash.as_ref(),
164 ctx.tag,
165 ) else {
166 return Vec::new();
167 };
168
169 let mut actions = Vec::new();
170 for (id, ingress_control, op_freq, op_samples, bitrate, airtime_profile, announce_cap) in
171 egress_candidates
172 {
173 if self.ingress_control.should_egress_limit_pr(
174 id,
175 &ingress_control,
176 op_freq,
177 op_samples,
178 ) || self
179 .announce_queues
180 .blocks_recursive_path_request(id, ctx.now)
181 {
182 continue;
183 }
184
185 self.announce_queues.reserve_recursive_path_request(
186 id,
187 path_request_len + constants::HEADER_MINSIZE,
188 ctx.now,
189 bitrate,
190 airtime_profile,
191 announce_cap,
192 );
193 actions.push(TransportAction::SendOnInterface {
194 interface: id,
195 raw: path_request_raw.clone().into(),
196 });
197 }
198
199 if !actions.is_empty() {
200 self.discovery_path_requests.insert(
201 ctx.destination_hash,
202 DiscoveryPathRequest {
203 timestamp: ctx.now,
204 requesting_interface: ctx.interface_id,
205 },
206 );
207 }
208
209 actions
210 }
211}
212
213fn build_path_request_packet(
214 destination_hash: &[u8; 16],
215 transport_identity_hash: Option<&[u8; 16]>,
216 tag: &[u8],
217) -> Option<(Vec<u8>, usize)> {
218 let mut data = Vec::with_capacity(16 + transport_identity_hash.map_or(0, |_| 16) + tag.len());
219 data.extend_from_slice(destination_hash);
220 if let Some(identity_hash) = transport_identity_hash {
221 data.extend_from_slice(identity_hash);
222 }
223 data.extend_from_slice(tag);
224
225 let flags = crate::packet::PacketFlags {
226 header_type: constants::HEADER_1,
227 context_flag: constants::FLAG_UNSET,
228 transport_type: constants::TRANSPORT_BROADCAST,
229 destination_type: constants::DESTINATION_PLAIN,
230 packet_type: constants::PACKET_TYPE_DATA,
231 };
232 let path_request_dest =
233 crate::destination::destination_hash("rnstransport", &["path", "request"], None);
234
235 let data_len = data.len();
236 RawPacket::pack(
237 flags,
238 0,
239 &path_request_dest,
240 None,
241 constants::CONTEXT_NONE,
242 &data,
243 )
244 .ok()
245 .map(|packet| (packet.raw, data_len))
246}