|
1 | 1 | # root |
2 | | -An I/O Free High-Level Routing Protocol inspired by Babel |
| 2 | + |
| 3 | +[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. |
| 4 | + |
| 5 | +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. |
| 6 | + |
| 7 | +For a complete example routing application that uses TCP as transport, see `/examples/simple-mesh`. |
| 8 | + |
| 9 | +To get started with root, run: |
| 10 | +`cargo add root`, use the `serde` feature for serialization. |
| 11 | + |
| 12 | +# Why I/O-free? |
| 13 | + |
| 14 | +root is designed from the ground up to offer a platform, network, and protocol agnostic way to do routing. |
| 15 | +- 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. |
| 16 | +- 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) |
| 17 | +- An I/O-free implementation allows root to be thoroughly tested, and operate with deterministic state at all times. |
| 18 | + |
| 19 | +For more motivations, you can read [this set of articles](https://sans-io.readthedocs.io/index.html#). |
| 20 | + |
| 21 | + |
| 22 | +# Concepts |
| 23 | + |
| 24 | +root tries its best to abstract the complexity of networking, while maintaining compatibility with low level concepts. |
| 25 | + |
| 26 | +## Templating |
| 27 | + |
| 28 | +When building a routing network using the root framework, the architect can specify a set of pre-defined parameters that defines it. |
| 29 | + |
| 30 | +## The `NodeAddress` type |
| 31 | + |
| 32 | +The NodeAddress is a globally (on each network) unique identifier that is attached to each node. |
| 33 | + |
| 34 | +## The `Link` type |
| 35 | + |
| 36 | +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. |
| 37 | + |
| 38 | +# Example Usage |
| 39 | + |
| 40 | +> [!CAUTION] |
| 41 | +> These examples do not implement MAC, meaning that routes/packets can be forged. The root crate implicitly trusts the authenticity of such packets. |
| 42 | +
|
| 43 | +## Basic Example |
| 44 | + |
| 45 | +To demonstrate the use of the root crate, here is a super simple example where we have 3 nodes, `bob`, `eve`, and `alice`. |
| 46 | + |
| 47 | +We have: `bob <-> eve <-> alice`, but not `bob <-> alice`. |
| 48 | + |
| 49 | +We want the routing system to figure out how to reach `alice` from `bob` |
| 50 | + |
| 51 | +We can start off by defining the routing parameters. This is a compile-time constant shared across all nodes. |
| 52 | + |
| 53 | +```rust |
| 54 | +use root::framework::RoutingSystem; |
| 55 | +use root::router::NoMACSystem; |
| 56 | + |
| 57 | +struct SimpleExample {} // just a type to inform root of your network parameters |
| 58 | +impl RoutingSystem for SimpleExample{ |
| 59 | + type NodeAddress = String; // our nodes have string names |
| 60 | + type Link = i32; |
| 61 | + type MACSystem = NoMACSystem; // we won't use MAC for this example |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +Now, for each node, we can create a router: |
| 66 | +```rust |
| 67 | +// we have the following connection: bob <-> eve <-> alice |
| 68 | + |
| 69 | +let mut nodes = HashMap::new(); |
| 70 | + |
| 71 | +let mut bob = Router::<SimpleExample>::new("bob".to_string()); |
| 72 | +bob.links.insert(1, Neighbour::new("eve".to_string())); |
| 73 | +nodes.insert("bob", bob); |
| 74 | + |
| 75 | +let mut eve = Router::<SimpleExample>::new("eve".to_string()); |
| 76 | +eve.links.insert(1, Neighbour::new("bob".to_string())); |
| 77 | +eve.links.insert(2, Neighbour::new("alice".to_string())); |
| 78 | +nodes.insert("eve", eve); |
| 79 | + |
| 80 | +let mut alice = Router::<SimpleExample>::new("alice".to_string()); |
| 81 | +alice.links.insert(2, Neighbour::new("eve".to_string())); |
| 82 | +nodes.insert("alice", alice); |
| 83 | +``` |
| 84 | + |
| 85 | +Now we can let root take over, and have it automatically discover the route. |
| 86 | + |
| 87 | +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. |
| 88 | + |
| 89 | +```rust |
| 90 | +// lets simulate routing! |
| 91 | + |
| 92 | +for step in 0..3 { |
| 93 | + // collect all of our packets, if any |
| 94 | + let packets: Vec<OutboundPacket<SimpleExample>> = nodes.iter_mut().flat_map(|(_id, node)| node.outbound_packets.drain(..)).collect(); |
| 95 | + |
| 96 | + for OutboundPacket{link, dest, packet} in packets{ |
| 97 | + // 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 |
| 98 | + if let Some(node) = nodes.get_mut(dest.as_str()){ |
| 99 | + node.handle_packet(&packet, &link, &dest).expect("Failed to handle packet"); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + for node in nodes.values_mut(){ |
| 104 | + node.full_update(); // performs route table calculations, and writes routing updates into outbound_packets |
| 105 | + } |
| 106 | + |
| 107 | + // lets observe bob's route table: |
| 108 | + println!("Bob's routes in step {step}:"); |
| 109 | + for (neigh, Route::<SimpleExample>{ metric, next_hop, .. }) in &nodes["bob"].routes{ |
| 110 | + println!(" - {neigh}: metric: {metric}, next_hop: {next_hop}") |
| 111 | + } |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +Here is the output for this example: |
| 116 | +``` |
| 117 | +Bob's routes in step 0: |
| 118 | +Bob's routes in step 1: |
| 119 | +- eve: metric: 1, next_hop: eve |
| 120 | +Bob's routes in step 2: |
| 121 | +- eve: metric: 1, next_hop: eve |
| 122 | +- alice: metric: 2, next_hop: eve |
| 123 | +``` |
| 124 | +> [!NOTE] |
| 125 | +> You can try running this example yourself, its files are located in `./examples/super-simple` |
| 126 | +
|
| 127 | +## Network Example |
| 128 | + |
| 129 | +> [!NOTE] |
| 130 | +> To demonstrate the root crate working over real network connections, a complete example is provided in `./examples/simple-mesh`. |
| 131 | +
|
| 132 | +This example uses TCP streams as the transport, and is based on a event/channel pattern. |
0 commit comments