1use std::collections::{HashMap, VecDeque};
4use std::io::{self, Read, Write};
5use std::os::fd::AsRawFd;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::{Arc, Mutex};
8use std::thread;
9use std::time::{Duration, Instant};
10
11use rns_core::transport::types::{InterfaceId, InterfaceInfo};
12use rns_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey};
13use rns_crypto::sha256::sha256;
14use rns_crypto::OsRng;
15
16use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult, Writer};
17use crate::event::{DynamicInterfaceRegistration, Event, EventSender, InterfaceTelemetry};
18use crate::serial::{Parity, SerialConfig, SerialPort};
19
20pub const WDCL_T_DISCOVER: u8 = 0x00;
21pub const WDCL_T_CONNECT: u8 = 0x01;
22pub const WDCL_T_CMD: u8 = 0x02;
23pub const WDCL_T_LOG: u8 = 0x03;
24pub const WDCL_T_DISP: u8 = 0x04;
25pub const WDCL_T_ENDPOINT_PKT: u8 = 0x05;
26pub const WDCL_T_ENCAP_PROTO: u8 = 0x06;
27pub const WDCL_BROADCAST: [u8; 4] = [0xff; 4];
28
29pub const WDCL_CMD_ENDPOINT_PKT: u16 = 0x0001;
30pub const WDCL_CMD_ENDPOINTS_LIST: u16 = 0x0100;
31pub const WDCL_CMD_REMOTE_DISPLAY: u16 = 0x0a00;
32pub const WDCL_CMD_REMOTE_INPUT: u16 = 0x0a01;
33
34pub const ET_BOARD_INIT: u16 = 0x0003;
35pub const ET_PROTO_WDCL_CONNECTION: u16 = 0x3002;
36pub const ET_PROTO_WDCL_HOST_ENDPOINT: u16 = 0x3003;
37pub const ET_PROTO_WEAVE_EP_ALIVE: u16 = 0x3102;
38pub const ET_PROTO_WEAVE_EP_TIMEOUT: u16 = 0x3103;
39pub const ET_PROTO_WEAVE_EP_VIA: u16 = 0x3104;
40pub const ET_STAT_CPU: u16 = 0xe003;
41pub const ET_STAT_MEMORY: u16 = 0xe005;
42
43pub const WEAVE_HW_MTU: u32 = 1024;
44pub const WEAVE_DEFAULT_BITRATE: u64 = 250_000;
45pub const WEAVE_PEERING_TIMEOUT: f64 = 20.0;
46pub const WEAVE_DUPLICATE_TTL: f64 = 0.75;
47pub const WEAVE_DUPLICATE_CAPACITY: usize = 48;
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct WdclFrame {
51 pub destination: [u8; 4],
52 pub frame_type: u8,
53 pub payload: Vec<u8>,
54}
55
56impl WdclFrame {
57 pub fn encode(&self) -> Vec<u8> {
58 let mut output = Vec::with_capacity(5 + self.payload.len());
59 output.extend_from_slice(&self.destination);
60 output.push(self.frame_type);
61 output.extend_from_slice(&self.payload);
62 output
63 }
64
65 pub fn decode(data: &[u8]) -> Option<Self> {
66 if data.len() < 5 {
67 return None;
68 }
69 Some(Self {
70 destination: data[..4].try_into().ok()?,
71 frame_type: data[4],
72 payload: data[5..].to_vec(),
73 })
74 }
75
76 pub fn command(destination: [u8; 4], command: u16, data: &[u8]) -> Self {
77 let mut payload = Vec::with_capacity(2 + data.len());
78 payload.extend_from_slice(&command.to_be_bytes());
79 payload.extend_from_slice(data);
80 Self {
81 destination,
82 frame_type: WDCL_T_CMD,
83 payload,
84 }
85 }
86
87 pub fn endpoint_packet(destination: [u8; 4], endpoint: [u8; 8], packet: &[u8]) -> Self {
88 let mut data = Vec::with_capacity(8 + packet.len());
89 data.extend_from_slice(&endpoint);
90 data.extend_from_slice(packet);
91 Self::command(destination, WDCL_CMD_ENDPOINT_PKT, &data)
92 }
93}
94
95#[derive(Debug, Clone, PartialEq)]
96pub struct WeaveLogFrame {
97 pub timestamp: f64,
98 pub level: u8,
99 pub event: u16,
100 pub data: Vec<u8>,
101}
102
103#[derive(Debug, Clone, PartialEq)]
104pub enum WeaveInput {
105 Discovery {
106 switch_id: [u8; 4],
107 signing_key: [u8; 32],
108 },
109 EndpointPacket {
110 source: [u8; 8],
111 packet: Vec<u8>,
112 },
113 Log(WeaveLogFrame),
114 Display {
115 offset: usize,
116 total: usize,
117 fragment: Vec<u8>,
118 },
119 Unknown(WdclFrame),
120}
121
122pub fn parse_device_frame(data: &[u8], host_switch_id: [u8; 4]) -> Option<WeaveInput> {
125 let frame = WdclFrame::decode(data)?;
126 match frame.frame_type {
127 WDCL_T_DISCOVER if frame.payload.len() == 96 => {
128 let key: [u8; 32] = frame.payload[..32].try_into().ok()?;
129 let signature: [u8; 64] = frame.payload[32..].try_into().ok()?;
130 if !Ed25519PublicKey::from_bytes(&key)
131 .ok()?
132 .verify(&signature, &frame.destination)
133 {
134 return None;
135 }
136 Some(WeaveInput::Discovery {
137 switch_id: key[28..].try_into().ok()?,
138 signing_key: key,
139 })
140 }
141 WDCL_T_ENDPOINT_PKT if frame.destination == host_switch_id && frame.payload.len() > 8 => {
142 let split = frame.payload.len() - 8;
143 Some(WeaveInput::EndpointPacket {
144 source: frame.payload[split..].try_into().ok()?,
145 packet: frame.payload[..split].to_vec(),
146 })
147 }
148 WDCL_T_LOG if frame.payload.len() >= 9 => {
149 let data = &frame.payload[1..];
151 Some(WeaveInput::Log(WeaveLogFrame {
152 timestamp: u32::from_be_bytes(data[..4].try_into().ok()?) as f64 / 1000.0,
153 level: data[4],
154 event: u16::from_be_bytes(data[5..7].try_into().ok()?),
155 data: data[7..].to_vec(),
156 }))
157 }
158 WDCL_T_DISP if frame.payload.len() >= 9 => Some(WeaveInput::Display {
159 offset: u32::from_be_bytes(frame.payload[1..5].try_into().ok()?) as usize,
160 total: u32::from_be_bytes(frame.payload[5..9].try_into().ok()?) as usize,
161 fragment: frame.payload[9..].to_vec(),
162 }),
163 _ => Some(WeaveInput::Unknown(frame)),
164 }
165}
166
167#[derive(Debug, Clone)]
168pub struct WeavePeerState {
169 pub endpoint_id: [u8; 8],
170 pub via_switch_id: Option<[u8; 4]>,
171 pub last_heard: f64,
172}
173
174#[derive(Debug, Clone, Default)]
175pub struct WeaveState {
176 pub connected: bool,
177 pub switch_id: Option<[u8; 4]>,
178 pub endpoint_id: Option<[u8; 8]>,
179 pub cpu_load: Option<u8>,
180 pub mem_load: Option<f64>,
181 pub peers: HashMap<[u8; 8], WeavePeerState>,
182 pub logs: VecDeque<WeaveLogFrame>,
183 pub display: Vec<u8>,
184 pub unknown_events: VecDeque<WeaveLogFrame>,
185 duplicates: VecDeque<([u8; 32], f64)>,
186}
187
188impl WeaveState {
189 pub fn accept_packet(&mut self, packet: &[u8], now: f64) -> bool {
190 while self
191 .duplicates
192 .front()
193 .is_some_and(|entry| now >= entry.1 + WEAVE_DUPLICATE_TTL)
194 {
195 self.duplicates.pop_front();
196 }
197 let hash = sha256(packet);
198 if self.duplicates.iter().any(|entry| entry.0 == hash) {
199 return false;
200 }
201 if self.duplicates.len() == WEAVE_DUPLICATE_CAPACITY {
202 self.duplicates.pop_front();
203 }
204 self.duplicates.push_back((hash, now));
205 true
206 }
207
208 pub fn handle_log(&mut self, frame: WeaveLogFrame, now: f64) {
209 if self.logs.len() == 1024 {
210 self.logs.pop_front();
211 }
212 self.logs.push_back(frame.clone());
213 match frame.event {
214 ET_BOARD_INIT => {}
215 ET_PROTO_WDCL_CONNECTION => self.connected = true,
216 ET_PROTO_WDCL_HOST_ENDPOINT if frame.data.len() == 8 => {
217 self.endpoint_id = frame.data.as_slice().try_into().ok();
218 }
219 ET_PROTO_WEAVE_EP_ALIVE if frame.data.len() == 8 => {
220 let endpoint_id: [u8; 8] = frame.data.as_slice().try_into().unwrap();
221 self.peers
222 .entry(endpoint_id)
223 .and_modify(|peer| peer.last_heard = now)
224 .or_insert(WeavePeerState {
225 endpoint_id,
226 via_switch_id: None,
227 last_heard: now,
228 });
229 }
230 ET_PROTO_WEAVE_EP_TIMEOUT if frame.data.len() == 8 => {
231 if let Ok(endpoint) = <[u8; 8]>::try_from(frame.data.as_slice()) {
232 self.peers.remove(&endpoint);
233 }
234 }
235 ET_PROTO_WEAVE_EP_VIA if frame.data.len() == 12 => {
236 let endpoint: [u8; 8] = frame.data[..8].try_into().unwrap();
237 let via: [u8; 4] = frame.data[8..].try_into().unwrap();
238 if let Some(peer) = self.peers.get_mut(&endpoint) {
239 peer.via_switch_id = Some(via);
240 }
241 }
242 ET_STAT_CPU if !frame.data.is_empty() => self.cpu_load = Some(frame.data[0]),
243 ET_STAT_MEMORY if frame.data.len() == 8 => {
244 let free = u32::from_be_bytes(frame.data[..4].try_into().unwrap()) as f64;
245 let total = u32::from_be_bytes(frame.data[4..].try_into().unwrap()) as f64;
246 self.mem_load = (total > 0.0).then_some(((total - free) / total) * 100.0);
247 }
248 _ => self.unknown_events.push_back(frame),
249 }
250 }
251
252 pub fn expire_peers(&mut self, now: f64) -> Vec<[u8; 8]> {
253 let expired: Vec<_> = self
254 .peers
255 .iter()
256 .filter_map(|(id, peer)| {
257 (now >= peer.last_heard + WEAVE_PEERING_TIMEOUT).then_some(*id)
258 })
259 .collect();
260 for id in &expired {
261 self.peers.remove(id);
262 }
263 expired
264 }
265
266 pub fn apply_display(&mut self, offset: usize, total: usize, fragment: &[u8]) -> bool {
267 if total > 128 * 64 / 8 || offset > total || fragment.len() > total - offset {
268 return false;
269 }
270 self.display.resize(total, 0);
271 self.display[offset..offset + fragment.len()].copy_from_slice(fragment);
272 offset + fragment.len() == total
273 }
274}
275
276#[derive(Debug, Clone)]
277pub struct WeaveConfig {
278 pub name: String,
279 pub port: String,
280 pub configured_bitrate: u64,
281 pub interface_id: InterfaceId,
282 pub state: Arc<Mutex<WeaveState>>,
283}
284
285struct WeaveSession {
286 config: WeaveConfig,
287 tx: EventSender,
288 next_dynamic_id: Arc<AtomicU64>,
289 mode: u8,
290 gravity: i64,
291 recursive_prs: bool,
292 announces_from_internal: bool,
293 announces_to_internal: Option<bool>,
294 ingress_control: rns_core::transport::types::IngressControlConfig,
295 ifac: Option<crate::ifac::IfacState>,
296 host_identity: Arc<Ed25519PrivateKey>,
297 host_public: [u8; 32],
298 host_switch_id: [u8; 4],
299 writer: Arc<Mutex<Option<std::fs::File>>>,
300 remote_switch_id: Arc<Mutex<Option<[u8; 4]>>>,
301 peer_interfaces: Arc<Mutex<HashMap<[u8; 8], InterfaceId>>>,
302}
303
304impl WeaveSession {
305 fn new(config: WeaveConfig, ctx: &StartContext) -> Arc<Self> {
306 let identity = Ed25519PrivateKey::generate(&mut OsRng);
307 let host_public = identity.public_key().public_bytes();
308 Arc::new(Self {
309 config,
310 tx: ctx.tx.clone(),
311 next_dynamic_id: Arc::clone(&ctx.next_dynamic_id),
312 mode: ctx.mode,
313 gravity: ctx.gravity,
314 recursive_prs: ctx.recursive_prs,
315 announces_from_internal: ctx.announces_from_internal,
316 announces_to_internal: ctx.announces_to_internal,
317 ingress_control: ctx.ingress_control,
318 ifac: ctx.ifac.clone(),
319 host_identity: Arc::new(identity),
320 host_public,
321 host_switch_id: host_public[28..].try_into().unwrap(),
322 writer: Arc::new(Mutex::new(None)),
323 remote_switch_id: Arc::new(Mutex::new(None)),
324 peer_interfaces: Arc::new(Mutex::new(HashMap::new())),
325 })
326 }
327
328 fn spawn(self: &Arc<Self>) -> io::Result<()> {
329 let session = Arc::clone(self);
330 thread::Builder::new()
331 .name(format!("weave-session-{}", self.config.interface_id.0))
332 .spawn(move || session.reconnect_loop())?;
333 Ok(())
334 }
335
336 fn reconnect_loop(self: Arc<Self>) {
337 loop {
338 if let Err(error) = self.run_connection() {
339 log::warn!(
340 "[{}] Weave connection failed: {}; retrying in five seconds",
341 self.config.name,
342 error
343 );
344 }
345 self.disconnect_all();
346 thread::sleep(Duration::from_secs(5));
347 }
348 }
349
350 fn run_connection(self: &Arc<Self>) -> io::Result<()> {
351 let port = SerialPort::open(&SerialConfig {
352 path: self.config.port.clone(),
353 baud: 3_000_000,
354 data_bits: 8,
355 parity: Parity::None,
356 stop_bits: 1,
357 })?;
358 let mut reader = port.reader()?;
359 *super::lock_or_recover(&self.writer, "weave writer") = Some(port.writer()?);
360 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") = None;
361 {
362 let mut state = super::lock_or_recover(&self.config.state, "weave state");
363 state.connected = false;
364 state.switch_id = None;
365 }
366
367 self.send_frame(&WdclFrame {
368 destination: WDCL_BROADCAST,
369 frame_type: WDCL_T_DISCOVER,
370 payload: self.host_switch_id.to_vec(),
371 })?;
372
373 let handshake_deadline = Instant::now() + Duration::from_secs(2);
374 let mut decoder = crate::hdlc::Decoder::with_limits(5, 128 * 1024);
375 let mut buffer = [0u8; 32 * 1024];
376 let mut last_peer_expiry = Instant::now();
377 loop {
378 let mut pollfd = libc::pollfd {
379 fd: reader.as_raw_fd(),
380 events: libc::POLLIN,
381 revents: 0,
382 };
383 let ready = unsafe { libc::poll(&mut pollfd, 1, 250) };
384 if ready < 0 {
385 return Err(io::Error::last_os_error());
386 }
387 if ready > 0 {
388 let read = reader.read(&mut buffer)?;
389 if read == 0 {
390 return Err(io::Error::new(
391 io::ErrorKind::UnexpectedEof,
392 "serial port closed",
393 ));
394 }
395 for frame in decoder.feed(&buffer[..read]) {
396 self.process_frame(&frame)?;
397 }
398 }
399
400 let connected = super::lock_or_recover(&self.config.state, "weave state").connected;
401 if !connected && Instant::now() >= handshake_deadline {
402 return Err(io::Error::new(
403 io::ErrorKind::TimedOut,
404 "WDCL connection handshake timed out",
405 ));
406 }
407 if last_peer_expiry.elapsed() >= Duration::from_secs(1) {
408 self.expire_peers(crate::time::now());
409 last_peer_expiry = Instant::now();
410 }
411 }
412 }
413
414 fn send_frame(&self, frame: &WdclFrame) -> io::Result<()> {
415 let encoded = crate::hdlc::frame(&frame.encode());
416 let mut writer = super::lock_or_recover(&self.writer, "weave writer");
417 writer
418 .as_mut()
419 .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "Weave is offline"))?
420 .write_all(&encoded)
421 }
422
423 fn process_frame(self: &Arc<Self>, raw: &[u8]) -> io::Result<()> {
424 let Some(input) = parse_device_frame(raw, self.host_switch_id) else {
425 return Ok(());
426 };
427 match input {
428 WeaveInput::Discovery {
429 switch_id,
430 signing_key: _,
431 } => {
432 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") =
433 Some(switch_id);
434 super::lock_or_recover(&self.config.state, "weave state").switch_id =
435 Some(switch_id);
436 let mut payload = self.host_public.to_vec();
437 payload.extend_from_slice(&self.host_identity.sign(&switch_id));
438 self.send_frame(&WdclFrame {
439 destination: switch_id,
440 frame_type: WDCL_T_CONNECT,
441 payload,
442 })?;
443 }
444 WeaveInput::EndpointPacket { source, packet } => {
445 let now = crate::time::now();
446 {
447 let mut state = super::lock_or_recover(&self.config.state, "weave state");
448 state
449 .peers
450 .entry(source)
451 .and_modify(|peer| peer.last_heard = now)
452 .or_insert(WeavePeerState {
453 endpoint_id: source,
454 via_switch_id: None,
455 last_heard: now,
456 });
457 if !state.accept_packet(&packet, now) {
458 return Ok(());
459 }
460 }
461 let interface_id = self.ensure_peer(source, now);
462 let _ = self.tx.send(Event::Frame {
463 interface_id,
464 data: packet,
465 rssi: None,
466 snr: None,
467 });
468 }
469 WeaveInput::Log(frame) => self.process_log(frame),
470 WeaveInput::Display {
471 offset,
472 total,
473 fragment,
474 } => {
475 super::lock_or_recover(&self.config.state, "weave state")
476 .apply_display(offset, total, &fragment);
477 }
478 WeaveInput::Unknown(_) => {}
479 }
480 Ok(())
481 }
482
483 fn process_log(self: &Arc<Self>, frame: WeaveLogFrame) {
484 let now = crate::time::now();
485 let event = frame.event;
486 let data = frame.data.clone();
487 super::lock_or_recover(&self.config.state, "weave state").handle_log(frame, now);
488
489 match event {
490 ET_PROTO_WDCL_CONNECTION => {
491 let _ = self
492 .tx
493 .send(Event::InterfaceUp(self.config.interface_id, None, None));
494 self.publish_parent_telemetry();
495 }
496 ET_PROTO_WEAVE_EP_ALIVE if data.len() == 8 => {
497 let endpoint: [u8; 8] = data.as_slice().try_into().unwrap();
498 self.ensure_peer(endpoint, now);
499 }
500 ET_PROTO_WEAVE_EP_TIMEOUT if data.len() == 8 => {
501 let endpoint: [u8; 8] = data.as_slice().try_into().unwrap();
502 self.remove_peer(endpoint);
503 }
504 ET_PROTO_WEAVE_EP_VIA if data.len() == 12 => {
505 let endpoint: [u8; 8] = data[..8].try_into().unwrap();
506 let via: [u8; 4] = data[8..].try_into().unwrap();
507 if let Some(interface_id) =
508 super::lock_or_recover(&self.peer_interfaces, "weave peer interfaces")
509 .get(&endpoint)
510 .copied()
511 {
512 let _ = self.tx.send(Event::InterfaceTelemetry {
513 interface_id,
514 telemetry: InterfaceTelemetry {
515 via_switch_id: Some(via),
516 ..Default::default()
517 },
518 });
519 }
520 }
521 ET_PROTO_WDCL_HOST_ENDPOINT | ET_STAT_CPU | ET_STAT_MEMORY => {
522 self.publish_parent_telemetry()
523 }
524 _ => {}
525 }
526 }
527
528 fn ensure_peer(self: &Arc<Self>, endpoint: [u8; 8], now: f64) -> InterfaceId {
529 if let Some(id) = super::lock_or_recover(&self.peer_interfaces, "weave peers")
530 .get(&endpoint)
531 .copied()
532 {
533 return id;
534 }
535 let id = InterfaceId(self.next_dynamic_id.fetch_add(1, Ordering::Relaxed));
536 super::lock_or_recover(&self.peer_interfaces, "weave peers").insert(endpoint, id);
537 let via_switch_id = super::lock_or_recover(&self.config.state, "weave state")
538 .peers
539 .get(&endpoint)
540 .and_then(|peer| peer.via_switch_id);
541 let name = format!("WeaveInterfacePeer[{}]", hex(&endpoint));
542 let info = InterfaceInfo {
543 id,
544 name,
545 mode: self.mode,
546 gravity: self.gravity,
547 recursive_prs: self.recursive_prs,
548 announces_from_internal: self.announces_from_internal,
549 announces_to_internal: self.announces_to_internal,
550 out_capable: true,
551 in_capable: true,
552 bitrate: Some(self.config.configured_bitrate),
553 airtime_profile: None,
554 announce_rate_target: None,
555 announce_rate_grace: 0,
556 announce_rate_penalty: 0.0,
557 announce_cap: rns_core::constants::ANNOUNCE_CAP,
558 is_local_client: false,
559 wants_tunnel: false,
560 tunnel_id: None,
561 mtu: WEAVE_HW_MTU,
562 ia_freq: 0.0,
563 ip_freq: 0.0,
564 op_freq: 0.0,
565 op_samples: 0,
566 started: now,
567 ingress_control: self.ingress_control,
568 };
569 let _ = self.tx.send(Event::DynamicInterfaceUp {
570 id,
571 writer: Box::new(WeavePeerWriter {
572 session: Arc::clone(self),
573 endpoint,
574 }),
575 registration: Box::new(DynamicInterfaceRegistration {
576 info,
577 interface_type: "WeaveInterfacePeer".into(),
578 parent_id: self.config.interface_id,
579 telemetry: InterfaceTelemetry {
580 via_switch_id,
581 ..Default::default()
582 },
583 ifac: self.ifac.clone(),
584 }),
585 });
586 self.publish_parent_telemetry();
587 id
588 }
589
590 fn remove_peer(&self, endpoint: [u8; 8]) {
591 if let Some(id) =
592 super::lock_or_recover(&self.peer_interfaces, "weave peers").remove(&endpoint)
593 {
594 super::lock_or_recover(&self.config.state, "weave state")
595 .peers
596 .remove(&endpoint);
597 let _ = self.tx.send(Event::InterfaceDown(id));
598 self.publish_parent_telemetry();
599 }
600 }
601
602 fn expire_peers(&self, now: f64) {
603 let expired = super::lock_or_recover(&self.config.state, "weave state").expire_peers(now);
604 for endpoint in &expired {
605 if let Some(id) =
606 super::lock_or_recover(&self.peer_interfaces, "weave peers").remove(endpoint)
607 {
608 let _ = self.tx.send(Event::InterfaceDown(id));
609 }
610 }
611 if !super::lock_or_recover(&self.peer_interfaces, "weave peers").is_empty()
612 || !expired.is_empty()
613 {
614 self.publish_parent_telemetry();
615 }
616 }
617
618 fn publish_parent_telemetry(&self) {
619 let state = super::lock_or_recover(&self.config.state, "weave state");
620 let _ = self.tx.send(Event::InterfaceTelemetry {
621 interface_id: self.config.interface_id,
622 telemetry: InterfaceTelemetry {
623 cpu_load: state.cpu_load.map(f64::from),
624 mem_load: state.mem_load,
625 switch_id: state.switch_id,
626 endpoint_id: state.endpoint_id,
627 via_switch_id: None,
628 peers: Some(state.peers.len()),
629 },
630 });
631 }
632
633 fn disconnect_all(&self) {
634 *super::lock_or_recover(&self.writer, "weave writer") = None;
635 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") = None;
636 {
637 let mut state = super::lock_or_recover(&self.config.state, "weave state");
638 state.connected = false;
639 state.switch_id = None;
640 state.peers.clear();
641 }
642 let peers: Vec<_> = super::lock_or_recover(&self.peer_interfaces, "weave peers")
643 .drain()
644 .map(|(_, id)| id)
645 .collect();
646 for id in peers {
647 let _ = self.tx.send(Event::InterfaceDown(id));
648 }
649 let _ = self.tx.send(Event::InterfaceDown(self.config.interface_id));
650 self.publish_parent_telemetry();
651 }
652}
653
654struct WeavePeerWriter {
655 session: Arc<WeaveSession>,
656 endpoint: [u8; 8],
657}
658
659impl Writer for WeavePeerWriter {
660 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
661 let remote = super::lock_or_recover(&self.session.remote_switch_id, "weave remote switch")
662 .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "Weave is offline"))?;
663 self.session
664 .send_frame(&WdclFrame::endpoint_packet(remote, self.endpoint, data))
665 }
666}
667
668fn hex(bytes: &[u8]) -> String {
669 bytes.iter().map(|byte| format!("{byte:02x}")).collect()
670}
671
672pub struct WeaveFactory;
673struct ParentWriter;
674impl Writer for ParentWriter {
675 fn send_frame_confirmed(&mut self, _data: &[u8]) -> io::Result<()> {
676 Err(io::Error::new(
677 io::ErrorKind::Unsupported,
678 "Weave parent is not a transmitting peer",
679 ))
680 }
681 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
682 Ok(())
683 }
684}
685
686impl InterfaceFactory for WeaveFactory {
687 fn type_name(&self) -> &str {
688 "WeaveInterface"
689 }
690 fn default_ifac_size(&self) -> usize {
691 16
692 }
693
694 fn parse_config(
695 &self,
696 name: &str,
697 id: InterfaceId,
698 params: &HashMap<String, String>,
699 ) -> Result<Box<dyn InterfaceConfigData>, String> {
700 let port = params
701 .get("port")
702 .cloned()
703 .ok_or_else(|| "WeaveInterface requires 'port'".to_string())?;
704 let configured_bitrate = params
705 .get("configured_bitrate")
706 .and_then(|v| v.parse().ok())
707 .unwrap_or(WEAVE_DEFAULT_BITRATE);
708 Ok(Box::new(WeaveConfig {
709 name: name.into(),
710 port,
711 configured_bitrate,
712 interface_id: id,
713 state: Arc::new(Mutex::new(WeaveState::default())),
714 }))
715 }
716
717 fn start(
718 &self,
719 config: Box<dyn InterfaceConfigData>,
720 ctx: StartContext,
721 ) -> io::Result<StartResult> {
722 let config = *config
723 .into_any()
724 .downcast::<WeaveConfig>()
725 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong Weave config type"))?;
726 let session = WeaveSession::new(config.clone(), &ctx);
727 session.spawn()?;
728 let info = InterfaceInfo {
729 id: config.interface_id,
730 name: config.name.clone(),
731 mode: ctx.mode,
732 gravity: ctx.gravity,
733 recursive_prs: ctx.recursive_prs,
734 announces_from_internal: ctx.announces_from_internal,
735 announces_to_internal: ctx.announces_to_internal,
736 out_capable: false,
737 in_capable: true,
738 bitrate: Some(config.configured_bitrate),
739 airtime_profile: None,
740 announce_rate_target: None,
741 announce_rate_grace: 0,
742 announce_rate_penalty: 0.0,
743 announce_cap: rns_core::constants::ANNOUNCE_CAP,
744 is_local_client: false,
745 wants_tunnel: false,
746 tunnel_id: None,
747 mtu: WEAVE_HW_MTU,
748 ia_freq: 0.0,
749 ip_freq: 0.0,
750 op_freq: 0.0,
751 op_samples: 0,
752 started: crate::time::now(),
753 ingress_control: ctx.ingress_control,
754 };
755 Ok(StartResult::Simple {
756 control: None,
757 id: config.interface_id,
758 info,
759 writer: Box::new(ParentWriter),
760 interface_type_name: "WeaveInterface".into(),
761 })
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use rns_crypto::ed25519::Ed25519PrivateKey;
769 use rns_crypto::FixedRng;
770
771 fn log_frame(host: [u8; 4], event: u16, data: &[u8]) -> Vec<u8> {
772 let mut payload = vec![0, 0, 0, 0, 1, 2];
773 payload.extend_from_slice(&event.to_be_bytes());
774 payload.extend_from_slice(data);
775 WdclFrame {
776 destination: host,
777 frame_type: WDCL_T_LOG,
778 payload,
779 }
780 .encode()
781 }
782
783 #[test]
784 fn wdcl_fragmented_hdlc_and_discovery_signature() {
785 let private = Ed25519PrivateKey::generate(&mut FixedRng::new(&[7; 64]));
786 let key = private.public_key().public_bytes();
787 let signed_id = [1, 2, 3, 4];
788 let mut payload = key.to_vec();
789 payload.extend_from_slice(&private.sign(&signed_id));
790 let frame = WdclFrame {
791 destination: signed_id,
792 frame_type: WDCL_T_DISCOVER,
793 payload,
794 }
795 .encode();
796 assert!(matches!(
797 parse_device_frame(&frame, [9; 4]),
798 Some(WeaveInput::Discovery { .. })
799 ));
800
801 let wire = crate::hdlc::frame(&frame);
802 let mut decoder = crate::hdlc::Decoder::with_min_frame_size(5);
803 assert!(decoder.feed(&wire[..3]).is_empty());
804 assert_eq!(decoder.feed(&wire[3..]), vec![frame]);
805 }
806
807 #[test]
808 fn board_init_unknown_and_duplicate_state_are_preserved() {
809 let mut state = WeaveState::default();
810 state.handle_log(
811 WeaveLogFrame {
812 timestamp: 1.0,
813 level: 1,
814 event: ET_BOARD_INIT,
815 data: vec![1],
816 },
817 1.0,
818 );
819 state.handle_log(
820 WeaveLogFrame {
821 timestamp: 2.0,
822 level: 1,
823 event: 0x7777,
824 data: vec![2],
825 },
826 2.0,
827 );
828 assert_eq!(state.logs.len(), 2);
829 assert_eq!(state.unknown_events.len(), 1);
830 assert!(state.accept_packet(b"packet", 3.0));
831 assert!(!state.accept_packet(b"packet", 3.1));
832 assert!(state.accept_packet(b"packet", 4.0));
833 }
834
835 #[test]
836 fn simulated_session_authenticates_and_manages_endpoint_lifecycle() {
837 let (tx, rx) = crate::event::channel();
838 let config = WeaveConfig {
839 name: "weave-test".into(),
840 port: "/dev/null".into(),
841 configured_bitrate: WEAVE_DEFAULT_BITRATE,
842 interface_id: InterfaceId(55),
843 state: Arc::new(Mutex::new(WeaveState::default())),
844 };
845 let context = StartContext {
846 tx,
847 next_dynamic_id: Arc::new(AtomicU64::new(10_000)),
848 mode: rns_core::constants::MODE_GATEWAY,
849 gravity: 0,
850 recursive_prs: true,
851 announces_from_internal: false,
852 announces_to_internal: None,
853 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
854 ifac: None,
855 underlay_mark: None,
856 };
857 let session = WeaveSession::new(config, &context);
858 *super::super::lock_or_recover(&session.writer, "test writer") =
859 Some(tempfile::tempfile().unwrap());
860
861 let remote = Ed25519PrivateKey::generate(&mut FixedRng::new(&[0x44; 64]));
862 let remote_public = remote.public_key().public_bytes();
863 let mut discovery_payload = remote_public.to_vec();
864 discovery_payload.extend_from_slice(&remote.sign(&session.host_switch_id));
865 session
866 .process_frame(
867 &WdclFrame {
868 destination: session.host_switch_id,
869 frame_type: WDCL_T_DISCOVER,
870 payload: discovery_payload,
871 }
872 .encode(),
873 )
874 .unwrap();
875 assert_eq!(
876 *super::super::lock_or_recover(&session.remote_switch_id, "test remote"),
877 Some(remote_public[28..].try_into().unwrap())
878 );
879
880 session
881 .process_frame(&log_frame(
882 session.host_switch_id,
883 ET_PROTO_WDCL_CONNECTION,
884 &[1],
885 ))
886 .unwrap();
887 assert!(matches!(
888 rx.recv_timeout(Duration::from_secs(1)).unwrap(),
889 Event::InterfaceUp(InterfaceId(55), None, None)
890 ));
891
892 let endpoint = [0x77; 8];
893 session
894 .process_frame(&log_frame(
895 session.host_switch_id,
896 ET_PROTO_WEAVE_EP_ALIVE,
897 &endpoint,
898 ))
899 .unwrap();
900 let peer_id = loop {
901 if let Event::DynamicInterfaceUp {
902 id, registration, ..
903 } = rx.recv_timeout(Duration::from_secs(1)).unwrap()
904 {
905 assert_eq!(registration.interface_type, "WeaveInterfacePeer");
906 assert_eq!(registration.parent_id, InterfaceId(55));
907 break id;
908 }
909 };
910
911 let mut endpoint_payload = b"rns-packet".to_vec();
912 endpoint_payload.extend_from_slice(&endpoint);
913 let endpoint_frame = WdclFrame {
914 destination: session.host_switch_id,
915 frame_type: WDCL_T_ENDPOINT_PKT,
916 payload: endpoint_payload,
917 }
918 .encode();
919 session.process_frame(&endpoint_frame).unwrap();
920 assert!(loop {
921 if let Event::Frame {
922 interface_id, data, ..
923 } = rx.recv_timeout(Duration::from_secs(1)).unwrap()
924 {
925 break interface_id == peer_id && data == b"rns-packet";
926 }
927 });
928 session.process_frame(&endpoint_frame).unwrap();
929 assert!(rx.recv_timeout(Duration::from_millis(50)).is_err());
930
931 session.expire_peers(crate::time::now() + WEAVE_PEERING_TIMEOUT + 1.0);
932 assert!(loop {
933 if let Event::InterfaceDown(id) = rx.recv_timeout(Duration::from_secs(1)).unwrap() {
934 break id == peer_id;
935 }
936 });
937 }
938}