1use super::*;
2
3impl TransportEngine {
4 pub fn lowest_interface_bitrate(&self) -> Option<u64> {
9 super::path_requests::lowest_interface_bitrate(&self.interfaces)
10 }
11
12 pub fn medium_path_timeout(&self) -> f64 {
18 super::path_requests::medium_path_timeout(&self.interfaces)
19 }
20
21 pub fn path_table_entries(&self) -> impl Iterator<Item = (&[u8; 16], &PathEntry)> {
22 self.path_table
23 .iter()
24 .filter_map(|(k, ps)| ps.primary().map(|e| (k, e)))
25 }
26
27 pub fn path_table_sets(&self) -> impl Iterator<Item = (&[u8; 16], &PathSet)> {
28 self.path_table.iter()
29 }
30
31 pub fn interface_count(&self) -> usize {
32 self.interfaces.len()
33 }
34
35 pub fn link_table_count(&self) -> usize {
36 self.link_table.len()
37 }
38
39 pub fn active_link_count(&self) -> usize {
40 self.link_table
41 .values()
42 .filter(|entry| entry.validated)
43 .count()
44 }
45
46 pub fn is_unvalidated_link_packet(&self, packet: &crate::packet::RawPacket) -> bool {
47 packet.flags.packet_type != crate::constants::PACKET_TYPE_ANNOUNCE
48 && packet.flags.packet_type != crate::constants::PACKET_TYPE_LINKREQUEST
49 && packet.context != crate::constants::CONTEXT_LRPROOF
50 && self
51 .link_table
52 .get(&packet.destination_hash)
53 .is_some_and(|entry| !entry.validated)
54 }
55
56 pub fn is_packet_filter_protocol_violation(
58 &self,
59 packet: &crate::packet::RawPacket,
60 interface: InterfaceId,
61 ) -> bool {
62 let mut hops = packet.hops.saturating_add(1);
63 if self.interface_is_local_client(interface) {
64 hops = hops.saturating_sub(1);
65 }
66 if packet.flags.packet_type == crate::constants::PACKET_TYPE_ANNOUNCE {
67 return packet.flags.destination_type != crate::constants::DESTINATION_SINGLE;
68 }
69 (packet.flags.destination_type == crate::constants::DESTINATION_PLAIN
70 || packet.flags.destination_type == crate::constants::DESTINATION_GROUP)
71 && hops > 1
72 }
73
74 pub fn path_table_count(&self) -> usize {
75 self.path_table.len()
76 }
77
78 pub fn announce_table_count(&self) -> usize {
79 self.announce_table.len()
80 }
81
82 pub fn reverse_table_count(&self) -> usize {
83 self.reverse_table.len()
84 }
85
86 pub fn held_announces_count(&self) -> usize {
87 self.held_announces.len()
88 }
89
90 pub fn packet_hashlist_len(&self) -> usize {
91 self.packet_hashlist.len()
92 }
93
94 pub fn prefetch_packet_hash(&self, packet_hash: &[u8; 32]) {
98 self.packet_hashlist.prefetch(packet_hash);
99 }
100
101 #[doc(hidden)]
107 pub fn forget_packet_hash(&mut self, packet_hash: &[u8; 32]) -> bool {
108 self.packet_hashlist.remove(packet_hash)
109 }
110
111 pub fn announce_sig_cache_len(&self) -> usize {
112 self.announce_sig_cache.len()
113 }
114
115 pub fn rate_limiter_count(&self) -> usize {
116 self.rate_limiter.len()
117 }
118
119 pub fn blackholed_count(&self) -> usize {
120 self.blackholed_identities.len()
121 }
122
123 pub fn tunnel_count(&self) -> usize {
124 self.tunnel_table.len()
125 }
126
127 pub fn discovery_pr_tags_count(&self) -> usize {
128 self.discovery_pr_tags.len()
129 }
130
131 #[cfg(test)]
132 pub(crate) fn has_discovery_pr_tag(&self, unique_tag: &[u8; 32]) -> bool {
133 self.discovery_pr_tag_set.contains(unique_tag)
134 }
135
136 pub fn discovery_path_requests_count(&self) -> usize {
137 self.discovery_path_requests.len()
138 }
139
140 pub fn announce_queue_count(&self) -> usize {
141 self.announce_queues.queue_count()
142 }
143
144 pub fn nonempty_announce_queue_count(&self) -> usize {
145 self.announce_queues.nonempty_queue_count()
146 }
147
148 pub fn queued_announce_count(&self) -> usize {
149 self.announce_queues.total_queued_announces()
150 }
151
152 pub fn queued_announce_bytes(&self) -> usize {
153 self.announce_queues.total_queued_bytes()
154 }
155
156 pub fn announce_queue_interface_cap_drop_count(&self) -> u64 {
157 self.announce_queues.interface_cap_drop_count()
158 }
159
160 pub fn local_destinations_count(&self) -> usize {
161 self.local_destinations.len()
162 }
163
164 pub fn rate_limiter(&self) -> &AnnounceRateLimiter {
165 &self.rate_limiter
166 }
167
168 pub fn interface_info(&self, id: &InterfaceId) -> Option<&InterfaceInfo> {
169 self.interfaces.get(id)
170 }
171
172 pub fn redirect_path(&mut self, dest_hash: &[u8; 16], interface: InterfaceId, now: f64) {
173 if let Some(entry) = self
174 .path_table
175 .get_mut(dest_hash)
176 .and_then(|ps| ps.primary_mut())
177 {
178 entry.receiving_interface = interface;
179 entry.next_hop = *dest_hash;
183 entry.hops = 1;
184 entry.timestamp = now;
185 entry.expires = now + 3600.0;
186 } else {
187 self.upsert_path_destination(
188 *dest_hash,
189 PathEntry {
190 timestamp: now,
191 next_hop: *dest_hash,
192 hops: 1,
193 expires: now + 3600.0,
194 random_blobs: Vec::new(),
195 receiving_interface: interface,
196 packet_hash: [0u8; 32],
197 announce_raw: None,
198 },
199 now,
200 );
201 }
202 }
203
204 pub fn inject_path(&mut self, dest_hash: [u8; 16], entry: PathEntry) {
205 self.upsert_path_destination(dest_hash, entry.clone(), entry.timestamp);
206 }
207
208 pub fn drop_path(&mut self, dest_hash: &[u8; 16]) -> bool {
209 self.path_table.remove(dest_hash).is_some()
210 }
211
212 pub fn drop_all_via(&mut self, transport_hash: &[u8; 16]) -> usize {
213 let mut removed = 0usize;
214 for ps in self.path_table.values_mut() {
215 let before = ps.len();
216 ps.retain(|entry| &entry.next_hop != transport_hash);
217 removed += before - ps.len();
218 }
219 self.path_table.retain(|_, ps| !ps.is_empty());
220 removed
221 }
222
223 pub fn drop_paths_for_interface(&mut self, interface: InterfaceId) -> usize {
224 let mut removed = 0usize;
225 let mut cleared_destinations = Vec::new();
226 for (dest_hash, ps) in self.path_table.iter_mut() {
227 let before = ps.len();
228 ps.retain(|entry| entry.receiving_interface != interface);
229 if ps.is_empty() {
230 cleared_destinations.push(*dest_hash);
231 }
232 removed += before - ps.len();
233 }
234 self.path_table.retain(|_, ps| !ps.is_empty());
235 for dest_hash in cleared_destinations {
236 self.path_states.remove(&dest_hash);
237 }
238 removed
239 }
240
241 pub fn drop_reverse_for_interface(&mut self, interface: InterfaceId) -> usize {
242 let before = self.reverse_table.len();
243 self.reverse_table.retain(|_, entry| {
244 entry.receiving_interface != interface && entry.outbound_interface != interface
245 });
246 before - self.reverse_table.len()
247 }
248
249 pub fn drop_links_for_interface(&mut self, interface: InterfaceId) -> usize {
250 let before = self.link_table.len();
251 self.link_table.retain(|_, entry| {
252 entry.next_hop_interface != interface && entry.received_interface != interface
253 });
254 before - self.link_table.len()
255 }
256
257 pub fn drop_announce_queues(&mut self) {
258 self.announce_table.clear();
259 self.held_announces.clear();
260 self.announce_queues = AnnounceQueues::new(self.config.announce_queue_max_interfaces);
261 self.ingress_control.clear();
262 }
263
264 pub fn void_queues(&mut self) {
265 self.drop_announce_queues();
266 self.reverse_table.clear();
267 }
268
269 pub fn identity_hash(&self) -> Option<&[u8; 16]> {
270 self.config.identity_hash.as_ref()
271 }
272
273 pub fn transport_enabled(&self) -> bool {
274 self.config.transport_enabled
275 }
276
277 pub fn config(&self) -> &TransportConfig {
278 &self.config
279 }
280
281 pub fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
282 self.config.packet_hashlist_max_entries = max_entries;
283 let seed = self.packet_hashlist.hashing_seed();
284 self.packet_hashlist =
285 PacketHashlist::with_allocation(max_entries, self.config.packet_hashlist_allocation);
286 if let Some(seed) = seed {
287 self.packet_hashlist.seed_hashing(seed);
288 }
289 }
290
291 pub fn get_path_table(&self, max_hops: Option<u8>) -> Vec<PathTableRow> {
292 let mut result = Vec::new();
293 for (dest_hash, ps) in self.path_table.iter() {
294 if let Some(entry) = ps.primary() {
295 if let Some(max) = max_hops {
296 if entry.hops > max {
297 continue;
298 }
299 }
300 let iface_name = self
301 .interfaces
302 .get(&entry.receiving_interface)
303 .map(|i| i.name.clone())
304 .unwrap_or_else(|| {
305 alloc::format!("Interface({})", entry.receiving_interface.0)
306 });
307 result.push((
308 *dest_hash,
309 entry.timestamp,
310 entry.next_hop,
311 entry.hops,
312 entry.expires,
313 iface_name,
314 ));
315 }
316 }
317 result
318 }
319
320 pub fn get_rate_table(&self) -> Vec<RateTableRow> {
321 self.rate_limiter
322 .entries()
323 .map(|(hash, entry)| {
324 (
325 *hash,
326 entry.last,
327 entry.rate_violations,
328 entry.blocked_until,
329 entry.timestamps.clone(),
330 )
331 })
332 .collect()
333 }
334
335 pub fn get_blackholed(&self) -> Vec<([u8; 16], f64, f64, Option<alloc::string::String>)> {
336 self.blackholed_entries()
337 .map(|(hash, entry)| (*hash, entry.created, entry.expires, entry.reason.clone()))
338 .collect()
339 }
340
341 pub fn active_destination_hashes(&self) -> alloc::collections::BTreeSet<[u8; 16]> {
342 self.path_table.keys().copied().collect()
343 }
344
345 pub fn path_destination_cap_evict_count(&self) -> usize {
346 self.path_destination_cap_evict_count
347 }
348
349 pub fn active_packet_hashes(&self) -> Vec<[u8; 32]> {
350 let mut hashes: Vec<[u8; 32]> = self
351 .path_table
352 .values()
353 .flat_map(|ps| ps.iter().map(|p| p.packet_hash))
354 .collect();
355
356 hashes.extend(
357 self.tunnel_table
358 .iter()
359 .flat_map(|(_, tunnel)| tunnel.paths.values().map(|p| p.packet_hash)),
360 );
361 hashes.sort_unstable();
362 hashes.dedup();
363 hashes
364 }
365
366 pub fn cull_rate_limiter(
367 &mut self,
368 active: &alloc::collections::BTreeSet<[u8; 16]>,
369 now: f64,
370 ttl_secs: f64,
371 ) -> usize {
372 self.rate_limiter.cull_stale(active, now, ttl_secs)
373 }
374
375 pub fn update_interface_freq(&mut self, id: InterfaceId, ia_freq: f64) {
376 if let Some(info) = self.interfaces.get_mut(&id) {
377 info.ia_freq = ia_freq;
378 }
379 }
380
381 pub fn update_interface_freqs(
382 &mut self,
383 id: InterfaceId,
384 ia_freq: f64,
385 ip_freq: f64,
386 op_freq: f64,
387 op_samples: usize,
388 ) {
389 if let Some(info) = self.interfaces.get_mut(&id) {
390 info.ia_freq = ia_freq;
391 info.ip_freq = ip_freq;
392 info.op_freq = op_freq;
393 info.op_samples = op_samples;
394 }
395 }
396
397 pub fn held_announce_count(&self, interface: &InterfaceId) -> usize {
398 self.ingress_control.held_count(interface)
399 }
400
401 pub fn burst_active(&self, interface: &InterfaceId) -> bool {
402 self.ingress_control.burst_active(interface)
403 }
404
405 pub fn burst_activated(&self, interface: &InterfaceId) -> f64 {
406 self.ingress_control.burst_activated(interface)
407 }
408
409 pub fn pr_burst_active(&self, interface: &InterfaceId) -> bool {
410 self.ingress_control.pr_burst_active(interface)
411 }
412
413 pub fn pr_burst_activated(&self, interface: &InterfaceId) -> f64 {
414 self.ingress_control.pr_burst_activated(interface)
415 }
416
417 #[cfg(test)]
418 #[allow(dead_code)]
419 pub(crate) fn path_table(&self) -> &BTreeMap<[u8; 16], PathSet> {
420 &self.path_table
421 }
422
423 #[cfg(test)]
424 #[allow(dead_code)]
425 pub(crate) fn announce_table(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
426 &self.announce_table
427 }
428
429 #[cfg(test)]
430 #[allow(dead_code)]
431 pub(crate) fn held_announces(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
432 &self.held_announces
433 }
434
435 #[cfg(test)]
436 #[allow(dead_code)]
437 pub(crate) fn announce_retained_bytes(&self) -> usize {
438 self.announce_retained_bytes_total()
439 }
440
441 pub fn seed_table_hashing(&mut self, rng: &mut dyn Rng) {
448 let mut seeds = [0u8; 16];
449 rng.fill_bytes(&mut seeds);
450 let (reverse_seed, dedup_seed) = seeds.split_at(8);
451 let seed = |bytes: &[u8]| u64::from_le_bytes(bytes.try_into().expect("8 bytes"));
452 self.packet_hashlist.seed_hashing(seed(dedup_seed));
453 let hasher = foldhash::fast::FixedState::with_seed(seed(reverse_seed));
454 let mut table =
455 tables::ReverseTable::with_capacity_and_hasher(self.reverse_table.len(), hasher);
456 table.extend(self.reverse_table.drain());
457 self.reverse_table = table;
458 }
459
460 #[cfg(test)]
461 #[allow(dead_code)]
462 pub(crate) fn reverse_table(&self) -> &tables::ReverseTable {
463 &self.reverse_table
464 }
465}