Skip to main content

rns_core/transport/
queries.rs

1use super::*;
2
3impl TransportEngine {
4    /// Return the slowest positive bitrate among registered interfaces.
5    ///
6    /// Returns `None` when no registered interface advertises a usable
7    /// bitrate.
8    pub fn lowest_interface_bitrate(&self) -> Option<u64> {
9        super::path_requests::lowest_interface_bitrate(&self.interfaces)
10    }
11
12    /// Estimate a path-request round-trip timeout for the slowest medium.
13    ///
14    /// The estimate covers two MTU transmissions at the slowest registered
15    /// positive bitrate plus the per-hop link-establishment allowance. It is
16    /// zero when no usable interface bitrate is known.
17    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    /// Classify packet-filter rejections that are peer protocol violations.
57    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    /// Remove a packet hash from deduplication after an interface failover race.
95    ///
96    /// Network drivers should call this when an active-link packet arrives on
97    /// the link's former interface, allowing the same packet to be accepted
98    /// later on its current route.
99    #[doc(hidden)]
100    pub fn forget_packet_hash(&mut self, packet_hash: &[u8; 32]) -> bool {
101        self.packet_hashlist.remove(packet_hash)
102    }
103
104    pub fn announce_sig_cache_len(&self) -> usize {
105        self.announce_sig_cache.len()
106    }
107
108    pub fn rate_limiter_count(&self) -> usize {
109        self.rate_limiter.len()
110    }
111
112    pub fn blackholed_count(&self) -> usize {
113        self.blackholed_identities.len()
114    }
115
116    pub fn tunnel_count(&self) -> usize {
117        self.tunnel_table.len()
118    }
119
120    pub fn discovery_pr_tags_count(&self) -> usize {
121        self.discovery_pr_tags.len()
122    }
123
124    #[cfg(test)]
125    pub(crate) fn has_discovery_pr_tag(&self, unique_tag: &[u8; 32]) -> bool {
126        self.discovery_pr_tag_set.contains(unique_tag)
127    }
128
129    pub fn discovery_path_requests_count(&self) -> usize {
130        self.discovery_path_requests.len()
131    }
132
133    pub fn announce_queue_count(&self) -> usize {
134        self.announce_queues.queue_count()
135    }
136
137    pub fn nonempty_announce_queue_count(&self) -> usize {
138        self.announce_queues.nonempty_queue_count()
139    }
140
141    pub fn queued_announce_count(&self) -> usize {
142        self.announce_queues.total_queued_announces()
143    }
144
145    pub fn queued_announce_bytes(&self) -> usize {
146        self.announce_queues.total_queued_bytes()
147    }
148
149    pub fn announce_queue_interface_cap_drop_count(&self) -> u64 {
150        self.announce_queues.interface_cap_drop_count()
151    }
152
153    pub fn local_destinations_count(&self) -> usize {
154        self.local_destinations.len()
155    }
156
157    pub fn rate_limiter(&self) -> &AnnounceRateLimiter {
158        &self.rate_limiter
159    }
160
161    pub fn interface_info(&self, id: &InterfaceId) -> Option<&InterfaceInfo> {
162        self.interfaces.get(id)
163    }
164
165    pub fn redirect_path(&mut self, dest_hash: &[u8; 16], interface: InterfaceId, now: f64) {
166        if let Some(entry) = self
167            .path_table
168            .get_mut(dest_hash)
169            .and_then(|ps| ps.primary_mut())
170        {
171            entry.receiving_interface = interface;
172            // A redirected path is a true one-hop path to the destination.
173            // Retaining the previous transport as next_hop makes outbound
174            // routing inject a HEADER_2 transport header for that stale peer.
175            entry.next_hop = *dest_hash;
176            entry.hops = 1;
177            entry.timestamp = now;
178            entry.expires = now + 3600.0;
179        } else {
180            self.upsert_path_destination(
181                *dest_hash,
182                PathEntry {
183                    timestamp: now,
184                    next_hop: *dest_hash,
185                    hops: 1,
186                    expires: now + 3600.0,
187                    random_blobs: Vec::new(),
188                    receiving_interface: interface,
189                    packet_hash: [0u8; 32],
190                    announce_raw: None,
191                },
192                now,
193            );
194        }
195    }
196
197    pub fn inject_path(&mut self, dest_hash: [u8; 16], entry: PathEntry) {
198        self.upsert_path_destination(dest_hash, entry.clone(), entry.timestamp);
199    }
200
201    pub fn drop_path(&mut self, dest_hash: &[u8; 16]) -> bool {
202        self.path_table.remove(dest_hash).is_some()
203    }
204
205    pub fn drop_all_via(&mut self, transport_hash: &[u8; 16]) -> usize {
206        let mut removed = 0usize;
207        for ps in self.path_table.values_mut() {
208            let before = ps.len();
209            ps.retain(|entry| &entry.next_hop != transport_hash);
210            removed += before - ps.len();
211        }
212        self.path_table.retain(|_, ps| !ps.is_empty());
213        removed
214    }
215
216    pub fn drop_paths_for_interface(&mut self, interface: InterfaceId) -> usize {
217        let mut removed = 0usize;
218        let mut cleared_destinations = Vec::new();
219        for (dest_hash, ps) in self.path_table.iter_mut() {
220            let before = ps.len();
221            ps.retain(|entry| entry.receiving_interface != interface);
222            if ps.is_empty() {
223                cleared_destinations.push(*dest_hash);
224            }
225            removed += before - ps.len();
226        }
227        self.path_table.retain(|_, ps| !ps.is_empty());
228        for dest_hash in cleared_destinations {
229            self.path_states.remove(&dest_hash);
230        }
231        removed
232    }
233
234    pub fn drop_reverse_for_interface(&mut self, interface: InterfaceId) -> usize {
235        let before = self.reverse_table.len();
236        self.reverse_table.retain(|_, entry| {
237            entry.receiving_interface != interface && entry.outbound_interface != interface
238        });
239        before - self.reverse_table.len()
240    }
241
242    pub fn drop_links_for_interface(&mut self, interface: InterfaceId) -> usize {
243        let before = self.link_table.len();
244        self.link_table.retain(|_, entry| {
245            entry.next_hop_interface != interface && entry.received_interface != interface
246        });
247        before - self.link_table.len()
248    }
249
250    pub fn drop_announce_queues(&mut self) {
251        self.announce_table.clear();
252        self.held_announces.clear();
253        self.announce_queues = AnnounceQueues::new(self.config.announce_queue_max_interfaces);
254        self.ingress_control.clear();
255    }
256
257    pub fn void_queues(&mut self) {
258        self.drop_announce_queues();
259        self.reverse_table.clear();
260    }
261
262    pub fn identity_hash(&self) -> Option<&[u8; 16]> {
263        self.config.identity_hash.as_ref()
264    }
265
266    pub fn transport_enabled(&self) -> bool {
267        self.config.transport_enabled
268    }
269
270    pub fn config(&self) -> &TransportConfig {
271        &self.config
272    }
273
274    pub fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
275        self.config.packet_hashlist_max_entries = max_entries;
276        self.packet_hashlist =
277            PacketHashlist::with_allocation(max_entries, self.config.packet_hashlist_allocation);
278    }
279
280    pub fn get_path_table(&self, max_hops: Option<u8>) -> Vec<PathTableRow> {
281        let mut result = Vec::new();
282        for (dest_hash, ps) in self.path_table.iter() {
283            if let Some(entry) = ps.primary() {
284                if let Some(max) = max_hops {
285                    if entry.hops > max {
286                        continue;
287                    }
288                }
289                let iface_name = self
290                    .interfaces
291                    .get(&entry.receiving_interface)
292                    .map(|i| i.name.clone())
293                    .unwrap_or_else(|| {
294                        alloc::format!("Interface({})", entry.receiving_interface.0)
295                    });
296                result.push((
297                    *dest_hash,
298                    entry.timestamp,
299                    entry.next_hop,
300                    entry.hops,
301                    entry.expires,
302                    iface_name,
303                ));
304            }
305        }
306        result
307    }
308
309    pub fn get_rate_table(&self) -> Vec<RateTableRow> {
310        self.rate_limiter
311            .entries()
312            .map(|(hash, entry)| {
313                (
314                    *hash,
315                    entry.last,
316                    entry.rate_violations,
317                    entry.blocked_until,
318                    entry.timestamps.clone(),
319                )
320            })
321            .collect()
322    }
323
324    pub fn get_blackholed(&self) -> Vec<([u8; 16], f64, f64, Option<alloc::string::String>)> {
325        self.blackholed_entries()
326            .map(|(hash, entry)| (*hash, entry.created, entry.expires, entry.reason.clone()))
327            .collect()
328    }
329
330    pub fn active_destination_hashes(&self) -> alloc::collections::BTreeSet<[u8; 16]> {
331        self.path_table.keys().copied().collect()
332    }
333
334    pub fn path_destination_cap_evict_count(&self) -> usize {
335        self.path_destination_cap_evict_count
336    }
337
338    pub fn active_packet_hashes(&self) -> Vec<[u8; 32]> {
339        let mut hashes: Vec<[u8; 32]> = self
340            .path_table
341            .values()
342            .flat_map(|ps| ps.iter().map(|p| p.packet_hash))
343            .collect();
344
345        hashes.extend(
346            self.tunnel_table
347                .iter()
348                .flat_map(|(_, tunnel)| tunnel.paths.values().map(|p| p.packet_hash)),
349        );
350        hashes.sort_unstable();
351        hashes.dedup();
352        hashes
353    }
354
355    pub fn cull_rate_limiter(
356        &mut self,
357        active: &alloc::collections::BTreeSet<[u8; 16]>,
358        now: f64,
359        ttl_secs: f64,
360    ) -> usize {
361        self.rate_limiter.cull_stale(active, now, ttl_secs)
362    }
363
364    pub fn update_interface_freq(&mut self, id: InterfaceId, ia_freq: f64) {
365        if let Some(info) = self.interfaces.get_mut(&id) {
366            info.ia_freq = ia_freq;
367        }
368    }
369
370    pub fn update_interface_freqs(
371        &mut self,
372        id: InterfaceId,
373        ia_freq: f64,
374        ip_freq: f64,
375        op_freq: f64,
376        op_samples: usize,
377    ) {
378        if let Some(info) = self.interfaces.get_mut(&id) {
379            info.ia_freq = ia_freq;
380            info.ip_freq = ip_freq;
381            info.op_freq = op_freq;
382            info.op_samples = op_samples;
383        }
384    }
385
386    pub fn held_announce_count(&self, interface: &InterfaceId) -> usize {
387        self.ingress_control.held_count(interface)
388    }
389
390    pub fn burst_active(&self, interface: &InterfaceId) -> bool {
391        self.ingress_control.burst_active(interface)
392    }
393
394    pub fn burst_activated(&self, interface: &InterfaceId) -> f64 {
395        self.ingress_control.burst_activated(interface)
396    }
397
398    pub fn pr_burst_active(&self, interface: &InterfaceId) -> bool {
399        self.ingress_control.pr_burst_active(interface)
400    }
401
402    pub fn pr_burst_activated(&self, interface: &InterfaceId) -> f64 {
403        self.ingress_control.pr_burst_activated(interface)
404    }
405
406    #[cfg(test)]
407    #[allow(dead_code)]
408    pub(crate) fn path_table(&self) -> &BTreeMap<[u8; 16], PathSet> {
409        &self.path_table
410    }
411
412    #[cfg(test)]
413    #[allow(dead_code)]
414    pub(crate) fn announce_table(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
415        &self.announce_table
416    }
417
418    #[cfg(test)]
419    #[allow(dead_code)]
420    pub(crate) fn held_announces(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
421        &self.held_announces
422    }
423
424    #[cfg(test)]
425    #[allow(dead_code)]
426    pub(crate) fn announce_retained_bytes(&self) -> usize {
427        self.announce_retained_bytes_total()
428    }
429
430    #[cfg(test)]
431    #[allow(dead_code)]
432    pub(crate) fn reverse_table(&self) -> &BTreeMap<[u8; 16], tables::ReverseEntry> {
433        &self.reverse_table
434    }
435}