Skip to main content

pamoja_routing/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! Cost-aware mesh routing for the pamoja SDK.
4//!
5//! Flooding gets a packet across a mesh by having every node rebroadcast it, which always
6//! works but is expensive: every node spends airtime and power on every packet. Once a
7//! mesh has settled, most traffic goes to a few known places, and a node that remembers
8//! the way can forward a packet to just the right neighbour instead of shouting it to the
9//! whole network. That is routing, and on the cheap radios this SDK targets the saving in
10//! airtime and battery is the difference between a network that lasts and one that does
11//! not.
12//!
13//! This crate is the decision layer for that, as pure logic with no radio and no
14//! allocation:
15//!
16//! - [`Router`] - a fixed-size table that learns the way to a node from the traffic it
17//!   already hears: when a packet from a distant node arrives via a neighbour, that
18//!   neighbour is the way back, at the cost the packet reports. The table keeps the
19//!   cheapest way it knows to each destination and forgets the most expensive when it runs
20//!   out of room.
21//! - [`Router::forward`] - the per-packet decision: deliver a packet that is for this
22//!   node, [relay](Forward::Relay) one toward a known destination, or [flood](Forward::Flood)
23//!   when there is no route yet. That last case is where this layer hands back to the
24//!   flooding in `pamoja-mesh`, so routing is an optimisation over flooding, never a
25//!   single point of failure.
26//!
27//! Nodes are identified by the same address a [`pamoja-mesh`](https://docs.rs/pamoja-mesh)
28//! frame carries, so the two compose directly: learn from a received frame's source and
29//! the neighbour it came from, then ask [`forward`](Router::forward) where the next one
30//! should go.
31//!
32//! # Examples
33//!
34//! ```
35//! use pamoja_routing::{Forward, Router};
36//!
37//! let mut router: Router<16> = Router::new(0x01);
38//!
39//! // We hear node 0x09's traffic arrive via neighbour 0x05, two hops out.
40//! router.observe(0x09, 0x05, 2);
41//! assert_eq!(router.forward(0x09), Forward::Relay(0x05));
42//!
43//! // A cheaper way to 0x09 turns up via neighbour 0x07; the router prefers it.
44//! router.observe(0x09, 0x07, 1);
45//! assert_eq!(router.forward(0x09), Forward::Relay(0x07));
46//!
47//! // With no route to 0x20 yet, the router falls back to flooding.
48//! assert_eq!(router.forward(0x20), Forward::Flood);
49//! ```
50
51// `cfg(test)` already builds this crate against std, so the runtime-sized table compiles
52// for the test run whether or not the feature is on, and its tests always execute.
53#[cfg(any(feature = "alloc", test))]
54extern crate alloc;
55
56mod router;
57
58#[cfg(any(feature = "alloc", test))]
59pub use router::DynamicRouter;
60pub use router::{Forward, Route, Router};