Explorar o código

Implement the sequence counter from RFC6550 7.2

Signed-off-by: Thibaut Vandervelden <thvdveld@vub.be>
Thibaut Vandervelden %!s(int64=2) %!d(string=hai) anos
pai
achega
be7bd72d5b
Modificáronse 4 ficheiros con 196 adicións e 0 borrados
  1. 2 0
      src/iface/mod.rs
  2. 1 0
      src/iface/rpl/consts.rs
  3. 189 0
      src/iface/rpl/lollipop.rs
  4. 4 0
      src/iface/rpl/mod.rs

+ 2 - 0
src/iface/mod.rs

@@ -10,6 +10,8 @@ mod interface;
 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 mod neighbor;
 mod route;
+#[cfg(feature = "proto-rpl")]
+mod rpl;
 mod socket_meta;
 mod socket_set;
 

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

@@ -0,0 +1 @@
+pub const SEQUENCE_WINDOW: u8 = 16;

+ 189 - 0
src/iface/rpl/lollipop.rs

@@ -0,0 +1,189 @@
+//! Implementation of sequence counters defined in [RFC 6550 § 7.2]. Values from 128 and greater
+//! are used as a linear sequence to indicate a restart and bootstrap the counter. Values less than
+//! or equal to 127 are used as a circular sequence number space of size 128. When operating in the
+//! circular region, if sequence numbers are detected to be too far apart, then they are not
+//! comparable.
+//!
+//! [RFC 6550 § 7.2]: https://datatracker.ietf.org/doc/html/rfc6550#section-7.2
+
+#[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct SequenceCounter(u8);
+
+impl Default for SequenceCounter {
+    fn default() -> Self {
+        // RFC6550 7.2 recommends 240 (256 - SEQUENCE_WINDOW) as the initialization value of the
+        // counter.
+        Self(240)
+    }
+}
+
+impl SequenceCounter {
+    /// Create a new sequence counter.
+    ///
+    /// Use `Self::default()` when a new sequence counter needs to be created with a value that is
+    /// recommended in RFC6550 7.2, being 240.
+    pub fn new(value: u8) -> Self {
+        Self(value)
+    }
+
+    /// Return the value of the sequence counter.
+    pub fn value(&self) -> u8 {
+        self.0
+    }
+
+    /// Increment the sequence counter.
+    ///
+    /// When the sequence counter is greater than or equal to 128, the maximum value is 255.
+    /// When the sequence counter is less than 128, the maximum value is 127.
+    ///
+    /// When an increment of the sequence counter would cause the counter to increment beyond its
+    /// maximum value, the counter MUST wrap back to zero.
+    pub fn increment(&mut self) {
+        let max = if self.0 >= 128 { 255 } else { 127 };
+
+        self.0 = match self.0.checked_add(1) {
+            Some(val) if val <= max => val,
+            _ => 0,
+        };
+    }
+}
+
+impl PartialEq for SequenceCounter {
+    fn eq(&self, other: &Self) -> bool {
+        let a = self.value() as usize;
+        let b = other.value() as usize;
+
+        if ((128..=255).contains(&a) && (0..=127).contains(&b))
+            || ((128..=255).contains(&b) && (0..=127).contains(&a))
+        {
+            false
+        } else {
+            let result = if a > b { a - b } else { b - a };
+
+            if result <= super::consts::SEQUENCE_WINDOW as usize {
+                // RFC1982
+                a == b
+            } else {
+                // This case is actually not comparable.
+                false
+            }
+        }
+    }
+}
+
+impl PartialOrd for SequenceCounter {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        use super::consts::SEQUENCE_WINDOW;
+        use core::cmp::Ordering;
+
+        let a = self.value() as usize;
+        let b = other.value() as usize;
+
+        if (128..256).contains(&a) && (0..128).contains(&b) {
+            if 256 + b - a <= SEQUENCE_WINDOW as usize {
+                Some(Ordering::Less)
+            } else {
+                Some(Ordering::Greater)
+            }
+        } else if (128..256).contains(&b) && (0..128).contains(&a) {
+            if 256 + a - b <= SEQUENCE_WINDOW as usize {
+                Some(Ordering::Greater)
+            } else {
+                Some(Ordering::Less)
+            }
+        } else if ((0..128).contains(&a) && (0..128).contains(&b))
+            || ((128..256).contains(&a) && (128..256).contains(&b))
+        {
+            let result = if a > b { a - b } else { b - a };
+
+            if result <= SEQUENCE_WINDOW as usize {
+                // RFC1982
+                a.partial_cmp(&b)
+            } else {
+                // This case is not comparable.
+                None
+            }
+        } else {
+            unreachable!();
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn sequence_counter_increment() {
+        let mut seq = SequenceCounter::new(253);
+        seq.increment();
+        assert_eq!(seq.value(), 254);
+        seq.increment();
+        assert_eq!(seq.value(), 255);
+        seq.increment();
+        assert_eq!(seq.value(), 0);
+
+        let mut seq = SequenceCounter::new(126);
+        seq.increment();
+        assert_eq!(seq.value(), 127);
+        seq.increment();
+        assert_eq!(seq.value(), 0);
+    }
+
+    #[test]
+    fn sequence_counter_comparison() {
+        use core::cmp::Ordering;
+
+        assert!(SequenceCounter::new(240) != SequenceCounter::new(1));
+        assert!(SequenceCounter::new(1) != SequenceCounter::new(240));
+        assert!(SequenceCounter::new(1) != SequenceCounter::new(240));
+        assert!(SequenceCounter::new(240) == SequenceCounter::new(240));
+        assert!(SequenceCounter::new(240 - 17) != SequenceCounter::new(240));
+
+        assert_eq!(
+            SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(5)),
+            Some(Ordering::Greater)
+        );
+        assert_eq!(
+            SequenceCounter::new(250).partial_cmp(&SequenceCounter::new(5)),
+            Some(Ordering::Less)
+        );
+        assert_eq!(
+            SequenceCounter::new(5).partial_cmp(&SequenceCounter::new(250)),
+            Some(Ordering::Greater)
+        );
+        assert_eq!(
+            SequenceCounter::new(127).partial_cmp(&SequenceCounter::new(129)),
+            Some(Ordering::Less)
+        );
+        assert_eq!(
+            SequenceCounter::new(120).partial_cmp(&SequenceCounter::new(121)),
+            Some(Ordering::Less)
+        );
+        assert_eq!(
+            SequenceCounter::new(121).partial_cmp(&SequenceCounter::new(120)),
+            Some(Ordering::Greater)
+        );
+        assert_eq!(
+            SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(241)),
+            Some(Ordering::Less)
+        );
+        assert_eq!(
+            SequenceCounter::new(241).partial_cmp(&SequenceCounter::new(240)),
+            Some(Ordering::Greater)
+        );
+        assert_eq!(
+            SequenceCounter::new(120).partial_cmp(&SequenceCounter::new(120)),
+            Some(Ordering::Equal)
+        );
+        assert_eq!(
+            SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(240)),
+            Some(Ordering::Equal)
+        );
+        assert_eq!(
+            SequenceCounter::new(130).partial_cmp(&SequenceCounter::new(241)),
+            None
+        );
+    }
+}

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

@@ -0,0 +1,4 @@
+#![allow(unused)]
+
+mod consts;
+mod lollipop;