Skip to content

Commit cdd4e40

Browse files
committed
add readme example
1 parent 2996aef commit cdd4e40

File tree

13 files changed

+195
-31
lines changed

13 files changed

+195
-31
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ members = [
55
"examples/mesh-vis",
66
"examples/simple-mesh",
77
"examples/graph-gen"
8-
]
8+
, "examples/super-simple"]

README.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The application is solely responsible for providing I/O and scheduling events, m
66

77
For a complete example routing application that uses TCP as transport, see `/examples/simple-mesh`.
88

9+
To get started with root, run:
10+
`cargo add root`, use the `serde` feature for serialization.
11+
912
# Why I/O-free?
1013

1114
root is designed from the ground up to offer a platform, network, and protocol agnostic way to do routing.
@@ -15,6 +18,7 @@ root is designed from the ground up to offer a platform, network, and protocol a
1518

1619
For more motivations, you can read [this set of articles](https://sans-io.readthedocs.io/index.html#).
1720

21+
1822
# Concepts
1923

2024
root tries its best to abstract the complexity of networking, while maintaining compatibility with low level concepts.
@@ -33,9 +37,23 @@ The link type represents a physical bidirectional connection between two nodes.
3337

3438
# Example Usage
3539

36-
To create a bare-bones network using root, you can use the following example:
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.
3752

3853
```rust
54+
use root::framework::RoutingSystem;
55+
use root::router::NoMACSystem;
56+
3957
struct SimpleExample {} // just a type to inform root of your network parameters
4058
impl RoutingSystem for SimpleExample{
4159
type NodeAddress = String; // our nodes have string names
@@ -44,3 +62,71 @@ impl RoutingSystem for SimpleExample{
4462
}
4563
```
4664

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.

examples/graph-gen/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
rand = "0.9.0-alpha.1"
7+
rand = "0.9.0-alpha.1"

examples/graph-gen/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::cmp::{max, min};
2-
use std::collections::HashSet;
2+
use std::collections::{HashMap, HashSet};
33
use rand::{Rng};
44

55
fn main() {

examples/mesh-vis/src/graph_parse.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,9 @@ pub fn load(state: &Yaml) -> anyhow::Result<State> {
206206
};
207207
for (_, neigh, metric) in adj.iter().filter(|x| x.0 == *node) {
208208
sys.router.links.insert(*neigh, Neighbour{
209-
link_cost: *metric,
209+
metric: *metric,
210210
addr: *neigh,
211-
routes: HashMap::new(),
212-
link: *neigh
211+
routes: HashMap::new()
213212
});
214213
}
215214
nodes.push(sys);
@@ -344,7 +343,7 @@ pub fn save(state: &State) -> Yaml {
344343
"{} {} {}",
345344
addr,
346345
n_addr,
347-
neigh.link_cost
346+
neigh.metric
348347
)
349348
.as_str(),
350349
));

examples/simple-mesh/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ async fn main() -> anyhow::Result<()> {
6464
*link,
6565
Neighbour {
6666
addr: netlink.neigh_node.clone(),
67-
link: netlink.link,
68-
link_cost: INF,
67+
metric: INF,
6968
routes: HashMap::new(),
7069
},
7170
);

examples/simple-mesh/src/mesh_router.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,7 @@ fn handle_packet(
347347
link_id,
348348
Neighbour {
349349
addr: node_id.clone(),
350-
link: link_id,
351-
link_cost: INF,
350+
metric: INF,
352351
routes: HashMap::new(),
353352
},
354353
);
@@ -427,7 +426,7 @@ fn update_link_health(
427426
new_ping: Duration,
428427
) -> anyhow::Result<()> {
429428
if let Some(neigh) = ps.router.links.get_mut(&link) {
430-
neigh.link_cost = {
429+
neigh.metric = {
431430
if new_ping == Duration::MAX {
432431
INF
433432
} else {
@@ -655,8 +654,7 @@ fn handle_command(
655654
netlink.link,
656655
Neighbour {
657656
addr: netlink.neigh_node.clone(),
658-
link: netlink.link,
659-
link_cost: INF,
657+
metric: INF,
660658
routes: HashMap::new(),
661659
},
662660
);

examples/super-simple/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "super-simple"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
root = { path = "../../root", features = ["serde"] }

examples/super-simple/src/main.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use std::collections::HashMap;
2+
use root::concepts::neighbour::Neighbour;
3+
use root::concepts::packet::OutboundPacket;
4+
use root::concepts::route::Route;
5+
use root::framework::RoutingSystem;
6+
use root::router::{NoMACSystem, Router};
7+
8+
struct SimpleExample {} // just a type to inform root of your network parameters
9+
impl RoutingSystem for SimpleExample{
10+
type NodeAddress = String; // our nodes have string names
11+
type Link = i32;
12+
type MACSystem = NoMACSystem; // we won't use MAC for this example
13+
}
14+
15+
fn main() {
16+
// we have the following connection: bob <-> eve <-> alice
17+
18+
let mut nodes = HashMap::new();
19+
20+
let mut bob = Router::<SimpleExample>::new("bob".to_string());
21+
bob.links.insert(1, Neighbour::new("eve".to_string()));
22+
nodes.insert("bob", bob);
23+
24+
let mut eve = Router::<SimpleExample>::new("eve".to_string());
25+
eve.links.insert(1, Neighbour::new("bob".to_string()));
26+
eve.links.insert(2, Neighbour::new("alice".to_string()));
27+
nodes.insert("eve", eve);
28+
29+
let mut alice = Router::<SimpleExample>::new("alice".to_string());
30+
alice.links.insert(2, Neighbour::new("eve".to_string()));
31+
nodes.insert("alice", alice);
32+
33+
// lets simulate routing!
34+
35+
for step in 0..3 {
36+
// collect all of our packets, if any
37+
let packets: Vec<OutboundPacket<SimpleExample>> = nodes.iter_mut().flat_map(|(_id, node)| node.outbound_packets.drain(..)).collect();
38+
39+
for OutboundPacket{link, dest, packet} in packets{
40+
// deliver the routing packet. in this simple example, the link isn't really used
41+
if let Some(node) = nodes.get_mut(dest.as_str()){
42+
node.handle_packet(&packet, &link, &dest).expect("Failed to handle packet");
43+
}
44+
}
45+
46+
for node in nodes.values_mut(){
47+
node.full_update(); // performs route table calculations, and writes routing updates into outbound_packets
48+
}
49+
50+
// lets observe bob's route table:
51+
println!("Bob's routes in step {step}:");
52+
for (neigh, Route::<SimpleExample>{ metric, next_hop, .. }) in &nodes["bob"].routes{
53+
println!(" - {neigh}: metric: {metric}, next_hop: {next_hop}")
54+
}
55+
}
56+
57+
// OUTPUT:
58+
// Bob's routes in step 0:
59+
// Bob's routes in step 1:
60+
// - eve: metric: 1, next_hop: eve
61+
// Bob's routes in step 2:
62+
// - eve: metric: 1, next_hop: eve
63+
// - alice: metric: 2, next_hop: eve
64+
}

root/src/concepts/neighbour.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ use crate::framework::{RoutingSystem};
1111
#[educe(Clone(bound()))]
1212
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))]
1313
pub struct Neighbour<T: RoutingSystem + ?Sized> {
14-
/// the physical network link id, the pair (link, addr) should be unique
15-
pub link: T::Link,
1614
/// the routing network address
1715
pub addr: T::NodeAddress,
18-
// pub hello_interval: Duration,
19-
// pub timer_last_ihu: Instant,
2016
pub routes: HashMap<T::NodeAddress, ExternalRoute<T>>,
21-
/// Direct Link-cost to this neighbour, 0xFFFF for Infinity. Lower is better.
17+
/// Direct Link-metric to this neighbour, 0xFFFF for Infinity. Lower is better.
2218
/// INF if the link is down
23-
pub link_cost: u16
19+
pub metric: u16
20+
}
21+
22+
impl<T: RoutingSystem + ?Sized> Neighbour<T>{
23+
pub fn new(addr: T::NodeAddress) -> Neighbour<T>{
24+
Self{
25+
addr,
26+
routes: Default::default(),
27+
metric: 1,
28+
}
29+
}
2430
}

0 commit comments

Comments
 (0)