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