diff --git a/Cargo.toml b/Cargo.toml index 9ee30c2e..280ba7d0 100644 --- a/Cargo.toml +++ b/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]] diff --git a/build.rs b/build.rs index 760ee189..19dd8636 100644 --- a/build.rs +++ b/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 ]; diff --git a/gen_config.py b/gen_config.py index 83d58738..46549b70 100644 --- a/gen_config.py +++ b/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 diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs index 2decec05..d7da7aa1 100644 --- a/src/iface/rpl/mod.rs +++ b/src/iface/rpl/mod.rs @@ -3,4 +3,5 @@ mod consts; mod lollipop; mod rank; +mod relations; mod trickle; diff --git a/src/iface/rpl/relations.rs b/src/iface/rpl/relations.rs new file mode 100644 index 00000000..da02a3cf --- /dev/null +++ b/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, +} + +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 { + 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 { + (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()); + } +} diff --git a/src/lib.rs b/src/lib.rs index ee8abdd5..1256292b 100644 --- a/src/lib.rs +++ b/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))]