Skip to main content

rns_core/transport/
path_requests.rs

1use super::*;
2
3impl TransportEngine {
4    pub fn handle_path_request(
5        &mut self,
6        data: &[u8],
7        interface_id: InterfaceId,
8        now: f64,
9    ) -> Vec<TransportAction> {
10        let Some(ctx) = self.parse_path_request(data, interface_id, now) else {
11            return Vec::new();
12        };
13        log::trace!(target: crate::logging::PATHING_LOG_TARGET,
14            "Path request for {:02x?} on interface {}",
15            &ctx.destination_hash[..4],
16            interface_id.0,
17        );
18        if self.local_destinations.contains_key(&ctx.destination_hash) {
19            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
20                "Ignoring path request for {:02x?}: destination is local",
21                &ctx.destination_hash[..4],
22            );
23            return Vec::new();
24        }
25        if self.config.transport_enabled && self.handle_known_path_request(&ctx) {
26            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
27                "Answering path request for {:02x?}: path is known",
28                &ctx.destination_hash[..4],
29            );
30            return Vec::new();
31        }
32        if self.config.transport_enabled {
33            return self.handle_discovery_path_request(&ctx);
34        }
35        log::trace!(target: crate::logging::PATHING_LOG_TARGET,
36            "Ignoring path request for {:02x?}: transport is disabled",
37            &ctx.destination_hash[..4],
38        );
39        Vec::new()
40    }
41
42    fn parse_path_request<'a>(
43        &mut self,
44        data: &'a [u8],
45        interface_id: InterfaceId,
46        now: f64,
47    ) -> Option<PathRequestCtx<'a>> {
48        if data.len() < 16 {
49            return None;
50        }
51
52        let mut destination_hash = [0u8; 16];
53        destination_hash.copy_from_slice(&data[..16]);
54
55        let tag_bytes = if data.len() > 32 {
56            Some(&data[32..])
57        } else if data.len() > 16 {
58            Some(&data[16..])
59        } else {
60            None
61        }?;
62
63        let tag_len = tag_bytes.len().min(16);
64        let mut unique_tag = [0u8; 32];
65        unique_tag[..16].copy_from_slice(&destination_hash);
66        unique_tag[16..16 + tag_len].copy_from_slice(&tag_bytes[..tag_len]);
67        if !self.insert_discovery_pr_tag(unique_tag) {
68            return None;
69        }
70
71        Some(PathRequestCtx {
72            tag: &tag_bytes[..tag_len],
73            interface_id,
74            now,
75            destination_hash,
76        })
77    }
78
79    fn handle_known_path_request(&mut self, ctx: &PathRequestCtx<'_>) -> bool {
80        let Some(path) = self
81            .path_table
82            .get(&ctx.destination_hash)
83            .and_then(|ps| ps.primary())
84            .cloned()
85        else {
86            return false;
87        };
88
89        if let Some(recv_info) = self.interfaces.get(&ctx.interface_id) {
90            if recv_info.mode == constants::MODE_ROAMING
91                && path.receiving_interface == ctx.interface_id
92            {
93                return true;
94            }
95        }
96
97        let Some(raw) = path.announce_raw.as_ref() else {
98            return false;
99        };
100        if let Some(existing) = self.announce_table.remove(&ctx.destination_hash) {
101            self.insert_held_announce(ctx.destination_hash, existing, ctx.now);
102        }
103        let retransmit_timeout = if let Some(iface_info) = self.interfaces.get(&ctx.interface_id) {
104            let base = ctx.now + constants::PATH_REQUEST_GRACE;
105            if iface_info.mode == constants::MODE_ROAMING {
106                base + constants::PATH_REQUEST_RG
107            } else {
108                base
109            }
110        } else {
111            ctx.now + constants::PATH_REQUEST_GRACE
112        };
113
114        let Ok(parsed) = RawPacket::unpack(raw) else {
115            return false;
116        };
117
118        let entry = AnnounceEntry {
119            timestamp: ctx.now,
120            retransmit_timeout,
121            retries: constants::PATHFINDER_R,
122            received_from: path.next_hop,
123            hops: path.hops,
124            packet_raw: raw.clone(),
125            packet_data: parsed.data,
126            destination_hash: ctx.destination_hash,
127            context_flag: parsed.flags.context_flag,
128            local_rebroadcasts: 0,
129            block_rebroadcasts: true,
130            attached_interface: Some(ctx.interface_id),
131        };
132
133        self.insert_announce_entry(ctx.destination_hash, entry, ctx.now);
134        true
135    }
136
137    fn handle_discovery_path_request(&mut self, ctx: &PathRequestCtx<'_>) -> Vec<TransportAction> {
138        let Some((mode, recursive_prs, ingress_control, ip_freq, started)) =
139            self.interfaces.get(&ctx.interface_id).map(|info| {
140                (
141                    info.mode,
142                    info.recursive_prs,
143                    info.ingress_control,
144                    info.ip_freq,
145                    info.started,
146                )
147            })
148        else {
149            return Vec::new();
150        };
151
152        let search_mode_filter: Option<&[u8]> = if recursive_prs
153            || constants::DISCOVER_PATHS_FOR.contains(&mode)
154        {
155            None
156        } else if mode == constants::MODE_BOUNDARY {
157            Some(&constants::BOUNDARY_SEARCH_MODES)
158        } else {
159            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
160                "Not discovering path to {:02x?}: recursive path discovery is disabled on interface {}",
161                &ctx.destination_hash[..4],
162                ctx.interface_id.0,
163            );
164            return Vec::new();
165        };
166
167        if self.ingress_control.should_ingress_limit_pr(
168            ctx.interface_id,
169            &ingress_control,
170            ip_freq,
171            started,
172            ctx.now,
173        ) {
174            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
175                "Not discovering path to {:02x?}: ingress path-request limiting is active on interface {}",
176                &ctx.destination_hash[..4],
177                ctx.interface_id.0,
178            );
179            return Vec::new();
180        }
181
182        let egress_candidates: Vec<_> = self
183            .interfaces
184            .values()
185            .filter(|info| info.id != ctx.interface_id && info.out_capable)
186            .filter(|info| search_mode_filter.map_or(true, |modes| modes.contains(&info.mode)))
187            .map(|info| {
188                (
189                    info.id,
190                    info.ingress_control,
191                    info.op_freq,
192                    info.op_samples,
193                    info.bitrate,
194                    info.airtime_profile,
195                    info.announce_cap,
196                )
197            })
198            .collect();
199
200        let Some((path_request_raw, path_request_len)) = build_path_request_packet(
201            &ctx.destination_hash,
202            self.config.identity_hash.as_ref(),
203            ctx.tag,
204        ) else {
205            return Vec::new();
206        };
207
208        let mut actions = Vec::new();
209        for (id, ingress_control, op_freq, op_samples, bitrate, airtime_profile, announce_cap) in
210            egress_candidates
211        {
212            if self.ingress_control.should_egress_limit_pr(
213                id,
214                &ingress_control,
215                op_freq,
216                op_samples,
217            ) || self
218                .announce_queues
219                .blocks_recursive_path_request(id, ctx.now)
220            {
221                continue;
222            }
223
224            self.announce_queues.reserve_recursive_path_request(
225                id,
226                path_request_len + constants::HEADER_MINSIZE,
227                ctx.now,
228                bitrate,
229                airtime_profile,
230                announce_cap,
231            );
232            actions.push(TransportAction::SendOnInterface {
233                interface: id,
234                raw: path_request_raw.clone().into(),
235            });
236        }
237
238        if !actions.is_empty() {
239            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
240                "Discovering unknown path to {:02x?} on behalf of interface {} via {} interfaces",
241                &ctx.destination_hash[..4],
242                ctx.interface_id.0,
243                actions.len(),
244            );
245            self.discovery_path_requests.insert(
246                ctx.destination_hash,
247                DiscoveryPathRequest {
248                    timestamp: ctx.now,
249                    requesting_interface: ctx.interface_id,
250                },
251            );
252        } else {
253            log::trace!(target: crate::logging::PATHING_LOG_TARGET,
254                "Not discovering path to {:02x?}: no eligible egress interface",
255                &ctx.destination_hash[..4],
256            );
257        }
258
259        actions
260    }
261}
262
263fn build_path_request_packet(
264    destination_hash: &[u8; 16],
265    transport_identity_hash: Option<&[u8; 16]>,
266    tag: &[u8],
267) -> Option<(Vec<u8>, usize)> {
268    let mut data = Vec::with_capacity(16 + transport_identity_hash.map_or(0, |_| 16) + tag.len());
269    data.extend_from_slice(destination_hash);
270    if let Some(identity_hash) = transport_identity_hash {
271        data.extend_from_slice(identity_hash);
272    }
273    data.extend_from_slice(tag);
274
275    let flags = crate::packet::PacketFlags {
276        header_type: constants::HEADER_1,
277        context_flag: constants::FLAG_UNSET,
278        transport_type: constants::TRANSPORT_BROADCAST,
279        destination_type: constants::DESTINATION_PLAIN,
280        packet_type: constants::PACKET_TYPE_DATA,
281    };
282    let path_request_dest =
283        crate::destination::destination_hash("rnstransport", &["path", "request"], None);
284
285    let data_len = data.len();
286    RawPacket::pack(
287        flags,
288        0,
289        &path_request_dest,
290        None,
291        constants::CONTEXT_NONE,
292        &data,
293    )
294    .ok()
295    .map(|packet| (packet.raw, data_len))
296}