Skip to content

Commit f5837b4

Browse files
committed
Session: Duplicate detection for RX
1 parent 05263e7 commit f5837b4

File tree

4 files changed

+224
-1
lines changed

4 files changed

+224
-1
lines changed

matter/src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub enum Error {
2929
BufferTooSmall,
3030
ClusterNotFound,
3131
CommandNotFound,
32+
Duplicate,
3233
EndpointNotFound,
3334
Crypto,
3435
TLSStack,

matter/src/transport/dedup.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
const MSG_RX_STATE_BITMAP_LEN: u32 = 16;
19+
20+
#[derive(Debug)]
21+
pub struct RxCtrState {
22+
max_ctr: u32,
23+
ctr_bitmap: u16,
24+
}
25+
26+
impl RxCtrState {
27+
pub fn new(max_ctr: u32) -> Self {
28+
Self {
29+
max_ctr,
30+
ctr_bitmap: 0xffff,
31+
}
32+
}
33+
34+
fn contains(&self, bit_number: u32) -> bool {
35+
(self.ctr_bitmap & (1 << bit_number)) != 0
36+
}
37+
38+
fn insert(&mut self, bit_number: u32) {
39+
self.ctr_bitmap |= 1 << bit_number;
40+
}
41+
42+
/// Receive a message and update Rx State accordingly
43+
/// Returns a bool indicating whether the message is a duplicate
44+
pub fn recv(&mut self, msg_ctr: u32, is_encrypted: bool) -> bool {
45+
let idiff = (msg_ctr as i32) - (self.max_ctr as i32);
46+
let udiff = idiff.unsigned_abs();
47+
48+
if msg_ctr == self.max_ctr {
49+
// Duplicate
50+
true
51+
} else if (-(MSG_RX_STATE_BITMAP_LEN as i32)..0).contains(&idiff) {
52+
// In Rx Bitmap
53+
let index = udiff - 1;
54+
if self.contains(index) {
55+
// Duplicate
56+
true
57+
} else {
58+
self.insert(index);
59+
false
60+
}
61+
}
62+
// Now the leftover cases are the new counter is outside of the bitmap as well as max_ctr
63+
// in either direction. Encrypted only allows in forward direction
64+
else if msg_ctr > self.max_ctr {
65+
self.max_ctr = msg_ctr;
66+
if udiff < MSG_RX_STATE_BITMAP_LEN {
67+
// The previous max_ctr is now the actual counter
68+
self.ctr_bitmap <<= udiff;
69+
self.insert(udiff - 1);
70+
} else {
71+
self.ctr_bitmap = 0xffff;
72+
}
73+
false
74+
} else if !is_encrypted {
75+
// This is the case where the peer possibly rebooted and chose a different
76+
// random counter
77+
self.max_ctr = msg_ctr;
78+
self.ctr_bitmap = 0xffff;
79+
false
80+
} else {
81+
true
82+
}
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
89+
use super::RxCtrState;
90+
91+
const ENCRYPTED: bool = true;
92+
const NOT_ENCRYPTED: bool = false;
93+
94+
fn assert_ndup(b: bool) {
95+
assert!(!b);
96+
}
97+
98+
fn assert_dup(b: bool) {
99+
assert!(b);
100+
}
101+
102+
#[test]
103+
fn new_msg_ctr() {
104+
let mut s = RxCtrState::new(101);
105+
106+
assert_ndup(s.recv(103, ENCRYPTED));
107+
assert_ndup(s.recv(104, ENCRYPTED));
108+
assert_ndup(s.recv(106, ENCRYPTED));
109+
assert_eq!(s.max_ctr, 106);
110+
assert_eq!(s.ctr_bitmap, 0b1111_1111_1111_0110);
111+
112+
assert_ndup(s.recv(118, NOT_ENCRYPTED));
113+
assert_eq!(s.ctr_bitmap, 0b0110_1000_0000_0000);
114+
assert_ndup(s.recv(119, NOT_ENCRYPTED));
115+
assert_ndup(s.recv(121, NOT_ENCRYPTED));
116+
assert_eq!(s.ctr_bitmap, 0b0100_0000_0000_0110);
117+
}
118+
119+
#[test]
120+
fn dup_max_ctr() {
121+
let mut s = RxCtrState::new(101);
122+
123+
assert_ndup(s.recv(103, ENCRYPTED));
124+
assert_dup(s.recv(103, ENCRYPTED));
125+
assert_dup(s.recv(103, NOT_ENCRYPTED));
126+
127+
assert_eq!(s.max_ctr, 103);
128+
assert_eq!(s.ctr_bitmap, 0b1111_1111_1111_1110);
129+
}
130+
131+
#[test]
132+
fn dup_in_rx_bitmap() {
133+
let mut ctr = 101;
134+
let mut s = RxCtrState::new(101);
135+
for _ in 1..8 {
136+
ctr += 2;
137+
assert_ndup(s.recv(ctr, ENCRYPTED));
138+
}
139+
assert_ndup(s.recv(116, ENCRYPTED));
140+
assert_ndup(s.recv(117, ENCRYPTED));
141+
assert_eq!(s.max_ctr, 117);
142+
assert_eq!(s.ctr_bitmap, 0b1010_1010_1010_1011);
143+
144+
// duplicate on the left corner
145+
assert_dup(s.recv(101, ENCRYPTED));
146+
assert_dup(s.recv(101, NOT_ENCRYPTED));
147+
148+
// duplicate on the right corner
149+
assert_dup(s.recv(116, ENCRYPTED));
150+
assert_dup(s.recv(116, NOT_ENCRYPTED));
151+
152+
// valid insert
153+
assert_ndup(s.recv(102, ENCRYPTED));
154+
assert_dup(s.recv(102, ENCRYPTED));
155+
assert_eq!(s.ctr_bitmap, 0b1110_1010_1010_1011);
156+
}
157+
158+
#[test]
159+
fn valid_corners_in_rx_bitmap() {
160+
let mut ctr = 102;
161+
let mut s = RxCtrState::new(101);
162+
for _ in 1..9 {
163+
ctr += 2;
164+
assert_ndup(s.recv(ctr, ENCRYPTED));
165+
}
166+
assert_eq!(s.max_ctr, 118);
167+
assert_eq!(s.ctr_bitmap, 0b0010_1010_1010_1010);
168+
169+
// valid insert on the left corner
170+
assert_ndup(s.recv(102, ENCRYPTED));
171+
assert_eq!(s.ctr_bitmap, 0b1010_1010_1010_1010);
172+
173+
// valid insert on the right corner
174+
assert_ndup(s.recv(117, ENCRYPTED));
175+
assert_eq!(s.ctr_bitmap, 0b1010_1010_1010_1011);
176+
}
177+
178+
#[test]
179+
fn encrypted_wraparound() {
180+
let mut s = RxCtrState::new(65534);
181+
182+
assert_ndup(s.recv(65535, ENCRYPTED));
183+
assert_ndup(s.recv(65536, ENCRYPTED));
184+
assert_dup(s.recv(0, ENCRYPTED));
185+
}
186+
187+
#[test]
188+
fn unencrypted_wraparound() {
189+
let mut s = RxCtrState::new(65534);
190+
191+
assert_ndup(s.recv(65536, NOT_ENCRYPTED));
192+
assert_ndup(s.recv(0, NOT_ENCRYPTED));
193+
}
194+
195+
#[test]
196+
fn unencrypted_device_reboot() {
197+
println!("Sub 65532 is {:?}", 1_u16.overflowing_sub(65532));
198+
println!("Sub 65535 is {:?}", 1_u16.overflowing_sub(65535));
199+
println!("Sub 11-13 is {:?}", 11_u32.wrapping_sub(13_u32) as i32);
200+
println!("Sub regular is {:?}", 2000_u16.overflowing_sub(1998));
201+
let mut s = RxCtrState::new(20010);
202+
203+
assert_ndup(s.recv(20011, NOT_ENCRYPTED));
204+
assert_ndup(s.recv(0, NOT_ENCRYPTED));
205+
}
206+
}

matter/src/transport/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
mod dedup;
1819
pub mod exchange;
1920
pub mod mgr;
2021
pub mod mrp;

matter/src/transport/session.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use log::{info, trace};
3333
use rand::Rng;
3434

3535
use super::{
36+
dedup::RxCtrState,
3637
network::{Address, NetworkInterface},
3738
packet::{Packet, PacketPool},
3839
};
@@ -84,6 +85,7 @@ pub struct Session {
8485
local_sess_id: u16,
8586
peer_sess_id: u16,
8687
msg_ctr: u32,
88+
rx_ctr_state: RxCtrState,
8789
mode: SessionMode,
8890
data: Option<Box<dyn Any>>,
8991
last_use: SystemTime,
@@ -138,6 +140,7 @@ impl Session {
138140
peer_sess_id: 0,
139141
local_sess_id: 0,
140142
msg_ctr: rand::thread_rng().gen_range(0..MATTER_MSG_CTR_RANGE),
143+
rx_ctr_state: RxCtrState::new(0),
141144
mode: SessionMode::PlainText,
142145
data: None,
143146
last_use: SystemTime::now(),
@@ -156,6 +159,7 @@ impl Session {
156159
local_sess_id: clone_from.local_sess_id,
157160
peer_sess_id: clone_from.peer_sess_id,
158161
msg_ctr: rand::thread_rng().gen_range(0..MATTER_MSG_CTR_RANGE),
162+
rx_ctr_state: RxCtrState::new(0),
159163
mode: clone_from.mode,
160164
data: None,
161165
last_use: SystemTime::now(),
@@ -481,7 +485,17 @@ impl SessionMgr {
481485
rx.plain.get_src_u64(),
482486
rx.plain.is_encrypted(),
483487
) {
484-
Ok(s) => Some(s),
488+
Ok(s) => {
489+
let session = self.sessions[s].as_mut().unwrap();
490+
let is_encrypted = session.is_encrypted();
491+
let duplicate = session.rx_ctr_state.recv(rx.plain.ctr, is_encrypted);
492+
if duplicate {
493+
info!("Dropping duplicate packet");
494+
return Err(Error::Duplicate);
495+
} else {
496+
Some(s)
497+
}
498+
}
485499
Err(Error::NoSpace) => None,
486500
Err(e) => {
487501
return Err(e);
@@ -508,6 +522,7 @@ impl SessionMgr {
508522

509523
// Get session
510524
let sess_handle = self.post_recv(&rx)?;
525+
511526
Ok((rx, sess_handle))
512527
}
513528

0 commit comments

Comments
 (0)