1use super::*;
2
3impl TransportEngine {
4 pub fn tick(&mut self, now: f64, rng: &mut dyn Rng) -> Vec<TransportAction> {
6 let mut ctx = TickCtx {
7 now,
8 rng,
9 actions: Vec::new(),
10 };
11 self.process_tick_pending_announces(&mut ctx);
12
13 let mut queue_actions = self.announce_queues.process_queues(now, &self.interfaces);
14 ctx.actions.append(&mut queue_actions);
15
16 self.process_tick_ingress_release(&mut ctx);
17 self.cull_tick_tables(&mut ctx);
18 ctx.actions
19 }
20
21 fn process_tick_pending_announces(&mut self, ctx: &mut TickCtx<'_>) {
22 if ctx.now <= self.announces_last_checked + constants::ANNOUNCES_CHECK_INTERVAL {
23 return;
24 }
25
26 self.cull_expired_announce_entries(ctx.now);
27 self.enforce_announce_retention_cap(ctx.now);
28 if let Some(identity_hash) = self.config.identity_hash {
29 let announce_actions = jobs::process_pending_announces(
30 &mut self.announce_table,
31 &mut self.held_announces,
32 &identity_hash,
33 ctx.now,
34 );
35 let gated = self.gate_retransmit_actions(announce_actions, ctx.now);
36 ctx.actions.extend(gated);
37 }
38 self.cull_expired_announce_entries(ctx.now);
39 self.enforce_announce_retention_cap(ctx.now);
40 self.announces_last_checked = ctx.now;
41 }
42
43 fn process_tick_ingress_release(&mut self, ctx: &mut TickCtx<'_>) {
44 let ic_interfaces = self.ingress_control.interfaces_with_held();
45 for iface_id in ic_interfaces {
46 let (ia_freq, started, ingress_config) = match self.interfaces.get(&iface_id) {
47 Some(info) => (info.ia_freq, info.started, info.ingress_control),
48 None => continue,
49 };
50 if !ingress_config.enabled {
51 continue;
52 }
53 if let Some(held) = self.ingress_control.process_held_announces(
54 iface_id,
55 &ingress_config,
56 ia_freq,
57 started,
58 ctx.now,
59 ) {
60 let released_actions = self.handle_inbound(
61 InboundFrame {
62 raw: &held.raw,
63 iface: held.receiving_interface,
64 now: ctx.now,
65 rx: held.rx,
66 },
67 ctx.rng,
68 );
69 ctx.actions.extend(released_actions);
70 }
71 }
72 }
73
74 fn cull_tick_tables(&mut self, ctx: &mut TickCtx<'_>) {
75 if ctx.now <= self.tables_last_culled + constants::TABLES_CULL_INTERVAL {
76 return;
77 }
78
79 jobs::cull_path_table(&mut self.path_table, &self.interfaces, ctx.now);
80 jobs::cull_reverse_table(&mut self.reverse_table, &self.interfaces, ctx.now);
81 let (_culled, link_closed_actions) =
82 jobs::cull_link_table(&mut self.link_table, &self.interfaces, ctx.now);
83 ctx.actions.extend(link_closed_actions);
84 jobs::cull_path_states(&mut self.path_states, &self.path_table);
85 self.cull_blackholed(ctx.now);
86 self.path_requests.retain(|_, requested_at| {
87 ctx.now < *requested_at + constants::PATH_REQUEST_GATE_TIMEOUT
88 });
89 self.discovery_path_requests.retain(|destination, req| {
90 let deadline = self
91 .discovery_path_request_deadlines
92 .get(destination)
93 .copied()
94 .unwrap_or(req.timestamp + constants::DISCOVERY_PATH_REQUEST_TIMEOUT);
95 ctx.now < deadline
96 });
97 self.discovery_path_request_deadlines
98 .retain(|destination, _| self.discovery_path_requests.contains_key(destination));
99 self.tunnel_table
100 .void_missing_interfaces(|id| self.interfaces.contains_key(id));
101 self.tunnel_table.cull(ctx.now);
102 self.announce_sig_cache.cull(ctx.now);
103 self.tables_last_culled = ctx.now;
104 }
105
106 pub(super) fn gate_retransmit_actions(
111 &mut self,
112 actions: Vec<TransportAction>,
113 now: f64,
114 ) -> Vec<TransportAction> {
115 let mut result = Vec::new();
116 for action in actions {
117 match action {
118 TransportAction::SendOnInterface { interface, raw } => {
119 let (dest_hash, hops) = Self::extract_announce_info(&raw);
121 let (bitrate, airtime_profile, announce_cap) =
122 if let Some(info) = self.interfaces.get(&interface) {
123 (info.bitrate, info.airtime_profile, info.announce_cap)
124 } else {
125 (None, None, constants::ANNOUNCE_CAP)
126 };
127 if let Some(send_action) = self.announce_queues.gate_announce(
128 interface,
129 raw,
130 dest_hash,
131 hops,
132 now,
133 now,
134 bitrate,
135 airtime_profile,
136 announce_cap,
137 ) {
138 result.push(send_action);
139 }
140 }
141 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
142 let (dest_hash, hops) = Self::extract_announce_info(&raw);
143 let iface_ids: Vec<(
146 InterfaceId,
147 Option<u64>,
148 Option<types::AirtimeProfile>,
149 f64,
150 )> = self
151 .interfaces
152 .iter()
153 .filter(|(_, info)| info.out_capable)
154 .filter(|(id, _)| {
155 if let Some(ref ex) = exclude {
156 **id != *ex
157 } else {
158 true
159 }
160 })
161 .filter(|(_, info)| {
162 should_transmit_announce(
163 info,
164 &dest_hash,
165 hops,
166 &self.local_destinations,
167 &self.path_table,
168 &self.interfaces,
169 )
170 })
171 .map(|(id, info)| {
172 (*id, info.bitrate, info.airtime_profile, info.announce_cap)
173 })
174 .collect();
175
176 for (iface_id, bitrate, airtime_profile, announce_cap) in iface_ids {
177 if let Some(send_action) = self.announce_queues.gate_announce(
178 iface_id,
179 raw.clone(),
180 dest_hash,
181 hops,
182 now,
183 now,
184 bitrate,
185 airtime_profile,
186 announce_cap,
187 ) {
188 result.push(send_action);
189 }
190 }
191 }
192 other => result.push(other),
193 }
194 }
195 result
196 }
197
198 fn extract_announce_info(raw: &[u8]) -> ([u8; 16], u8) {
200 if raw.len() < 18 {
201 return ([0; 16], 0);
202 }
203 let header_type = (raw[0] >> 6) & 0x03;
204 let hops = raw[1];
205 if header_type == constants::HEADER_2 && raw.len() >= 34 {
206 let mut dest = [0u8; 16];
208 dest.copy_from_slice(&raw[18..34]);
209 (dest, hops)
210 } else {
211 let mut dest = [0u8; 16];
213 dest.copy_from_slice(&raw[2..18]);
214 (dest, hops)
215 }
216 }
217
218 #[cfg(test)]
219 #[allow(dead_code)]
220 pub(crate) fn link_table_ref(&self) -> &BTreeMap<[u8; 16], LinkEntry> {
221 &self.link_table
222 }
223}