Skip to main content

rns_core/transport/
mod.rs

1pub mod announce_proc;
2pub mod announce_queue;
3pub mod announce_verify_queue;
4pub mod dedup;
5pub mod inbound;
6pub mod ingress_control;
7pub mod jobs;
8pub mod outbound;
9pub mod path_requests;
10pub mod pathfinder;
11pub mod persistence;
12pub mod queries;
13pub mod rate_limit;
14pub mod retention;
15pub mod tables;
16pub mod tunnel;
17pub mod types;
18
19use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
20use alloc::string::String;
21use alloc::vec::Vec;
22use core::mem::size_of;
23
24use rns_crypto::Rng;
25
26use crate::announce::AnnounceData;
27use crate::constants;
28use crate::hash;
29use crate::packet::RawPacket;
30
31use self::announce_proc::compute_path_expires;
32use self::announce_queue::AnnounceQueues;
33use self::announce_verify_queue::{AnnounceVerifyKey, AnnounceVerifyQueue, PendingAnnounce};
34use self::dedup::{AnnounceSignatureCache, PacketHashlist};
35use self::inbound::{
36    clamp_forwarded_link_request_mtu, create_link_entry, create_reverse_entry,
37    forward_transport_packet, route_proof_via_reverse, route_via_link_table, LocalHopRewrite,
38};
39use self::ingress_control::IngressControl;
40use self::outbound::{route_outbound_with_options, should_transmit_announce, OutboundRouteOptions};
41use self::pathfinder::{
42    extract_random_blob, timebase_from_random_blob, trusted_timebase_from_random_blobs,
43    MultiPathDecision,
44};
45use self::rate_limit::AnnounceRateLimiter;
46use self::tables::{AnnounceEntry, DiscoveryPathRequest, LinkEntry, PathEntry, PathSet};
47use self::tunnel::TunnelTable;
48use self::types::{
49    BlackholeEntry, InterfaceId, InterfaceInfo, PacketBytes, TransportAction, TransportConfig,
50};
51
52pub type PathTableRow = ([u8; 16], f64, [u8; 16], u8, f64, String);
53pub type RateTableRow = ([u8; 16], f64, u32, f64, Vec<f64>);
54/// Parsed LRPROOF data used to rebalance an existing link route.
55pub type LrproofRebalanceCandidate = ([u8; 16], [u8; 16], u8, Vec<u8>);
56
57fn lrproof_hop_mismatch_diagnostic(packet_hops: u8, entry: &LinkEntry) -> String {
58    alloc::format!(
59        "Received link request proof with hop mismatch ({}/{}:{}->{}), not transporting it",
60        packet_hops,
61        entry.remaining_hops,
62        entry.next_hop_interface.0,
63        entry.received_interface.0,
64    )
65}
66
67fn link_route_hops_match(
68    packet_hops: u8,
69    entry: &LinkEntry,
70    receiving_interface: InterfaceId,
71) -> bool {
72    if entry.next_hop_interface == entry.received_interface {
73        packet_hops == entry.remaining_hops || packet_hops == entry.taken_hops
74    } else if receiving_interface == entry.next_hop_interface {
75        packet_hops == entry.remaining_hops
76    } else if receiving_interface == entry.received_interface {
77        packet_hops == entry.taken_hops
78    } else {
79        // The routing failure is the interface, not a hop mismatch.
80        true
81    }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Default)]
85pub struct RxMetadata {
86    pub rssi: Option<i16>,
87    pub snr: Option<f32>,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq)]
91pub struct InboundFrame<'a> {
92    pub raw: &'a [u8],
93    pub iface: InterfaceId,
94    pub now: f64,
95    pub rx: RxMetadata,
96}
97
98impl<'a> InboundFrame<'a> {
99    pub fn new(raw: &'a [u8], iface: InterfaceId, now: f64) -> Self {
100        Self {
101            raw,
102            iface,
103            now,
104            rx: RxMetadata::default(),
105        }
106    }
107
108    pub fn with_rx(mut self, rx: RxMetadata) -> Self {
109        self.rx = rx;
110        self
111    }
112}
113
114/// A syntactically decoded packet. Routing/admission is rechecked on every use.
115/// Fields are read-only so cached hashes cannot diverge from the parsed bytes.
116pub struct DecodedPacket {
117    packet: RawPacket,
118    /// Set by [`TransportEngine::decode_inbound`] for link traffic whose hash
119    /// it leaves unread; `packet.packet_hash` is then zeroed until the engine
120    /// needs it.
121    hash_deferred: bool,
122}
123
124impl DecodedPacket {
125    pub fn unpack(raw: &[u8]) -> Result<Self, crate::packet::PacketError> {
126        RawPacket::unpack(raw).map(Self::hashed)
127    }
128
129    /// Decode wire bytes the caller owns without copying them.
130    pub fn unpack_owned(raw: Vec<u8>) -> Result<Self, crate::packet::PacketError> {
131        RawPacket::unpack_owned(raw).map(Self::hashed)
132    }
133
134    fn hashed(packet: RawPacket) -> Self {
135        Self {
136            packet,
137            hash_deferred: false,
138        }
139    }
140
141    /// The decoded fields. Its `packet_hash` is zeroed when hashing was
142    /// deferred; read the hash through [`DecodedPacket::packet_hash`].
143    pub fn packet(&self) -> &RawPacket {
144        &self.packet
145    }
146
147    /// The packet hash, unless [`TransportEngine::decode_inbound`] deferred it.
148    pub fn packet_hash(&self) -> Option<&[u8; 32]> {
149        (!self.hash_deferred).then_some(&self.packet.packet_hash)
150    }
151}
152
153/// Decoded bytes plus the current receive context; no admission decision cached.
154pub struct DecodedInboundFrame {
155    pub decoded: DecodedPacket,
156    pub iface: InterfaceId,
157    pub now: f64,
158    pub rx: RxMetadata,
159}
160
161struct InboundPacketCtx {
162    packet: RawPacket,
163    /// `packet.packet_hash` is zeroed: the packet is link traffic that is
164    /// only forwarded, or a local link resource part, and nothing reads it.
165    hash_deferred: bool,
166    original_raw: Option<Vec<u8>>,
167    iface: InterfaceId,
168    now: f64,
169    from_local_client: bool,
170}
171
172struct VerifiedAnnounceCtx<'a> {
173    packet: &'a RawPacket,
174    original_raw: &'a [u8],
175    iface: InterfaceId,
176    now: f64,
177    validated: crate::announce::ValidatedAnnounce,
178    received_from: [u8; 16],
179    random_blob: [u8; 10],
180    announce_emitted: u64,
181}
182
183struct TickCtx<'a> {
184    now: f64,
185    rng: &'a mut dyn Rng,
186    actions: Vec<TransportAction>,
187}
188
189/// Validated, uniquely tagged path request awaiting ingress accounting.
190///
191/// This two-phase token is intended for the network driver; applications
192/// should use [`TransportEngine::handle_path_request`] instead.
193#[doc(hidden)]
194pub struct AcceptedPathRequest {
195    tag: [u8; 16],
196    tag_len: usize,
197    interface_id: InterfaceId,
198    now: f64,
199    destination_hash: [u8; 16],
200    already_in_flight: bool,
201}
202
203/// The core transport/routing engine.
204///
205/// Maintains routing tables and processes packets without performing any I/O.
206/// Returns `Vec<TransportAction>` that the caller must execute.
207pub struct TransportEngine {
208    config: TransportConfig,
209    path_table: BTreeMap<[u8; 16], PathSet>,
210    announce_table: BTreeMap<[u8; 16], AnnounceEntry>,
211    reverse_table: tables::ReverseTable,
212    link_table: BTreeMap<[u8; 16], LinkEntry>,
213    held_announces: BTreeMap<[u8; 16], AnnounceEntry>,
214    packet_hashlist: PacketHashlist,
215    announce_sig_cache: AnnounceSignatureCache,
216    rate_limiter: AnnounceRateLimiter,
217    path_states: BTreeMap<[u8; 16], u8>,
218    interfaces: BTreeMap<InterfaceId, InterfaceInfo>,
219    interface_hashes: BTreeMap<InterfaceId, [u8; 32]>,
220    local_destinations: BTreeMap<[u8; 16], u8>,
221    blackholed_identities: BTreeMap<[u8; 16], BlackholeEntry>,
222    announce_queues: AnnounceQueues,
223    ingress_control: IngressControl,
224    tunnel_table: TunnelTable,
225    discovery_pr_tags: VecDeque<[u8; 32]>,
226    discovery_pr_tag_set: BTreeSet<[u8; 32]>,
227    path_requests: BTreeMap<[u8; 16], f64>,
228    discovery_path_requests: BTreeMap<[u8; 16], DiscoveryPathRequest>,
229    discovery_path_request_deadlines: BTreeMap<[u8; 16], f64>,
230    /// Path requests for destinations behind a local client, by destination:
231    /// the requesting interface and when it asked (Transport.py
232    /// `pending_local_path_requests`).
233    pending_local_path_requests: BTreeMap<[u8; 16], (InterfaceId, f64)>,
234    path_destination_cap_evict_count: usize,
235    // Job timing
236    announces_last_checked: f64,
237    tables_last_culled: f64,
238}
239
240mod engine_state;
241mod inbound_engine;
242mod maintenance;
243mod outbound_engine;
244
245#[cfg(test)]
246mod tests;