https://github.com/encodeous/root
An abstract, I/O-free routing framework inspired by RFC 8966
https://github.com/encodeous/root
Last synced: 6 months ago
JSON representation
An abstract, I/O-free routing framework inspired by RFC 8966
- Host: GitHub
- URL: https://github.com/encodeous/root
- Owner: encodeous
- License: mit
- Created: 2024-06-09T21:26:59.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-11-08T00:04:30.000Z (about 1 year ago)
- Last Synced: 2024-12-08T06:18:55.656Z (about 1 year ago)
- Language: Rust
- Homepage: https://crates.io/crates/root
- Size: 999 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# root
[root](https://github.com/encodeous/root) is an abstract I/O-free routing framework inspired by the [Babel Routing Protocol](https://datatracker.ietf.org/doc/html/rfc8966). It provides a high-level framework to design dynamic, fault-tolerant networks.
The application is solely responsible for providing I/O and scheduling events, meaning that users of root are free to use any platform, framework, or architecture they desire.
For a complete example routing application that uses TCP as transport, see `/examples/simple-mesh`.
To get started with root, run:
`cargo add root`, use the `serde` feature for serialization.
# Why I/O-free?
root is designed from the ground up to offer a platform, network, and protocol agnostic way to do routing.
- Compared to traditional implementations that rely on a **specific network stack**, root is able to work for **any situation** where a graph of nodes are joined together with bidirectional links.
- Decisions about how to physically forward packets are left to the **application**, allowing hybrid routing over **multiple protocols** (i.e routing over IPv4, IPv6, Serial & Bluetooth all at once)
- An I/O-free implementation allows root to be thoroughly tested, and operate with deterministic state at all times.
For more motivations, you can read [this set of articles](https://sans-io.readthedocs.io/index.html#).
# Concepts
root tries its best to abstract the complexity of networking, while maintaining compatibility with low level concepts.
## Templating
When building a routing network using the root framework, the architect can specify a set of pre-defined parameters that defines it.
## The `NodeAddress` type
The NodeAddress is a globally (on each network) unique identifier that is attached to each node.
## The `Link` type
The link type represents a physical bidirectional connection between two nodes. This is not sent to other nodes, and should be unique on each node.
# Example Usage
> [!CAUTION]
> These examples do not implement MAC, meaning that routes/packets can be forged. The root crate implicitly trusts the authenticity of such packets.
## Basic Example
To demonstrate the use of the root crate, here is a super simple example where we have 3 nodes, `bob`, `eve`, and `alice`.
We have: `bob <-> eve <-> alice`, but not `bob <-> alice`.
We want the routing system to figure out how to reach `alice` from `bob`
We can start off by defining the routing parameters. This is a compile-time constant shared across all nodes.
```rust
use root::framework::RoutingSystem;
use root::router::NoMACSystem;
struct SimpleExample {} // just a type to inform root of your network parameters
impl RoutingSystem for SimpleExample{
type NodeAddress = String; // our nodes have string names
type Link = i32;
type MACSystem = NoMACSystem; // we won't use MAC for this example
}
```
Now, for each node, we can create a router:
```rust
// we have the following connection: bob <-> eve <-> alice
let mut nodes = HashMap::new();
let mut bob = Router::::new("bob".to_string());
bob.links.insert(1, Neighbour::new("eve".to_string()));
nodes.insert("bob", bob);
let mut eve = Router::::new("eve".to_string());
eve.links.insert(1, Neighbour::new("bob".to_string()));
eve.links.insert(2, Neighbour::new("alice".to_string()));
nodes.insert("eve", eve);
let mut alice = Router::::new("alice".to_string());
alice.links.insert(2, Neighbour::new("eve".to_string()));
nodes.insert("alice", alice);
```
Now we can let root take over, and have it automatically discover the route.
We simply let root generate routing packets, and simulate sending them to the other nodes. In a real network, these packets need to be serialized and sent over the network.
```rust
// lets simulate routing!
for step in 0..3 {
// collect all of our packets, if any
let packets: Vec> = nodes.iter_mut().flat_map(|(_id, node)| node.outbound_packets.drain(..)).collect();
for OutboundPacket{link, dest, packet} in packets{
// deliver the routing packet. in this simple example, the link isn't really used. in a real network, this link will give us information on how to send the packet
if let Some(node) = nodes.get_mut(dest.as_str()){
node.handle_packet(&packet, &link, &dest).expect("Failed to handle packet");
}
}
for node in nodes.values_mut(){
node.full_update(); // performs route table calculations, and writes routing updates into outbound_packets
}
// lets observe bob's route table:
println!("Bob's routes in step {step}:");
for (neigh, Route::{ metric, next_hop, .. }) in &nodes["bob"].routes{
println!(" - {neigh}: metric: {metric}, next_hop: {next_hop}")
}
}
```
Here is the output for this example:
```
Bob's routes in step 0:
Bob's routes in step 1:
- eve: metric: 1, next_hop: eve
Bob's routes in step 2:
- eve: metric: 1, next_hop: eve
- alice: metric: 2, next_hop: eve
```
> [!NOTE]
> You can try running this example yourself, its files are located in `./examples/super-simple`
## Network Example
> [!NOTE]
> To demonstrate the root crate working over real network connections, a complete example is provided in `./examples/simple-mesh`.
This example uses TCP streams as the transport, and is based on a event/channel pattern.