Skip to main content

rns_core/transport/
inbound.rs

1use alloc::vec::Vec;
2
3use super::tables::{LinkEntry, ReverseEntry};
4use super::types::{InterfaceId, TransportAction};
5use crate::constants;
6use crate::link::handshake::compute_link_id;
7use crate::packet::RawPacket;
8
9#[derive(Debug, Clone, Copy, Default)]
10pub struct LocalHopRewrite {
11    pub local_hops_delta: u8,
12    pub from_local_client: bool,
13    pub skip_local_hops_delta: bool,
14}
15
16impl LocalHopRewrite {
17    fn hop_byte(self, packet: &RawPacket) -> u8 {
18        if self.local_hops_delta != 0 && self.from_local_client && !self.skip_local_hops_delta {
19            self.local_hops_delta
20        } else {
21            packet.hops
22        }
23    }
24}
25
26/// Forward a packet that is addressed to us as a transport node.
27///
28/// Transport.py:1427-1504: When we receive a HEADER_2 packet with our
29/// transport_id, we forward it toward the destination using our path table.
30pub fn forward_transport_packet(
31    packet: &RawPacket,
32    next_hop: [u8; 16],
33    remaining_hops: u8,
34    _outbound_interface: InterfaceId,
35) -> Vec<u8> {
36    if remaining_hops > 1 || (remaining_hops == 1 && next_hop != packet.destination_hash) {
37        // Replace transport_id with next_hop, update hops. A one-hop path can
38        // still point at a final transport node for destinations behind it.
39        let mut new_raw = Vec::new();
40        new_raw.push(packet.raw[0]); // flags unchanged
41        new_raw.push(packet.hops); // updated hop count
42        new_raw.extend_from_slice(&next_hop); // transport_id = next hop
43                                              // Skip old transport_id (bytes 2..18), keep dest_hash + context + data
44        new_raw.extend_from_slice(&packet.raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
45        new_raw
46    } else if remaining_hops == 1 {
47        // Direct final hop: strip transport headers and deliver as H1.
48        let new_flags = (constants::HEADER_1 << 6)
49            | (constants::TRANSPORT_BROADCAST << 4)
50            | (packet.raw[0] & 0x0F);
51        let mut new_raw = Vec::new();
52        new_raw.push(new_flags);
53        new_raw.push(packet.hops);
54        new_raw.extend_from_slice(&packet.raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
55        new_raw
56    } else {
57        // remaining_hops == 0: final local delivery, strip transport header.
58        let new_flags = (constants::HEADER_1 << 6)
59            | (constants::TRANSPORT_BROADCAST << 4)
60            | (packet.raw[0] & 0x0F);
61        let mut new_raw = Vec::new();
62        new_raw.push(new_flags);
63        new_raw.push(packet.hops);
64        new_raw.extend_from_slice(&packet.raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
65        new_raw
66    }
67}
68
69/// Create a link table entry for a forwarded LINKREQUEST.
70pub fn create_link_entry(
71    packet: &RawPacket,
72    next_hop: [u8; 16],
73    outbound_interface: InterfaceId,
74    remaining_hops: u8,
75    receiving_interface: InterfaceId,
76    now: f64,
77    proof_timeout: f64,
78) -> ([u8; 16], LinkEntry) {
79    // Link ID must be computed the same way as in the link engine:
80    // compute_link_id(hashable_part, extra) where extra = data_len - ECPUBSIZE
81    // This ensures the transport's link table key matches the link_id in LRPROOF packets.
82    let hashable = packet.get_hashable_part();
83    let extra = if packet.data.len() > constants::LINK_ECPUBSIZE {
84        packet.data.len() - constants::LINK_ECPUBSIZE
85    } else {
86        0
87    };
88    let link_id = compute_link_id(&hashable, extra);
89
90    let entry = LinkEntry {
91        timestamp: now,
92        next_hop_transport_id: next_hop,
93        next_hop_interface: outbound_interface,
94        remaining_hops,
95        received_interface: receiving_interface,
96        taken_hops: packet.hops,
97        destination_hash: packet.destination_hash,
98        validated: false,
99        proof_timeout,
100    };
101
102    (link_id, entry)
103}
104
105/// Create a reverse table entry for proof routing.
106pub fn create_reverse_entry(
107    packet: &RawPacket,
108    outbound_interface: InterfaceId,
109    receiving_interface: InterfaceId,
110    now: f64,
111) -> ([u8; 16], ReverseEntry) {
112    let truncated_hash = packet.get_truncated_hash();
113    let entry = ReverseEntry {
114        receiving_interface,
115        outbound_interface,
116        timestamp: now,
117    };
118    (truncated_hash, entry)
119}
120
121/// Route a proof packet via the reverse table.
122///
123/// Transport.py:2090-2100: Pop the reverse entry, check that the proof
124/// arrived on the correct interface (outbound_interface), then forward
125/// it to the receiving_interface.
126pub fn route_proof_via_reverse(
127    packet: &RawPacket,
128    reverse_entry: &ReverseEntry,
129    receiving_interface: InterfaceId,
130    hop_rewrite: LocalHopRewrite,
131) -> Option<TransportAction> {
132    if receiving_interface == reverse_entry.outbound_interface {
133        let mut new_raw = Vec::new();
134        new_raw.push(packet.raw[0]);
135        new_raw.push(hop_rewrite.hop_byte(packet));
136        new_raw.extend_from_slice(&packet.raw[2..]);
137
138        Some(TransportAction::SendOnInterface {
139            interface: reverse_entry.receiving_interface,
140            raw: new_raw.into(),
141        })
142    } else {
143        None
144    }
145}
146
147/// Route link traffic bidirectionally through the link table.
148///
149/// Transport.py:1514-1549.
150pub fn route_via_link_table(
151    packet: &RawPacket,
152    link_entry: &LinkEntry,
153    receiving_interface: InterfaceId,
154    hop_rewrite: LocalHopRewrite,
155) -> Option<(InterfaceId, Vec<u8>)> {
156    let outbound_interface;
157
158    if link_entry.next_hop_interface == link_entry.received_interface {
159        // Same interface: check hop counts match
160        if packet.hops == link_entry.remaining_hops || packet.hops == link_entry.taken_hops {
161            outbound_interface = link_entry.next_hop_interface;
162        } else {
163            return None;
164        }
165    } else {
166        // Different interfaces: forward to opposite side
167        if receiving_interface == link_entry.next_hop_interface {
168            if packet.hops == link_entry.remaining_hops {
169                outbound_interface = link_entry.received_interface;
170            } else {
171                return None;
172            }
173        } else if receiving_interface == link_entry.received_interface {
174            if packet.hops == link_entry.taken_hops {
175                outbound_interface = link_entry.next_hop_interface;
176            } else {
177                return None;
178            }
179        } else {
180            return None;
181        }
182    }
183
184    let mut new_raw = Vec::new();
185    new_raw.push(packet.raw[0]);
186    new_raw.push(hop_rewrite.hop_byte(packet));
187    new_raw.extend_from_slice(&packet.raw[2..]);
188
189    Some((outbound_interface, new_raw))
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use crate::packet::PacketFlags;
196
197    fn make_h2_packet(dest: &[u8; 16], transport_id: &[u8; 16], hops: u8) -> RawPacket {
198        let flags = PacketFlags {
199            header_type: constants::HEADER_2,
200            context_flag: constants::FLAG_UNSET,
201            transport_type: constants::TRANSPORT_TRANSPORT,
202            destination_type: constants::DESTINATION_SINGLE,
203            packet_type: constants::PACKET_TYPE_DATA,
204        };
205        RawPacket::pack(
206            flags,
207            hops,
208            dest,
209            Some(transport_id),
210            constants::CONTEXT_NONE,
211            b"payload",
212        )
213        .unwrap()
214    }
215
216    #[test]
217    fn test_forward_transport_multi_hop() {
218        let dest = [0x11; 16];
219        let transport_id = [0x22; 16];
220        let next_hop = [0x33; 16];
221        let packet = make_h2_packet(&dest, &transport_id, 2);
222
223        let new_raw = forward_transport_packet(&packet, next_hop, 3, InterfaceId(1));
224
225        // Should still be HEADER_2
226        let flags = crate::packet::PacketFlags::unpack(new_raw[0]);
227        assert_eq!(flags.header_type, constants::HEADER_2);
228        // Hops should be updated
229        assert_eq!(new_raw[1], 2);
230        // New transport_id should be next_hop
231        assert_eq!(&new_raw[2..18], &next_hop);
232        // dest_hash preserved
233        assert_eq!(&new_raw[18..34], &dest);
234    }
235
236    #[test]
237    fn test_forward_transport_last_hop_strips_header() {
238        let dest = [0x11; 16];
239        let transport_id = [0x22; 16];
240        let packet = make_h2_packet(&dest, &transport_id, 3);
241
242        let new_raw = forward_transport_packet(&packet, dest, 1, InterfaceId(1));
243
244        // Should be HEADER_1 now
245        let flags = crate::packet::PacketFlags::unpack(new_raw[0]);
246        assert_eq!(flags.header_type, constants::HEADER_1);
247        assert_eq!(flags.transport_type, constants::TRANSPORT_BROADCAST);
248        // No transport_id in HEADER_1
249        // dest_hash starts at byte 2
250        assert_eq!(&new_raw[2..18], &dest);
251    }
252
253    #[test]
254    fn test_forward_transport_one_hop_to_transport_keeps_header() {
255        let dest = [0x11; 16];
256        let transport_id = [0x22; 16];
257        let next_transport = [0x33; 16];
258        let packet = make_h2_packet(&dest, &transport_id, 3);
259
260        let new_raw = forward_transport_packet(&packet, next_transport, 1, InterfaceId(1));
261
262        let flags = crate::packet::PacketFlags::unpack(new_raw[0]);
263        assert_eq!(flags.header_type, constants::HEADER_2);
264        assert_eq!(flags.transport_type, constants::TRANSPORT_TRANSPORT);
265        assert_eq!(&new_raw[2..18], &next_transport);
266        assert_eq!(&new_raw[18..34], &dest);
267    }
268
269    #[test]
270    fn forward_transport_packet_strips_header_for_final_local_hop() {
271        let flags = PacketFlags {
272            header_type: constants::HEADER_2,
273            context_flag: constants::FLAG_UNSET,
274            transport_type: constants::TRANSPORT_TRANSPORT,
275            destination_type: constants::DESTINATION_SINGLE,
276            packet_type: constants::PACKET_TYPE_DATA,
277        };
278        let daemon_id = [0x42; 16];
279        let dest_hash = [0x99; 16];
280        let mut raw = Vec::new();
281        raw.push(flags.pack());
282        raw.push(0);
283        raw.extend_from_slice(&daemon_id);
284        raw.extend_from_slice(&dest_hash);
285        raw.push(constants::CONTEXT_NONE);
286        raw.extend_from_slice(b"hello");
287        let packet = RawPacket::unpack(&raw).unwrap();
288
289        let forwarded = forward_transport_packet(&packet, dest_hash, 0, InterfaceId(2));
290        let forwarded_flags = PacketFlags::unpack(forwarded[0]);
291        assert_eq!(forwarded_flags.header_type, constants::HEADER_1);
292        assert_eq!(
293            forwarded_flags.transport_type,
294            constants::TRANSPORT_BROADCAST
295        );
296        assert_eq!(&forwarded[2..18], &dest_hash);
297        assert_eq!(&forwarded[19..], b"hello");
298    }
299
300    #[test]
301    fn test_route_proof_correct_interface() {
302        let flags = PacketFlags {
303            header_type: constants::HEADER_1,
304            context_flag: constants::FLAG_UNSET,
305            transport_type: constants::TRANSPORT_BROADCAST,
306            destination_type: constants::DESTINATION_SINGLE,
307            packet_type: constants::PACKET_TYPE_PROOF,
308        };
309        let packet = RawPacket::pack(
310            flags,
311            2,
312            &[0xAA; 16],
313            None,
314            constants::CONTEXT_NONE,
315            &[0xBB; 32],
316        )
317        .unwrap();
318
319        let reverse = ReverseEntry {
320            receiving_interface: InterfaceId(1),
321            outbound_interface: InterfaceId(2),
322            timestamp: 100.0,
323        };
324
325        let action = route_proof_via_reverse(
326            &packet,
327            &reverse,
328            InterfaceId(2),
329            LocalHopRewrite::default(),
330        );
331        assert!(action.is_some());
332        match action.unwrap() {
333            TransportAction::SendOnInterface { interface, .. } => {
334                assert_eq!(interface, InterfaceId(1));
335            }
336            _ => panic!("Expected SendOnInterface"),
337        }
338    }
339
340    #[test]
341    fn test_route_proof_wrong_interface() {
342        let flags = PacketFlags {
343            header_type: constants::HEADER_1,
344            context_flag: constants::FLAG_UNSET,
345            transport_type: constants::TRANSPORT_BROADCAST,
346            destination_type: constants::DESTINATION_SINGLE,
347            packet_type: constants::PACKET_TYPE_PROOF,
348        };
349        let packet = RawPacket::pack(
350            flags,
351            2,
352            &[0xAA; 16],
353            None,
354            constants::CONTEXT_NONE,
355            &[0xBB; 32],
356        )
357        .unwrap();
358
359        let reverse = ReverseEntry {
360            receiving_interface: InterfaceId(1),
361            outbound_interface: InterfaceId(2),
362            timestamp: 100.0,
363        };
364
365        // Received on wrong interface (3 instead of 2)
366        let action = route_proof_via_reverse(
367            &packet,
368            &reverse,
369            InterfaceId(3),
370            LocalHopRewrite::default(),
371        );
372        assert!(action.is_none());
373    }
374
375    #[test]
376    fn test_route_via_link_table_different_interfaces() {
377        let flags = PacketFlags {
378            header_type: constants::HEADER_1,
379            context_flag: constants::FLAG_UNSET,
380            transport_type: constants::TRANSPORT_BROADCAST,
381            destination_type: constants::DESTINATION_LINK,
382            packet_type: constants::PACKET_TYPE_DATA,
383        };
384        let packet = RawPacket::pack(
385            flags,
386            3,
387            &[0xAA; 16],
388            None,
389            constants::CONTEXT_NONE,
390            b"data",
391        )
392        .unwrap();
393
394        let link = LinkEntry {
395            timestamp: 100.0,
396            next_hop_transport_id: [0; 16],
397            next_hop_interface: InterfaceId(1),
398            remaining_hops: 3,
399            received_interface: InterfaceId(2),
400            taken_hops: 5,
401            destination_hash: [0xAA; 16],
402            validated: true,
403            proof_timeout: 200.0,
404        };
405
406        // Received on next_hop_interface (1), should forward to received_interface (2)
407        let result =
408            route_via_link_table(&packet, &link, InterfaceId(1), LocalHopRewrite::default());
409        assert!(result.is_some());
410        let (iface, _) = result.unwrap();
411        assert_eq!(iface, InterfaceId(2));
412
413        // Received on received_interface (2), should forward to next_hop_interface (1)
414        let packet2 = RawPacket::pack(
415            flags,
416            5,
417            &[0xAA; 16],
418            None,
419            constants::CONTEXT_NONE,
420            b"data",
421        )
422        .unwrap();
423        let result2 =
424            route_via_link_table(&packet2, &link, InterfaceId(2), LocalHopRewrite::default());
425        assert!(result2.is_some());
426        let (iface2, _) = result2.unwrap();
427        assert_eq!(iface2, InterfaceId(1));
428    }
429
430    #[test]
431    fn test_route_via_link_table_wrong_hops() {
432        let flags = PacketFlags {
433            header_type: constants::HEADER_1,
434            context_flag: constants::FLAG_UNSET,
435            transport_type: constants::TRANSPORT_BROADCAST,
436            destination_type: constants::DESTINATION_LINK,
437            packet_type: constants::PACKET_TYPE_DATA,
438        };
439        // Wrong hop count
440        let packet = RawPacket::pack(
441            flags,
442            99,
443            &[0xAA; 16],
444            None,
445            constants::CONTEXT_NONE,
446            b"data",
447        )
448        .unwrap();
449
450        let link = LinkEntry {
451            timestamp: 100.0,
452            next_hop_transport_id: [0; 16],
453            next_hop_interface: InterfaceId(1),
454            remaining_hops: 3,
455            received_interface: InterfaceId(2),
456            taken_hops: 5,
457            destination_hash: [0xAA; 16],
458            validated: true,
459            proof_timeout: 200.0,
460        };
461
462        let result =
463            route_via_link_table(&packet, &link, InterfaceId(1), LocalHopRewrite::default());
464        assert!(result.is_none());
465    }
466}