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::{
18 DynamicInterfaceRegistration, Event, EventSender, InterfaceTelemetry, LoopContext, LoopSource,
19 SourceStatus,
20};
21use crate::interface::device_writer::DeviceWriter;
22use crate::serial::{Parity, SerialConfig, SerialPort};
23
24pub const WDCL_T_DISCOVER: u8 = 0x00;
25pub const WDCL_T_CONNECT: u8 = 0x01;
26pub const WDCL_T_CMD: u8 = 0x02;
27pub const WDCL_T_LOG: u8 = 0x03;
28pub const WDCL_T_DISP: u8 = 0x04;
29pub const WDCL_T_ENDPOINT_PKT: u8 = 0x05;
30pub const WDCL_T_ENCAP_PROTO: u8 = 0x06;
31pub const WDCL_BROADCAST: [u8; 4] = [0xff; 4];
32
33pub const WDCL_CMD_ENDPOINT_PKT: u16 = 0x0001;
34pub const WDCL_CMD_ENDPOINTS_LIST: u16 = 0x0100;
35pub const WDCL_CMD_REMOTE_DISPLAY: u16 = 0x0a00;
36pub const WDCL_CMD_REMOTE_INPUT: u16 = 0x0a01;
37
38pub const ET_BOARD_INIT: u16 = 0x0003;
39pub const ET_PROTO_WDCL_CONNECTION: u16 = 0x3002;
40pub const ET_PROTO_WDCL_HOST_ENDPOINT: u16 = 0x3003;
41pub const ET_PROTO_WEAVE_EP_ALIVE: u16 = 0x3102;
42pub const ET_PROTO_WEAVE_EP_TIMEOUT: u16 = 0x3103;
43pub const ET_PROTO_WEAVE_EP_VIA: u16 = 0x3104;
44pub const ET_STAT_CPU: u16 = 0xe003;
45pub const ET_STAT_MEMORY: u16 = 0xe005;
46
47pub const WEAVE_HW_MTU: u32 = 1024;
48pub const WEAVE_DEFAULT_BITRATE: u64 = 250_000;
49pub const WEAVE_PEERING_TIMEOUT: f64 = 20.0;
50pub const WEAVE_DUPLICATE_TTL: f64 = 0.75;
51pub const WEAVE_DUPLICATE_CAPACITY: usize = 48;
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct WdclFrame {
55 pub destination: [u8; 4],
56 pub frame_type: u8,
57 pub payload: Vec<u8>,
58}
59
60impl WdclFrame {
61 pub fn encode(&self) -> Vec<u8> {
62 let mut output = Vec::with_capacity(5 + self.payload.len());
63 output.extend_from_slice(&self.destination);
64 output.push(self.frame_type);
65 output.extend_from_slice(&self.payload);
66 output
67 }
68
69 pub fn decode(data: &[u8]) -> Option<Self> {
70 if data.len() < 5 {
71 return None;
72 }
73 Some(Self {
74 destination: data[..4].try_into().ok()?,
75 frame_type: data[4],
76 payload: data[5..].to_vec(),
77 })
78 }
79
80 pub fn command(destination: [u8; 4], command: u16, data: &[u8]) -> Self {
81 let mut payload = Vec::with_capacity(2 + data.len());
82 payload.extend_from_slice(&command.to_be_bytes());
83 payload.extend_from_slice(data);
84 Self {
85 destination,
86 frame_type: WDCL_T_CMD,
87 payload,
88 }
89 }
90
91 pub fn endpoint_packet(destination: [u8; 4], endpoint: [u8; 8], packet: &[u8]) -> Self {
92 let mut data = Vec::with_capacity(8 + packet.len());
93 data.extend_from_slice(&endpoint);
94 data.extend_from_slice(packet);
95 Self::command(destination, WDCL_CMD_ENDPOINT_PKT, &data)
96 }
97}
98
99#[derive(Debug, Clone, PartialEq)]
100pub struct WeaveLogFrame {
101 pub timestamp: f64,
102 pub level: u8,
103 pub event: u16,
104 pub data: Vec<u8>,
105}
106
107#[derive(Debug, Clone, PartialEq)]
108pub enum WeaveInput {
109 Discovery {
110 switch_id: [u8; 4],
111 signing_key: [u8; 32],
112 },
113 EndpointPacket {
114 source: [u8; 8],
115 packet: Vec<u8>,
116 },
117 Log(WeaveLogFrame),
118 Display {
119 offset: usize,
120 total: usize,
121 fragment: Vec<u8>,
122 },
123 Unknown(WdclFrame),
124}
125
126pub fn parse_device_frame(data: &[u8], host_switch_id: [u8; 4]) -> Option<WeaveInput> {
129 let frame = WdclFrame::decode(data)?;
130 match frame.frame_type {
131 WDCL_T_DISCOVER if frame.payload.len() == 96 => {
132 let key: [u8; 32] = frame.payload[..32].try_into().ok()?;
133 let signature: [u8; 64] = frame.payload[32..].try_into().ok()?;
134 if !Ed25519PublicKey::from_bytes(&key)
135 .ok()?
136 .verify(&signature, &frame.destination)
137 {
138 return None;
139 }
140 Some(WeaveInput::Discovery {
141 switch_id: key[28..].try_into().ok()?,
142 signing_key: key,
143 })
144 }
145 WDCL_T_ENDPOINT_PKT if frame.destination == host_switch_id && frame.payload.len() > 8 => {
146 let split = frame.payload.len() - 8;
147 Some(WeaveInput::EndpointPacket {
148 source: frame.payload[split..].try_into().ok()?,
149 packet: frame.payload[..split].to_vec(),
150 })
151 }
152 WDCL_T_LOG if frame.payload.len() >= 9 => {
153 let data = &frame.payload[1..];
155 Some(WeaveInput::Log(WeaveLogFrame {
156 timestamp: u32::from_be_bytes(data[..4].try_into().ok()?) as f64 / 1000.0,
157 level: data[4],
158 event: u16::from_be_bytes(data[5..7].try_into().ok()?),
159 data: data[7..].to_vec(),
160 }))
161 }
162 WDCL_T_DISP if frame.payload.len() >= 9 => Some(WeaveInput::Display {
163 offset: u32::from_be_bytes(frame.payload[1..5].try_into().ok()?) as usize,
164 total: u32::from_be_bytes(frame.payload[5..9].try_into().ok()?) as usize,
165 fragment: frame.payload[9..].to_vec(),
166 }),
167 _ => Some(WeaveInput::Unknown(frame)),
168 }
169}
170
171#[derive(Debug, Clone)]
172pub struct WeavePeerState {
173 pub endpoint_id: [u8; 8],
174 pub via_switch_id: Option<[u8; 4]>,
175 pub last_heard: f64,
176}
177
178#[derive(Debug, Clone, Default)]
179pub struct WeaveState {
180 pub connected: bool,
181 pub switch_id: Option<[u8; 4]>,
182 pub endpoint_id: Option<[u8; 8]>,
183 pub cpu_load: Option<u8>,
184 pub mem_load: Option<f64>,
185 pub peers: HashMap<[u8; 8], WeavePeerState>,
186 pub logs: VecDeque<WeaveLogFrame>,
187 pub display: Vec<u8>,
188 pub unknown_events: VecDeque<WeaveLogFrame>,
189 duplicates: VecDeque<([u8; 32], f64)>,
190}
191
192impl WeaveState {
193 pub fn accept_packet(&mut self, packet: &[u8], now: f64) -> bool {
194 while self
195 .duplicates
196 .front()
197 .is_some_and(|entry| now >= entry.1 + WEAVE_DUPLICATE_TTL)
198 {
199 self.duplicates.pop_front();
200 }
201 let hash = sha256(packet);
202 if self.duplicates.iter().any(|entry| entry.0 == hash) {
203 return false;
204 }
205 if self.duplicates.len() == WEAVE_DUPLICATE_CAPACITY {
206 self.duplicates.pop_front();
207 }
208 self.duplicates.push_back((hash, now));
209 true
210 }
211
212 pub fn handle_log(&mut self, frame: WeaveLogFrame, now: f64) {
213 if self.logs.len() == 1024 {
214 self.logs.pop_front();
215 }
216 self.logs.push_back(frame.clone());
217 match frame.event {
218 ET_BOARD_INIT => {}
219 ET_PROTO_WDCL_CONNECTION => self.connected = true,
220 ET_PROTO_WDCL_HOST_ENDPOINT if frame.data.len() == 8 => {
221 self.endpoint_id = frame.data.as_slice().try_into().ok();
222 }
223 ET_PROTO_WEAVE_EP_ALIVE if frame.data.len() == 8 => {
224 let endpoint_id: [u8; 8] = frame.data.as_slice().try_into().unwrap();
225 self.peers
226 .entry(endpoint_id)
227 .and_modify(|peer| peer.last_heard = now)
228 .or_insert(WeavePeerState {
229 endpoint_id,
230 via_switch_id: None,
231 last_heard: now,
232 });
233 }
234 ET_PROTO_WEAVE_EP_TIMEOUT if frame.data.len() == 8 => {
235 if let Ok(endpoint) = <[u8; 8]>::try_from(frame.data.as_slice()) {
236 self.peers.remove(&endpoint);
237 }
238 }
239 ET_PROTO_WEAVE_EP_VIA if frame.data.len() == 12 => {
240 let endpoint: [u8; 8] = frame.data[..8].try_into().unwrap();
241 let via: [u8; 4] = frame.data[8..].try_into().unwrap();
242 if let Some(peer) = self.peers.get_mut(&endpoint) {
243 peer.via_switch_id = Some(via);
244 }
245 }
246 ET_STAT_CPU if !frame.data.is_empty() => self.cpu_load = Some(frame.data[0]),
247 ET_STAT_MEMORY if frame.data.len() == 8 => {
248 let free = u32::from_be_bytes(frame.data[..4].try_into().unwrap()) as f64;
249 let total = u32::from_be_bytes(frame.data[4..].try_into().unwrap()) as f64;
250 self.mem_load = (total > 0.0).then_some(((total - free) / total) * 100.0);
251 }
252 _ => self.unknown_events.push_back(frame),
253 }
254 }
255
256 pub fn expire_peers(&mut self, now: f64) -> Vec<[u8; 8]> {
257 let expired: Vec<_> = self
258 .peers
259 .iter()
260 .filter_map(|(id, peer)| {
261 (now >= peer.last_heard + WEAVE_PEERING_TIMEOUT).then_some(*id)
262 })
263 .collect();
264 for id in &expired {
265 self.peers.remove(id);
266 }
267 expired
268 }
269
270 pub fn apply_display(&mut self, offset: usize, total: usize, fragment: &[u8]) -> bool {
271 if total > 128 * 64 / 8 || offset > total || fragment.len() > total - offset {
272 return false;
273 }
274 self.display.resize(total, 0);
275 self.display[offset..offset + fragment.len()].copy_from_slice(fragment);
276 offset + fragment.len() == total
277 }
278}
279
280#[derive(Debug, Clone)]
281pub struct WeaveConfig {
282 pub name: String,
283 pub port: String,
284 pub configured_bitrate: u64,
285 pub interface_id: InterfaceId,
286 pub state: Arc<Mutex<WeaveState>>,
287}
288
289struct WeaveSession {
290 config: WeaveConfig,
291 tx: EventSender,
292 next_dynamic_id: Arc<AtomicU64>,
293 mode: u8,
294 gravity: i64,
295 recursive_prs: bool,
296 announces_from_internal: bool,
297 announces_to_internal: Option<bool>,
298 ingress_control: rns_core::transport::types::IngressControlConfig,
299 ifac: Option<crate::ifac::IfacState>,
300 host_identity: Arc<Ed25519PrivateKey>,
301 host_public: [u8; 32],
302 host_switch_id: [u8; 4],
303 writer: Arc<Mutex<Option<DeviceWriter<std::fs::File>>>>,
304 remote_switch_id: Arc<Mutex<Option<[u8; 4]>>>,
305 peer_interfaces: Arc<Mutex<HashMap<[u8; 8], InterfaceId>>>,
306}
307
308impl WeaveSession {
309 fn new(config: WeaveConfig, ctx: &StartContext) -> Arc<Self> {
310 let identity = Ed25519PrivateKey::generate(&mut OsRng);
311 let host_public = identity.public_key().public_bytes();
312 Arc::new(Self {
313 config,
314 tx: ctx.tx.clone(),
315 next_dynamic_id: Arc::clone(&ctx.next_dynamic_id),
316 mode: ctx.mode,
317 gravity: ctx.gravity,
318 recursive_prs: ctx.recursive_prs,
319 announces_from_internal: ctx.announces_from_internal,
320 announces_to_internal: ctx.announces_to_internal,
321 ingress_control: ctx.ingress_control,
322 ifac: ctx.ifac.clone(),
323 host_identity: Arc::new(identity),
324 host_public,
325 host_switch_id: host_public[28..].try_into().unwrap(),
326 writer: Arc::new(Mutex::new(None)),
327 remote_switch_id: Arc::new(Mutex::new(None)),
328 peer_interfaces: Arc::new(Mutex::new(HashMap::new())),
329 })
330 }
331
332 fn spawn(self: &Arc<Self>) -> io::Result<()> {
333 let session = Arc::clone(self);
334 if crate::engine::FOLD_IO {
335 thread::Builder::new()
336 .name(format!("weave-connect-{}", self.config.interface_id.0))
337 .spawn(move || session.connect_folded(false))?;
338 return Ok(());
339 }
340 thread::Builder::new()
341 .name(format!("weave-session-{}", self.config.interface_id.0))
342 .spawn(move || session.reconnect_loop())?;
343 Ok(())
344 }
345
346 fn reconnect_loop(self: Arc<Self>) {
347 loop {
348 if let Err(error) = self.run_connection() {
349 log::warn!(
350 "[{}] Weave connection failed: {}; retrying in five seconds",
351 self.config.name,
352 error
353 );
354 }
355 self.disconnect_all();
356 thread::sleep(Duration::from_secs(5));
357 }
358 }
359
360 fn run_connection(self: &Arc<Self>) -> io::Result<()> {
361 let port = SerialPort::open(&SerialConfig {
362 path: self.config.port.clone(),
363 baud: 3_000_000,
364 data_bits: 8,
365 parity: Parity::None,
366 stop_bits: 1,
367 })?;
368 let mut reader = port.reader()?;
369 *super::lock_or_recover(&self.writer, "weave writer") =
370 Some(DeviceWriter::new(port.writer()?));
371 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") = None;
372 {
373 let mut state = super::lock_or_recover(&self.config.state, "weave state");
374 state.connected = false;
375 state.switch_id = None;
376 }
377
378 self.send_frame(&WdclFrame {
379 destination: WDCL_BROADCAST,
380 frame_type: WDCL_T_DISCOVER,
381 payload: self.host_switch_id.to_vec(),
382 })?;
383
384 let handshake_deadline = Instant::now() + Duration::from_secs(2);
385 let mut decoder = crate::hdlc::Decoder::with_limits(5, 128 * 1024);
386 let mut buffer = [0u8; 32 * 1024];
387 let mut last_peer_expiry = Instant::now();
388 loop {
389 let mut pollfd = libc::pollfd {
390 fd: reader.as_raw_fd(),
391 events: libc::POLLIN,
392 revents: 0,
393 };
394 let ready = unsafe { libc::poll(&mut pollfd, 1, 250) };
395 if ready < 0 {
396 return Err(io::Error::last_os_error());
397 }
398 if ready > 0 {
399 let read = reader.read(&mut buffer)?;
400 if read == 0 {
401 return Err(io::Error::new(
402 io::ErrorKind::UnexpectedEof,
403 "serial port closed",
404 ));
405 }
406 for frame in decoder.feed(&buffer[..read]) {
407 self.process_frame(&frame)?;
408 }
409 }
410
411 let connected = super::lock_or_recover(&self.config.state, "weave state").connected;
412 if !connected && Instant::now() >= handshake_deadline {
413 return Err(io::Error::new(
414 io::ErrorKind::TimedOut,
415 "WDCL connection handshake timed out",
416 ));
417 }
418 if last_peer_expiry.elapsed() >= Duration::from_secs(1) {
419 self.expire_peers(crate::time::now());
420 last_peer_expiry = Instant::now();
421 }
422 }
423 }
424
425 fn emit(&self, event: Event) {
428 if crate::engine::FOLD_IO {
429 let _ = self.tx.push_from_loop([event]);
430 } else {
431 let _ = self.tx.send(event);
432 }
433 }
434
435 fn connect_folded(self: Arc<Self>, wait_first: bool) {
439 let mut wait = wait_first;
440 loop {
441 if wait {
442 thread::sleep(Duration::from_secs(5));
443 }
444 wait = true;
445 match self.open_folded() {
446 Ok(()) => return,
447 Err(error) => log::warn!(
448 "[{}] Weave connection failed: {}; retrying in five seconds",
449 self.config.name,
450 error
451 ),
452 }
453 }
454 }
455
456 fn open_folded(self: &Arc<Self>) -> io::Result<()> {
457 let port = SerialPort::open(&SerialConfig {
458 path: self.config.port.clone(),
459 baud: 3_000_000,
460 data_bits: 8,
461 parity: Parity::None,
462 stop_bits: 1,
463 })?;
464 let reader = port.reader()?;
465 *super::lock_or_recover(&self.writer, "weave writer") = Some(DeviceWriter::driven(
466 port.writer()?,
467 Arc::clone(self.tx.poller()),
468 super::loop_writer::write_key(self.config.interface_id),
469 &self.config.name,
470 ));
471 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") = None;
472 {
473 let mut state = super::lock_or_recover(&self.config.state, "weave state");
474 state.connected = false;
475 state.switch_id = None;
476 }
477 super::stream_read::set_nonblocking(reader.as_raw_fd())?;
480 self.send_frame(&WdclFrame {
481 destination: WDCL_BROADCAST,
482 frame_type: WDCL_T_DISCOVER,
483 payload: self.host_switch_id.to_vec(),
484 })?;
485 self.tx.register_source(
486 super::loop_writer::read_key(self.config.interface_id),
487 Box::new(WeaveSource {
488 reader,
489 decoder: crate::hdlc::Decoder::with_limits(5, 128 * 1024),
490 handshake_deadline: Instant::now() + Duration::from_secs(2),
491 session: Arc::clone(self),
492 }),
493 )
494 }
495
496 fn connection_lost(self: &Arc<Self>, error: &io::Error) {
498 log::warn!(
499 "[{}] Weave connection failed: {}; retrying in five seconds",
500 self.config.name,
501 error
502 );
503 self.disconnect_all();
504 let session = Arc::clone(self);
505 let spawned = thread::Builder::new()
506 .name(format!("weave-connect-{}", self.config.interface_id.0))
507 .spawn(move || session.connect_folded(true));
508 if let Err(e) = spawned {
509 log::error!(
510 "[{}] cannot start reconnecting, giving up: {}",
511 self.config.name,
512 e
513 );
514 }
515 }
516
517 fn send_frame(&self, frame: &WdclFrame) -> io::Result<()> {
518 let encoded = crate::hdlc::frame(&frame.encode());
519 let mut writer = super::lock_or_recover(&self.writer, "weave writer");
520 writer
521 .as_mut()
522 .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "Weave is offline"))?
523 .write_all(&encoded)
524 }
525
526 fn process_frame(self: &Arc<Self>, raw: &[u8]) -> io::Result<()> {
527 let Some(input) = parse_device_frame(raw, self.host_switch_id) else {
528 return Ok(());
529 };
530 match input {
531 WeaveInput::Discovery {
532 switch_id,
533 signing_key: _,
534 } => {
535 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") =
536 Some(switch_id);
537 super::lock_or_recover(&self.config.state, "weave state").switch_id =
538 Some(switch_id);
539 let mut payload = self.host_public.to_vec();
540 payload.extend_from_slice(&self.host_identity.sign(&switch_id));
541 self.send_frame(&WdclFrame {
542 destination: switch_id,
543 frame_type: WDCL_T_CONNECT,
544 payload,
545 })?;
546 }
547 WeaveInput::EndpointPacket { source, packet } => {
548 let now = crate::time::now();
549 {
550 let mut state = super::lock_or_recover(&self.config.state, "weave state");
551 state
552 .peers
553 .entry(source)
554 .and_modify(|peer| peer.last_heard = now)
555 .or_insert(WeavePeerState {
556 endpoint_id: source,
557 via_switch_id: None,
558 last_heard: now,
559 });
560 if !state.accept_packet(&packet, now) {
561 return Ok(());
562 }
563 }
564 let interface_id = self.ensure_peer(source, now);
565 self.emit(Event::Frame {
566 interface_id,
567 data: packet.into(),
568 rssi: None,
569 snr: None,
570 });
571 }
572 WeaveInput::Log(frame) => self.process_log(frame),
573 WeaveInput::Display {
574 offset,
575 total,
576 fragment,
577 } => {
578 super::lock_or_recover(&self.config.state, "weave state")
579 .apply_display(offset, total, &fragment);
580 }
581 WeaveInput::Unknown(_) => {}
582 }
583 Ok(())
584 }
585
586 fn process_log(self: &Arc<Self>, frame: WeaveLogFrame) {
587 let now = crate::time::now();
588 let event = frame.event;
589 let data = frame.data.clone();
590 super::lock_or_recover(&self.config.state, "weave state").handle_log(frame, now);
591
592 match event {
593 ET_PROTO_WDCL_CONNECTION => {
594 self.emit(Event::InterfaceUp(self.config.interface_id, None, None));
595 self.publish_parent_telemetry();
596 }
597 ET_PROTO_WEAVE_EP_ALIVE if data.len() == 8 => {
598 let endpoint: [u8; 8] = data.as_slice().try_into().unwrap();
599 self.ensure_peer(endpoint, now);
600 }
601 ET_PROTO_WEAVE_EP_TIMEOUT if data.len() == 8 => {
602 let endpoint: [u8; 8] = data.as_slice().try_into().unwrap();
603 self.remove_peer(endpoint);
604 }
605 ET_PROTO_WEAVE_EP_VIA if data.len() == 12 => {
606 let endpoint: [u8; 8] = data[..8].try_into().unwrap();
607 let via: [u8; 4] = data[8..].try_into().unwrap();
608 if let Some(interface_id) =
609 super::lock_or_recover(&self.peer_interfaces, "weave peer interfaces")
610 .get(&endpoint)
611 .copied()
612 {
613 self.emit(Event::InterfaceTelemetry {
614 interface_id,
615 telemetry: Box::new(InterfaceTelemetry {
616 via_switch_id: Some(via),
617 ..Default::default()
618 }),
619 });
620 }
621 }
622 ET_PROTO_WDCL_HOST_ENDPOINT | ET_STAT_CPU | ET_STAT_MEMORY => {
623 self.publish_parent_telemetry()
624 }
625 _ => {}
626 }
627 }
628
629 fn ensure_peer(self: &Arc<Self>, endpoint: [u8; 8], now: f64) -> InterfaceId {
630 if let Some(id) = super::lock_or_recover(&self.peer_interfaces, "weave peers")
631 .get(&endpoint)
632 .copied()
633 {
634 return id;
635 }
636 let id = InterfaceId(self.next_dynamic_id.fetch_add(1, Ordering::Relaxed));
637 super::lock_or_recover(&self.peer_interfaces, "weave peers").insert(endpoint, id);
638 let via_switch_id = super::lock_or_recover(&self.config.state, "weave state")
639 .peers
640 .get(&endpoint)
641 .and_then(|peer| peer.via_switch_id);
642 let name = format!("WeaveInterfacePeer[{}]", hex(&endpoint));
643 let info = InterfaceInfo {
644 stamp_costs: Default::default(),
645 id,
646 name,
647 mode: self.mode,
648 gravity: self.gravity,
649 recursive_prs: self.recursive_prs,
650 announces_from_internal: self.announces_from_internal,
651 announces_to_internal: self.announces_to_internal,
652 out_capable: true,
653 in_capable: true,
654 bitrate: Some(self.config.configured_bitrate),
655 airtime_profile: None,
656 announce_rate_target: None,
657 announce_rate_grace: 0,
658 announce_rate_penalty: 0.0,
659 announce_cap: rns_core::constants::ANNOUNCE_CAP,
660 is_local_client: false,
661 wants_tunnel: false,
662 tunnel_id: None,
663 mtu: WEAVE_HW_MTU,
664 ia_freq: 0.0,
665 ip_freq: 0.0,
666 op_freq: 0.0,
667 op_samples: 0,
668 started: now,
669 ingress_control: self.ingress_control,
670 };
671 self.emit(Event::DynamicInterfaceUp {
672 id,
673 writer: Box::new(WeavePeerWriter {
674 session: Arc::clone(self),
675 endpoint,
676 }),
677 registration: Box::new(DynamicInterfaceRegistration {
678 info,
679 interface_type: "WeaveInterfacePeer".into(),
680 parent_id: self.config.interface_id,
681 telemetry: InterfaceTelemetry {
682 via_switch_id,
683 ..Default::default()
684 },
685 ifac: self.ifac.clone(),
686 }),
687 });
688 self.publish_parent_telemetry();
689 id
690 }
691
692 fn remove_peer(&self, endpoint: [u8; 8]) {
693 if let Some(id) =
694 super::lock_or_recover(&self.peer_interfaces, "weave peers").remove(&endpoint)
695 {
696 super::lock_or_recover(&self.config.state, "weave state")
697 .peers
698 .remove(&endpoint);
699 self.emit(Event::InterfaceDown(id));
700 self.publish_parent_telemetry();
701 }
702 }
703
704 fn expire_peers(&self, now: f64) {
705 let expired = super::lock_or_recover(&self.config.state, "weave state").expire_peers(now);
706 for endpoint in &expired {
707 if let Some(id) =
708 super::lock_or_recover(&self.peer_interfaces, "weave peers").remove(endpoint)
709 {
710 self.emit(Event::InterfaceDown(id));
711 }
712 }
713 if !super::lock_or_recover(&self.peer_interfaces, "weave peers").is_empty()
714 || !expired.is_empty()
715 {
716 self.publish_parent_telemetry();
717 }
718 }
719
720 fn publish_parent_telemetry(&self) {
721 let state = super::lock_or_recover(&self.config.state, "weave state");
722 self.emit(Event::InterfaceTelemetry {
723 interface_id: self.config.interface_id,
724 telemetry: Box::new(InterfaceTelemetry {
725 cpu_load: state.cpu_load.map(f64::from),
726 mem_load: state.mem_load,
727 switch_id: state.switch_id,
728 endpoint_id: state.endpoint_id,
729 via_switch_id: None,
730 peers: Some(state.peers.len()),
731 ..Default::default()
732 }),
733 });
734 }
735
736 fn disconnect_all(&self) {
737 *super::lock_or_recover(&self.writer, "weave writer") = None;
738 *super::lock_or_recover(&self.remote_switch_id, "weave remote switch") = None;
739 {
740 let mut state = super::lock_or_recover(&self.config.state, "weave state");
741 state.connected = false;
742 state.switch_id = None;
743 state.peers.clear();
744 }
745 let peers: Vec<_> = super::lock_or_recover(&self.peer_interfaces, "weave peers")
746 .drain()
747 .map(|(_, id)| id)
748 .collect();
749 for id in peers {
750 self.emit(Event::InterfaceDown(id));
751 }
752 self.emit(Event::InterfaceDown(self.config.interface_id));
753 self.publish_parent_telemetry();
754 }
755}
756
757struct WeavePeerWriter {
758 session: Arc<WeaveSession>,
759 endpoint: [u8; 8],
760}
761
762impl Writer for WeavePeerWriter {
763 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
764 let remote = super::lock_or_recover(&self.session.remote_switch_id, "weave remote switch")
765 .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "Weave is offline"))?;
766 self.session
767 .send_frame(&WdclFrame::endpoint_packet(remote, self.endpoint, data))
768 }
769
770 fn enqueue_confirmed(
771 &mut self,
772 data: &[u8],
773 completion: &mut Option<crate::link_send::Completion>,
774 ) -> io::Result<()> {
775 self.send_frame(data)?;
778 if let Some(completion) = completion.take() {
779 completion.finish(Ok(()));
780 }
781 Ok(())
782 }
783
784 fn sends_inline(&self) -> bool {
785 crate::engine::FOLD_IO
787 }
788
789 fn flush_pending(&mut self) {
790 if let Some(writer) = super::lock_or_recover(&self.session.writer, "weave writer").as_mut()
791 {
792 writer.drain();
793 }
794 }
795}
796
797struct WeaveSource {
800 reader: std::fs::File,
801 decoder: crate::hdlc::Decoder,
802 handshake_deadline: Instant,
803 session: Arc<WeaveSession>,
804}
805
806impl WeaveSource {
807 fn connected(&self) -> bool {
808 super::lock_or_recover(&self.session.config.state, "weave state").connected
809 }
810
811 fn check_handshake(&self) -> io::Result<()> {
813 if !self.connected() && Instant::now() >= self.handshake_deadline {
814 return Err(io::Error::new(
815 io::ErrorKind::TimedOut,
816 "WDCL connection handshake timed out",
817 ));
818 }
819 Ok(())
820 }
821
822 fn read(&mut self, cx: &mut LoopContext<'_>) -> io::Result<SourceStatus> {
823 let mut read = 0;
824 while read < super::stream_read::STREAM_READ_BUDGET {
825 if !self.session.tx.inbound_data_has_room() {
826 return Ok(SourceStatus::Suspend);
827 }
828 let n = unsafe {
831 libc::read(
832 self.reader.as_raw_fd(),
833 cx.buf.as_mut_ptr().cast(),
834 cx.buf.len(),
835 )
836 };
837 if n < 0 {
838 let error = io::Error::last_os_error();
839 match error.kind() {
840 io::ErrorKind::WouldBlock => break,
841 io::ErrorKind::Interrupted => continue,
842 _ => return Err(error),
843 }
844 }
845 if n == 0 {
846 return Err(io::Error::new(
847 io::ErrorKind::UnexpectedEof,
848 "serial port closed",
849 ));
850 }
851 let n = n as usize;
852 read += n;
853 for frame in self.decoder.feed(&cx.buf[..n]) {
854 self.session.process_frame(&frame)?;
855 }
856 if n < cx.buf.len() {
857 break;
858 }
859 }
860 self.check_handshake()?;
861 Ok(SourceStatus::Rearm)
862 }
863}
864
865impl LoopSource for WeaveSource {
866 fn fd(&self) -> std::os::fd::RawFd {
867 self.reader.as_raw_fd()
868 }
869
870 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
871 match self.read(cx) {
872 Ok(status) => status,
873 Err(error) => {
874 self.session.connection_lost(&error);
875 SourceStatus::Remove
876 }
877 }
878 }
879
880 fn deadline(&self) -> Option<Instant> {
881 if !self.connected() {
884 return Some(self.handshake_deadline);
885 }
886 let state = super::lock_or_recover(&self.session.config.state, "weave state");
887 let oldest = state
888 .peers
889 .values()
890 .map(|peer| peer.last_heard)
891 .min_by(f64::total_cmp)?;
892 let wait = (oldest + WEAVE_PEERING_TIMEOUT - crate::time::now()).max(0.0);
893 Some(Instant::now() + Duration::from_secs_f64(wait))
894 }
895
896 fn on_timer(&mut self, _cx: &mut LoopContext<'_>) -> SourceStatus {
897 if let Err(error) = self.check_handshake() {
898 self.session.connection_lost(&error);
899 return SourceStatus::Remove;
900 }
901 self.session.expire_peers(crate::time::now());
902 SourceStatus::Rearm
903 }
904}
905
906fn hex(bytes: &[u8]) -> String {
907 bytes.iter().map(|byte| format!("{byte:02x}")).collect()
908}
909
910pub struct WeaveFactory;
911struct ParentWriter {
915 session: Arc<WeaveSession>,
916}
917impl Writer for ParentWriter {
918 fn send_frame_confirmed(&mut self, _data: &[u8]) -> io::Result<()> {
919 Err(io::Error::new(
920 io::ErrorKind::Unsupported,
921 "Weave parent is not a transmitting peer",
922 ))
923 }
924 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
925 Ok(())
926 }
927
928 fn flush_pending(&mut self) {
929 if let Some(writer) = super::lock_or_recover(&self.session.writer, "weave writer").as_mut()
930 {
931 writer.drain();
932 }
933 }
934
935 fn sends_inline(&self) -> bool {
936 crate::engine::FOLD_IO
939 }
940}
941
942impl InterfaceFactory for WeaveFactory {
943 fn type_name(&self) -> &str {
944 "WeaveInterface"
945 }
946 fn default_ifac_size(&self) -> usize {
947 16
948 }
949
950 fn parse_config(
951 &self,
952 name: &str,
953 id: InterfaceId,
954 params: &HashMap<String, String>,
955 ) -> Result<Box<dyn InterfaceConfigData>, String> {
956 let port = params
957 .get("port")
958 .cloned()
959 .ok_or_else(|| "WeaveInterface requires 'port'".to_string())?;
960 let configured_bitrate = params
961 .get("configured_bitrate")
962 .and_then(|v| v.parse().ok())
963 .unwrap_or(WEAVE_DEFAULT_BITRATE);
964 Ok(Box::new(WeaveConfig {
965 name: name.into(),
966 port,
967 configured_bitrate,
968 interface_id: id,
969 state: Arc::new(Mutex::new(WeaveState::default())),
970 }))
971 }
972
973 fn start(
974 &self,
975 config: Box<dyn InterfaceConfigData>,
976 ctx: StartContext,
977 ) -> io::Result<StartResult> {
978 let config = *config
979 .into_any()
980 .downcast::<WeaveConfig>()
981 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong Weave config type"))?;
982 let session = WeaveSession::new(config.clone(), &ctx);
983 session.spawn()?;
984 let info = InterfaceInfo {
985 stamp_costs: Default::default(),
986 id: config.interface_id,
987 name: config.name.clone(),
988 mode: ctx.mode,
989 gravity: ctx.gravity,
990 recursive_prs: ctx.recursive_prs,
991 announces_from_internal: ctx.announces_from_internal,
992 announces_to_internal: ctx.announces_to_internal,
993 out_capable: false,
994 in_capable: true,
995 bitrate: Some(config.configured_bitrate),
996 airtime_profile: None,
997 announce_rate_target: None,
998 announce_rate_grace: 0,
999 announce_rate_penalty: 0.0,
1000 announce_cap: rns_core::constants::ANNOUNCE_CAP,
1001 is_local_client: false,
1002 wants_tunnel: false,
1003 tunnel_id: None,
1004 mtu: WEAVE_HW_MTU,
1005 ia_freq: 0.0,
1006 ip_freq: 0.0,
1007 op_freq: 0.0,
1008 op_samples: 0,
1009 started: crate::time::now(),
1010 ingress_control: ctx.ingress_control,
1011 };
1012 Ok(StartResult::Simple {
1013 control: None,
1014 id: config.interface_id,
1015 info,
1016 writer: Box::new(ParentWriter { session }),
1017 interface_type_name: "WeaveInterface".into(),
1018 })
1019 }
1020}
1021
1022#[cfg(test)]
1023mod tests {
1024 use super::*;
1025 use rns_crypto::ed25519::Ed25519PrivateKey;
1026 use rns_crypto::FixedRng;
1027
1028 fn log_frame(host: [u8; 4], event: u16, data: &[u8]) -> Vec<u8> {
1029 let mut payload = vec![0, 0, 0, 0, 1, 2];
1030 payload.extend_from_slice(&event.to_be_bytes());
1031 payload.extend_from_slice(data);
1032 WdclFrame {
1033 destination: host,
1034 frame_type: WDCL_T_LOG,
1035 payload,
1036 }
1037 .encode()
1038 }
1039
1040 #[test]
1041 fn wdcl_fragmented_hdlc_and_discovery_signature() {
1042 let private = Ed25519PrivateKey::generate(&mut FixedRng::new(&[7; 64]));
1043 let key = private.public_key().public_bytes();
1044 let signed_id = [1, 2, 3, 4];
1045 let mut payload = key.to_vec();
1046 payload.extend_from_slice(&private.sign(&signed_id));
1047 let frame = WdclFrame {
1048 destination: signed_id,
1049 frame_type: WDCL_T_DISCOVER,
1050 payload,
1051 }
1052 .encode();
1053 assert!(matches!(
1054 parse_device_frame(&frame, [9; 4]),
1055 Some(WeaveInput::Discovery { .. })
1056 ));
1057
1058 let wire = crate::hdlc::frame(&frame);
1059 let mut decoder = crate::hdlc::Decoder::with_min_frame_size(5);
1060 assert!(decoder.feed(&wire[..3]).is_empty());
1061 assert_eq!(decoder.feed(&wire[3..]), vec![frame]);
1062 }
1063
1064 #[test]
1065 fn board_init_unknown_and_duplicate_state_are_preserved() {
1066 let mut state = WeaveState::default();
1067 state.handle_log(
1068 WeaveLogFrame {
1069 timestamp: 1.0,
1070 level: 1,
1071 event: ET_BOARD_INIT,
1072 data: vec![1],
1073 },
1074 1.0,
1075 );
1076 state.handle_log(
1077 WeaveLogFrame {
1078 timestamp: 2.0,
1079 level: 1,
1080 event: 0x7777,
1081 data: vec![2],
1082 },
1083 2.0,
1084 );
1085 assert_eq!(state.logs.len(), 2);
1086 assert_eq!(state.unknown_events.len(), 1);
1087 assert!(state.accept_packet(b"packet", 3.0));
1088 assert!(!state.accept_packet(b"packet", 3.1));
1089 assert!(state.accept_packet(b"packet", 4.0));
1090 }
1091
1092 #[test]
1093 fn simulated_session_authenticates_and_manages_endpoint_lifecycle() {
1094 let (tx, rx) = crate::event::channel();
1095 let config = WeaveConfig {
1096 name: "weave-test".into(),
1097 port: "/dev/null".into(),
1098 configured_bitrate: WEAVE_DEFAULT_BITRATE,
1099 interface_id: InterfaceId(55),
1100 state: Arc::new(Mutex::new(WeaveState::default())),
1101 };
1102 let context = StartContext {
1103 tx,
1104 next_dynamic_id: Arc::new(AtomicU64::new(10_000)),
1105 mode: rns_core::constants::MODE_GATEWAY,
1106 gravity: 0,
1107 recursive_prs: true,
1108 announces_from_internal: false,
1109 announces_to_internal: None,
1110 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
1111 stamp_costs: Default::default(),
1112 ifac: None,
1113 underlay_mark: None,
1114 };
1115 let session = WeaveSession::new(config, &context);
1116 *super::super::lock_or_recover(&session.writer, "test writer") =
1117 Some(DeviceWriter::new(tempfile::tempfile().unwrap()));
1118
1119 let remote = Ed25519PrivateKey::generate(&mut FixedRng::new(&[0x44; 64]));
1120 let remote_public = remote.public_key().public_bytes();
1121 let mut discovery_payload = remote_public.to_vec();
1122 discovery_payload.extend_from_slice(&remote.sign(&session.host_switch_id));
1123 session
1124 .process_frame(
1125 &WdclFrame {
1126 destination: session.host_switch_id,
1127 frame_type: WDCL_T_DISCOVER,
1128 payload: discovery_payload,
1129 }
1130 .encode(),
1131 )
1132 .unwrap();
1133 assert_eq!(
1134 *super::super::lock_or_recover(&session.remote_switch_id, "test remote"),
1135 Some(remote_public[28..].try_into().unwrap())
1136 );
1137
1138 session
1139 .process_frame(&log_frame(
1140 session.host_switch_id,
1141 ET_PROTO_WDCL_CONNECTION,
1142 &[1],
1143 ))
1144 .unwrap();
1145 assert!(matches!(
1146 rx.recv_timeout(Duration::from_secs(1)).unwrap(),
1147 Event::InterfaceUp(InterfaceId(55), None, None)
1148 ));
1149
1150 let endpoint = [0x77; 8];
1151 session
1152 .process_frame(&log_frame(
1153 session.host_switch_id,
1154 ET_PROTO_WEAVE_EP_ALIVE,
1155 &endpoint,
1156 ))
1157 .unwrap();
1158 let peer_id = loop {
1159 if let Event::DynamicInterfaceUp {
1160 id, registration, ..
1161 } = rx.recv_timeout(Duration::from_secs(1)).unwrap()
1162 {
1163 assert_eq!(registration.interface_type, "WeaveInterfacePeer");
1164 assert_eq!(registration.parent_id, InterfaceId(55));
1165 break id;
1166 }
1167 };
1168
1169 let mut endpoint_payload = b"rns-packet".to_vec();
1170 endpoint_payload.extend_from_slice(&endpoint);
1171 let endpoint_frame = WdclFrame {
1172 destination: session.host_switch_id,
1173 frame_type: WDCL_T_ENDPOINT_PKT,
1174 payload: endpoint_payload,
1175 }
1176 .encode();
1177 session.process_frame(&endpoint_frame).unwrap();
1178 assert!(loop {
1179 if let Event::Frame {
1180 interface_id, data, ..
1181 } = rx.recv_timeout(Duration::from_secs(1)).unwrap()
1182 {
1183 break interface_id == peer_id && *data == b"rns-packet"[..];
1184 }
1185 });
1186 session.process_frame(&endpoint_frame).unwrap();
1187 assert!(rx.recv_timeout(Duration::from_millis(50)).is_err());
1188
1189 session.expire_peers(crate::time::now() + WEAVE_PEERING_TIMEOUT + 1.0);
1190 assert!(loop {
1191 if let Event::InterfaceDown(id) = rx.recv_timeout(Duration::from_secs(1)).unwrap() {
1192 break id == peer_id;
1193 }
1194 });
1195 }
1196
1197 #[test]
1198 fn folded_weave_device_runs_on_the_driver_loop() {
1199 use std::os::fd::FromRawFd;
1200 if !crate::engine::FOLD_IO {
1201 return;
1202 }
1203 let tempdir = tempfile::tempdir().unwrap();
1204 let port_path = tempdir.path().join("weave-port");
1205 let (master_fd, slave_fd) = crate::serial::open_pty_pair().unwrap();
1206 std::os::unix::fs::symlink(
1207 std::fs::read_link(format!("/proc/self/fd/{slave_fd}")).unwrap(),
1208 &port_path,
1209 )
1210 .unwrap();
1211 let mut device = unsafe { std::fs::File::from_raw_fd(master_fd) };
1212 let _slave = unsafe { std::fs::File::from_raw_fd(slave_fd) };
1213
1214 let (tx, rx) = crate::event::channel();
1215 let config = WeaveConfig {
1216 name: "weave-folded".into(),
1217 port: port_path.display().to_string(),
1218 configured_bitrate: WEAVE_DEFAULT_BITRATE,
1219 interface_id: InterfaceId(56),
1220 state: Arc::new(Mutex::new(WeaveState::default())),
1221 };
1222 let context = StartContext {
1223 tx: tx.clone(),
1224 next_dynamic_id: Arc::new(AtomicU64::new(11_000)),
1225 mode: rns_core::constants::MODE_GATEWAY,
1226 gravity: 0,
1227 recursive_prs: true,
1228 announces_from_internal: false,
1229 announces_to_internal: None,
1230 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
1231 stamp_costs: Default::default(),
1232 ifac: None,
1233 underlay_mark: None,
1234 };
1235 let session = WeaveSession::new(config, &context);
1236 session.open_folded().unwrap();
1237 let host = session.host_switch_id;
1238
1239 let mut decoder = crate::hdlc::Decoder::with_limits(5, 128 * 1024);
1241 let mut device_reads = |device: &mut std::fs::File| -> Vec<Vec<u8>> {
1242 let mut buf = [0u8; 4096];
1243 let mut pfd = libc::pollfd {
1244 fd: device.as_raw_fd(),
1245 events: libc::POLLIN,
1246 revents: 0,
1247 };
1248 if unsafe { libc::poll(&mut pfd, 1, 2000) } <= 0 {
1249 return Vec::new();
1250 }
1251 let n = device.read(&mut buf).unwrap();
1252 decoder.feed(&buf[..n])
1253 };
1254 let discover = WdclFrame::decode(&device_reads(&mut device)[0]).unwrap();
1255 assert_eq!(discover.frame_type, WDCL_T_DISCOVER);
1256 let remote = Ed25519PrivateKey::generate(&mut FixedRng::new(&[0x45; 64]));
1257 let remote_public = remote.public_key().public_bytes();
1258 let mut payload = remote_public.to_vec();
1259 payload.extend_from_slice(&remote.sign(&host));
1260 let mut wire = crate::hdlc::frame(
1261 &WdclFrame {
1262 destination: host,
1263 frame_type: WDCL_T_DISCOVER,
1264 payload,
1265 }
1266 .encode(),
1267 );
1268 wire.extend_from_slice(&crate::hdlc::frame(&log_frame(
1269 host,
1270 ET_PROTO_WDCL_CONNECTION,
1271 &[1],
1272 )));
1273 let endpoint = [0x78; 8];
1274 wire.extend_from_slice(&crate::hdlc::frame(&log_frame(
1275 host,
1276 ET_PROTO_WEAVE_EP_ALIVE,
1277 &endpoint,
1278 )));
1279 device.write_all(&wire).unwrap();
1280
1281 let mut peer = None;
1283 let deadline = Instant::now() + Duration::from_secs(3);
1284 let mut parent_up = false;
1285 while peer.is_none() && Instant::now() < deadline {
1286 match rx.recv_timeout(Duration::from_millis(200)) {
1287 Ok(Event::InterfaceUp(InterfaceId(56), None, None)) => parent_up = true,
1288 Ok(Event::DynamicInterfaceUp { id, writer, .. }) => peer = Some((id, writer)),
1289 _ => {}
1290 }
1291 }
1292 assert!(parent_up);
1293 let (peer_id, writer) = peer.expect("peer came up");
1294 assert!(writer.sends_inline());
1295 let (mut writer, _) = crate::interface::wrap_async_writer(writer, peer_id, "weave", tx, 16);
1296
1297 let connect = device_reads(&mut device)
1299 .into_iter()
1300 .map(|raw| WdclFrame::decode(&raw).unwrap())
1301 .find(|frame| frame.frame_type == WDCL_T_CONNECT)
1302 .expect("connect request");
1303 assert_eq!(connect.destination, remote_public[28..]);
1304
1305 writer.send_frame(b"to the peer").unwrap();
1307 writer.flush_pending();
1308 let sent = WdclFrame::decode(&device_reads(&mut device)[0]).unwrap();
1309 assert_eq!(
1310 sent,
1311 WdclFrame::endpoint_packet(
1312 remote_public[28..].try_into().unwrap(),
1313 endpoint,
1314 b"to the peer"
1315 )
1316 );
1317 let mut endpoint_payload = b"from the peer".to_vec();
1318 endpoint_payload.extend_from_slice(&endpoint);
1319 device
1320 .write_all(&crate::hdlc::frame(
1321 &WdclFrame {
1322 destination: host,
1323 frame_type: WDCL_T_ENDPOINT_PKT,
1324 payload: endpoint_payload,
1325 }
1326 .encode(),
1327 ))
1328 .unwrap();
1329 let received = loop {
1330 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
1331 Event::Frame {
1332 interface_id, data, ..
1333 } => break (interface_id, data),
1334 _ => continue,
1335 }
1336 };
1337 assert_eq!(received.0, peer_id);
1338 assert_eq!(&*received.1, b"from the peer");
1339
1340 drop(device);
1342 let mut down = Vec::new();
1343 while let Ok(event) = rx.recv_timeout(Duration::from_millis(500)) {
1344 if let Event::InterfaceDown(id) = event {
1345 down.push(id);
1346 }
1347 }
1348 assert!(down.contains(&peer_id));
1349 assert!(down.contains(&InterfaceId(56)));
1350 }
1351}