1#[cfg(feature = "discovery")]
4use crate::discovery::{ClientStatsSnapshot, TopologySnapshot};
5use crate::{
6 RouteSelectionMode, TelemetryError, TelemetryResult,
7 config::RuntimeMemoryConfig,
8 diagnostics::RuntimeStatsSnapshot,
9 packet::Packet,
10 router::{Clock, Router},
11};
12use alloc::{boxed::Box, string::String, sync::Arc};
13
14pub type RelaySideId = usize;
15pub const IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES: usize =
16 crate::router::IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES;
17pub const IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES: usize =
18 crate::router::IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES;
19pub const DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT: usize =
20 crate::router::DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT;
21type PacketHandlerFn = dyn Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static;
23
24type PackedHandlerFn = dyn Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static;
26
27#[derive(Clone)]
29pub enum RelayTxHandlerFn {
30 Packed(Arc<PackedHandlerFn>),
31 Packet(Arc<PacketHandlerFn>),
32}
33
34pub use crate::routing_config::SideOptions as RelaySideOptions;
35
36#[derive(Clone)]
38pub struct RelaySide {
39 pub name: Arc<str>,
40 pub tx_handler: RelayTxHandlerFn,
41 pub opts: RelaySideOptions,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub enum RelayItem {
46 Packed(crate::SharedBytes),
47 Packet(Arc<Packet>),
48}
49
50#[derive(Clone, Debug)]
52pub struct RelayConfig {
53 core: crate::router::RouterConfig,
54}
55
56impl RelayConfig {
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn with_sender<S: AsRef<str>>(mut self, sender: S) -> Self {
62 self.core = self.core.with_sender(sender);
63 self
64 }
65
66 pub fn with_memory_config(mut self, memory: RuntimeMemoryConfig) -> TelemetryResult<Self> {
67 self.core = self.core.with_memory_config(memory)?;
68 Ok(self)
69 }
70}
71
72impl Default for RelayConfig {
73 fn default() -> Self {
74 Self {
75 core: crate::router::RouterConfig::relay(),
76 }
77 }
78}
79
80pub struct Relay {
83 pub(crate) core: Router,
84}
85
86impl Relay {
87 pub fn new(clock: Box<dyn Clock + Send + Sync>) -> Self {
88 Self::new_with_config(RelayConfig::default(), clock)
89 }
90
91 pub fn new_with_config(cfg: RelayConfig, clock: Box<dyn Clock + Send + Sync>) -> Self {
92 Self {
93 core: Router::new_with_clock(cfg.core, clock),
94 }
95 }
96
97 pub fn sender(&self) -> Arc<str> {
98 self.core.sender()
99 }
100
101 pub fn set_sender<S: AsRef<str>>(&self, sender: S) {
102 self.core.set_sender(sender)
103 }
104
105 pub fn note_side_link_probe_sample(
106 &self,
107 side: RelaySideId,
108 bytes: usize,
109 duration_ms: u64,
110 ) -> TelemetryResult<()> {
111 self.core
112 .note_side_link_probe_sample(side, bytes, duration_ms)
113 }
114
115 pub fn add_side_packed<N, F>(&self, name: N, tx: F) -> RelaySideId
116 where
117 N: AsRef<str>,
118 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
119 {
120 self.core.add_side_packed(name, tx)
121 }
122
123 pub fn add_side_packed_small_packets<N, F>(
124 &self,
125 name: N,
126 tx: F,
127 max_frame_bytes: usize,
128 ) -> RelaySideId
129 where
130 N: AsRef<str>,
131 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
132 {
133 self.core
134 .add_side_packed_small_packets(name, tx, max_frame_bytes)
135 }
136
137 pub fn add_side_packed_with_options<N, F>(
138 &self,
139 name: N,
140 tx: F,
141 opts: RelaySideOptions,
142 ) -> RelaySideId
143 where
144 N: AsRef<str>,
145 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
146 {
147 self.core
148 .add_side_packed_with_options(name, tx, legacy_chunk_options(opts))
149 }
150
151 pub fn add_side_packet<N, F>(&self, name: N, tx: F) -> RelaySideId
152 where
153 N: AsRef<str>,
154 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
155 {
156 self.core.add_side_packet(name, tx)
157 }
158
159 pub fn add_side_packet_with_options<N, F>(
160 &self,
161 name: N,
162 tx: F,
163 opts: RelaySideOptions,
164 ) -> RelaySideId
165 where
166 N: AsRef<str>,
167 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
168 {
169 self.core.add_side_packet_with_options(name, tx, opts)
170 }
171
172 pub fn remove_side(&self, side: RelaySideId) -> TelemetryResult<()> {
173 self.core.remove_side(side)
174 }
175
176 pub fn set_side_ingress_enabled(
177 &self,
178 side: RelaySideId,
179 enabled: bool,
180 ) -> TelemetryResult<()> {
181 self.core.set_side_ingress_enabled(side, enabled)
182 }
183
184 pub fn set_side_egress_enabled(&self, side: RelaySideId, enabled: bool) -> TelemetryResult<()> {
185 self.core.set_side_egress_enabled(side, enabled)
186 }
187
188 pub fn set_source_route_mode(
189 &self,
190 src: Option<RelaySideId>,
191 mode: RouteSelectionMode,
192 ) -> TelemetryResult<()> {
193 self.core.set_source_route_mode(src, mode)
194 }
195
196 pub fn clear_source_route_mode(&self, src: Option<RelaySideId>) -> TelemetryResult<()> {
197 self.core.clear_source_route_mode(src)
198 }
199
200 pub fn set_route_weight(
201 &self,
202 src: Option<RelaySideId>,
203 dst: RelaySideId,
204 weight: u32,
205 ) -> TelemetryResult<()> {
206 self.core.set_route_weight(src, dst, weight)
207 }
208
209 pub fn clear_route_weight(
210 &self,
211 src: Option<RelaySideId>,
212 dst: RelaySideId,
213 ) -> TelemetryResult<()> {
214 self.core.clear_route_weight(src, dst)
215 }
216
217 pub fn set_route_priority(
218 &self,
219 src: Option<RelaySideId>,
220 dst: RelaySideId,
221 priority: u32,
222 ) -> TelemetryResult<()> {
223 self.core.set_route_priority(src, dst, priority)
224 }
225
226 pub fn clear_route_priority(
227 &self,
228 src: Option<RelaySideId>,
229 dst: RelaySideId,
230 ) -> TelemetryResult<()> {
231 self.core.clear_route_priority(src, dst)
232 }
233
234 pub fn set_route(
235 &self,
236 src: Option<RelaySideId>,
237 dst: RelaySideId,
238 enabled: bool,
239 ) -> TelemetryResult<()> {
240 self.core.set_route(src, dst, enabled)
241 }
242
243 pub fn set_typed_route(
244 &self,
245 src: Option<RelaySideId>,
246 ty: crate::DataType,
247 dst: RelaySideId,
248 enabled: bool,
249 ) -> TelemetryResult<()> {
250 self.core.set_typed_route(src, ty, dst, enabled)
251 }
252
253 pub fn clear_typed_route(
254 &self,
255 src: Option<RelaySideId>,
256 ty: crate::DataType,
257 dst: RelaySideId,
258 ) -> TelemetryResult<()> {
259 self.core.clear_typed_route(src, ty, dst)
260 }
261
262 pub fn clear_route(&self, src: Option<RelaySideId>, dst: RelaySideId) -> TelemetryResult<()> {
263 self.core.clear_route(src, dst)
264 }
265
266 #[cfg(feature = "discovery")]
267 pub fn announce_discovery(&self) -> TelemetryResult<()> {
268 self.core.announce_discovery()
269 }
270
271 #[cfg(feature = "discovery")]
272 pub fn announce_leave(&self) -> TelemetryResult<()> {
273 self.core.announce_leave()
274 }
275
276 #[cfg(feature = "discovery")]
277 pub fn poll_discovery(&self) -> TelemetryResult<bool> {
278 self.core.poll_discovery()
279 }
280
281 #[cfg(feature = "discovery")]
282 pub fn export_topology(&self) -> TopologySnapshot {
283 self.core.export_topology()
284 }
285
286 #[cfg(feature = "discovery")]
287 pub fn client_stats(&self, sender_id: &str) -> Option<ClientStatsSnapshot> {
288 self.core.client_stats(sender_id)
289 }
290
291 pub fn export_runtime_stats(&self) -> RuntimeStatsSnapshot {
292 self.core.export_runtime_stats()
293 }
294
295 pub fn export_memory_layout_json(&self) -> String {
296 self.core.export_memory_layout_json()
297 }
298
299 pub fn rx_packed_from_side(&self, src: RelaySideId, bytes: &[u8]) -> TelemetryResult<()> {
300 self.core
301 .rx_packed_queue_from_side(bytes, src)
302 .map_err(legacy_ingress_error)
303 }
304
305 pub fn rx_from_side(&self, src: RelaySideId, packet: Packet) -> TelemetryResult<()> {
306 self.core
307 .rx_queue_from_side(packet, src)
308 .map_err(legacy_ingress_error)
309 }
310
311 pub fn clear_queues(&self) {
312 self.core.clear_queues()
313 }
314
315 pub fn clear_rx_queue(&self) {
316 self.core.clear_rx_queue()
317 }
318
319 pub fn clear_tx_queue(&self) {
320 self.core.clear_tx_queue()
321 }
322
323 pub fn process_rx_queue(&self) -> TelemetryResult<()> {
324 self.core.process_rx_queue()
325 }
326
327 pub fn process_tx_queue(&self) -> TelemetryResult<()> {
328 self.core.process_tx_queue()
329 }
330
331 pub fn process_all_queues(&self) -> TelemetryResult<()> {
332 self.core.process_all_queues()
333 }
334
335 pub fn process_tx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
336 self.core.process_tx_queue_with_timeout(timeout_ms)
337 }
338
339 pub fn process_rx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
340 self.core.process_rx_queue_with_timeout(timeout_ms)
341 }
342
343 pub fn process_all_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
344 self.core.process_all_queues_with_timeout(timeout_ms)
345 }
346
347 pub fn drain_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
348 self.core.drain_queues_with_timeout(timeout_ms)
349 }
350
351 pub fn periodic(&self, timeout_ms: u32) -> TelemetryResult<()> {
352 self.core.periodic(timeout_ms)
353 }
354}
355
356#[cfg(test)]
359impl core::ops::Deref for Relay {
360 type Target = Router;
361 fn deref(&self) -> &Router {
362 &self.core
363 }
364}
365
366fn legacy_chunk_options(mut opts: RelaySideOptions) -> RelaySideOptions {
370 if opts.max_frame_bytes != 0 {
371 opts.header_template_enabled = true;
372 }
373 opts
374}
375fn legacy_ingress_error(error: TelemetryError) -> TelemetryError {
376 match error {
377 TelemetryError::HandlerError("router: invalid side id") => {
378 TelemetryError::HandlerError("relay: invalid side id")
379 }
380 other => other,
381 }
382}