Browse Source

add(rpl): relations table

The relation table will containt the next hop for each node in the RPL
DODAG. For MOP1, this is the parent of each node. For MOP2, this is a
neighbor of a sender.

Signed-off-by: Thibaut Vandervelden <[email protected]>
Thibaut Vandervelden 1 year ago
parent
commit
d02d002c0b
6 changed files with 175 additions and 0 deletions
  1. 9 0
      Cargo.toml
  2. 1 0
      build.rs
  3. 1 0
      gen_config.py
  4. 1 0
      src/iface/rpl/mod.rs
  5. 162 0
      src/iface/rpl/relations.rs
  6. 1 0
      src/lib.rs

+ 9 - 0
Cargo.toml

@@ -214,6 +214,15 @@ dns-max-name-size-64 = []
 dns-max-name-size-128 = []
 dns-max-name-size-255 = [] # Default
 
+rpl-relations-buffer-count-1 = []
+rpl-relations-buffer-count-2 = []
+rpl-relations-buffer-count-4 = []
+rpl-relations-buffer-count-8 = []
+rpl-relations-buffer-count-16 = [] # Default
+rpl-relations-buffer-count-32 = []
+rpl-relations-buffer-count-64 = []
+rpl-relations-buffer-count-128 = []
+
 # END AUTOGENERATED CONFIG FEATURES
 
 [[example]]

+ 1 - 0
build.rs

@@ -18,6 +18,7 @@ static CONFIGS: &[(&str, usize)] = &[
     ("DNS_MAX_RESULT_COUNT", 1),
     ("DNS_MAX_SERVER_COUNT", 1),
     ("DNS_MAX_NAME_SIZE", 255),
+    ("RPL_RELATIONS_BUFFER_COUNT", 16),
     // END AUTOGENERATED CONFIG FEATURES
 ];
 

+ 1 - 0
gen_config.py

@@ -39,6 +39,7 @@ feature("reassembly_buffer_count", default=1, min=1, max=32, pow2=4)
 feature("dns_max_result_count", default=1, min=1, max=32, pow2=4)
 feature("dns_max_server_count", default=1, min=1, max=32, pow2=4)
 feature("dns_max_name_size", default=255, min=64, max=255, pow2=True)
+feature("rpl_relations_buffer_count", default=16, min=1, max=128, pow2=True)
 
 # ========= Update Cargo.toml
 

+ 1 - 0
src/iface/rpl/mod.rs

@@ -3,4 +3,5 @@
 mod consts;
 mod lollipop;
 mod rank;
+mod relations;
 mod trickle;

+ 162 - 0
src/iface/rpl/relations.rs

@@ -0,0 +1,162 @@
+use crate::time::Instant;
+use crate::wire::Ipv6Address;
+
+use crate::config::RPL_RELATIONS_BUFFER_COUNT;
+
+#[derive(Debug)]
+pub struct Relation {
+    destination: Ipv6Address,
+    next_hop: Ipv6Address,
+    expiration: Instant,
+}
+
+#[derive(Default, Debug)]
+pub struct Relations {
+    relations: heapless::Vec<Relation, { RPL_RELATIONS_BUFFER_COUNT }>,
+}
+
+impl Relations {
+    /// Add a new relation to the buffer. If there was already a relation in the buffer, then
+    /// update it.
+    pub fn add_relation(
+        &mut self,
+        destination: Ipv6Address,
+        next_hop: Ipv6Address,
+        expiration: Instant,
+    ) {
+        if let Some(r) = self
+            .relations
+            .iter_mut()
+            .find(|r| r.destination == destination)
+        {
+            r.next_hop = next_hop;
+            r.expiration = expiration;
+        } else {
+            let relation = Relation {
+                destination,
+                next_hop,
+                expiration,
+            };
+
+            if let Err(e) = self.relations.push(relation) {
+                net_debug!("Unable to add relation, buffer is full");
+            }
+        }
+    }
+
+    /// Remove all relation entries for a specific destination.
+    pub fn remove_relation(&mut self, destination: Ipv6Address) {
+        self.relations.retain(|r| r.destination != destination)
+    }
+
+    /// Return the next hop for a specific IPv6 address, if there is one.
+    pub fn find_next_hop(&mut self, destination: Ipv6Address) -> Option<Ipv6Address> {
+        self.relations.iter().find_map(|r| {
+            if r.destination == destination {
+                Some(r.next_hop)
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Purge expired relations.
+    pub fn purge(&mut self, now: Instant) {
+        self.relations.retain(|r| r.expiration > now)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::time::Duration;
+
+    fn addresses(count: usize) -> Vec<Ipv6Address> {
+        (0..count)
+            .map(|i| {
+                let mut ip = Ipv6Address::default();
+                ip.0[0] = i as u8;
+                ip
+            })
+            .collect()
+    }
+
+    #[test]
+    fn add_relation() {
+        let addrs = addresses(2);
+
+        let mut relations = Relations::default();
+        relations.add_relation(addrs[0], addrs[1], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+    }
+
+    #[test]
+    fn add_relations_full_buffer() {
+        let addrs = addresses(crate::config::RPL_RELATIONS_BUFFER_COUNT + 1);
+
+        // Try to add RPL_RELATIONS_BUFFER_COUNT + 1 to the buffer.
+        // The size of the buffer should still be RPL_RELATIONS_BUFFER_COUNT.
+        let mut relations = Relations::default();
+        for a in addrs {
+            relations.add_relation(a, a, Instant::now());
+        }
+
+        assert_eq!(relations.relations.len(), RPL_RELATIONS_BUFFER_COUNT);
+    }
+
+    #[test]
+    fn update_relation() {
+        let addrs = addresses(3);
+
+        let mut relations = Relations::default();
+        relations.add_relation(addrs[0], addrs[1], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+
+        relations.add_relation(addrs[0], addrs[2], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+
+        assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[2]));
+    }
+
+    #[test]
+    fn find_next_hop() {
+        let addrs = addresses(3);
+
+        let mut relations = Relations::default();
+        relations.add_relation(addrs[0], addrs[1], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+        assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[1]));
+
+        relations.add_relation(addrs[0], addrs[2], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+        assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[2]));
+
+        // Find the next hop of a destination not in the buffer.
+        assert_eq!(relations.find_next_hop(addrs[1]), None);
+    }
+
+    #[test]
+    fn remove_relation() {
+        let addrs = addresses(2);
+
+        let mut relations = Relations::default();
+        relations.add_relation(addrs[0], addrs[1], Instant::now());
+        assert_eq!(relations.relations.len(), 1);
+
+        relations.remove_relation(addrs[0]);
+        assert!(relations.relations.is_empty());
+    }
+
+    #[test]
+    fn purge_relation() {
+        let addrs = addresses(2);
+
+        let mut relations = Relations::default();
+        relations.add_relation(addrs[0], addrs[1], Instant::now() - Duration::from_secs(1));
+
+        assert_eq!(relations.relations.len(), 1);
+
+        relations.purge(Instant::now());
+        assert!(relations.relations.is_empty());
+    }
+}

+ 1 - 0
src/lib.rs

@@ -144,6 +144,7 @@ mod config {
     pub const IFACE_NEIGHBOR_CACHE_COUNT: usize = 3;
     pub const REASSEMBLY_BUFFER_COUNT: usize = 4;
     pub const REASSEMBLY_BUFFER_SIZE: usize = 1500;
+    pub const RPL_RELATIONS_BUFFER_COUNT: usize = 16;
 }
 
 #[cfg(not(test))]