Skip to content

Commit 5b2af5f

Browse files
authored
Merge pull request project-chip#50 from kedars/feature/dedup
de-dup for RX packets
2 parents 89c02e1 + 0e172f0 commit 5b2af5f

File tree

10 files changed

+260
-13
lines changed

10 files changed

+260
-13
lines changed

matter/src/data_model/sdm/noc.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::interaction_model::messages::ib;
3030
use crate::tlv::{FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV, UtfStr};
3131
use crate::transport::session::SessionMode;
3232
use crate::utils::writebuf::WriteBuf;
33-
use crate::{cmd_enter, error::*};
33+
use crate::{cmd_enter, error::*, secure_channel};
3434
use log::{error, info};
3535
use num_derive::FromPrimitive;
3636

@@ -177,6 +177,15 @@ impl NocCluster {
177177
return Err(NocStatus::InsufficientPrivlege);
178178
}
179179

180+
// This command's processing may take longer, send a stand alone ACK to the peer to avoid any retranmissions
181+
let ack_send = secure_channel::common::send_mrp_standalone_ack(
182+
cmd_req.trans.exch,
183+
cmd_req.trans.session,
184+
);
185+
if ack_send.is_err() {
186+
error!("Error sending Standalone ACK, falling back to piggybacked ACK");
187+
}
188+
180189
let r = AddNocReq::from_tlv(&cmd_req.data).map_err(|_| NocStatus::InvalidNOC)?;
181190

182191
let noc_value = Cert::new(r.noc_value.0).map_err(|_| NocStatus::InvalidNOC)?;
@@ -188,7 +197,6 @@ impl NocCluster {
188197
} else {
189198
None
190199
};
191-
192200
let fabric = Fabric::new(
193201
noc_data.key_pair,
194202
noc_data.root_ca,

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/interaction_model/command.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ macro_rules! cmd_enter {
3737
}};
3838
}
3939

40-
pub struct CommandReq<'a, 'b, 'c, 'd> {
40+
pub struct CommandReq<'a, 'b, 'c, 'd, 'e> {
4141
pub cmd: ib::CmdPath,
4242
pub data: TLVElement<'a>,
4343
pub resp: &'a mut TLVWriter<'b, 'c>,
44-
pub trans: &'a mut Transaction<'d>,
44+
pub trans: &'a mut Transaction<'d, 'e>,
4545
}
4646

4747
impl InteractionModel {

matter/src/interaction_model/core.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{
2525
exchange::Exchange,
2626
packet::Packet,
2727
proto_demux::{self, ProtoCtx, ResponseRequired},
28-
session::Session,
28+
session::SessionHandle,
2929
},
3030
};
3131
use colored::Colorize;
@@ -59,8 +59,8 @@ pub enum OpCode {
5959
TimedRequest = 10,
6060
}
6161

62-
impl<'a> Transaction<'a> {
63-
pub fn new(session: &'a mut Session, exch: &'a mut Exchange) -> Self {
62+
impl<'a, 'b> Transaction<'a, 'b> {
63+
pub fn new(session: &'a mut SessionHandle<'b>, exch: &'a mut Exchange) -> Self {
6464
Self {
6565
state: TransactionState::Ongoing,
6666
session,

matter/src/interaction_model/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use crate::{
1919
error::Error,
2020
tlv::TLVWriter,
21-
transport::{exchange::Exchange, proto_demux::ResponseRequired, session::Session},
21+
transport::{exchange::Exchange, proto_demux::ResponseRequired, session::SessionHandle},
2222
};
2323

2424
use self::{
@@ -32,9 +32,9 @@ pub enum TransactionState {
3232
Complete,
3333
Terminate,
3434
}
35-
pub struct Transaction<'a> {
35+
pub struct Transaction<'a, 'b> {
3636
pub state: TransactionState,
37-
pub session: &'a mut Session,
37+
pub session: &'a mut SessionHandle<'b>,
3838
pub exch: &'a mut Exchange,
3939
}
4040

matter/src/secure_channel/common.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@
1515
* limitations under the License.
1616
*/
1717

18+
use boxslab::Slab;
19+
use log::info;
1820
use num_derive::FromPrimitive;
1921

20-
use crate::{error::Error, transport::packet::Packet};
22+
use crate::{
23+
error::Error,
24+
transport::{
25+
exchange::Exchange,
26+
packet::{Packet, PacketPool},
27+
session::SessionHandle,
28+
},
29+
};
2130

2231
use super::status_report::{create_status_report, GeneralCode};
2332

@@ -83,3 +92,10 @@ pub fn create_mrp_standalone_ack(proto_tx: &mut Packet) {
8392
proto_tx.set_proto_opcode(OpCode::MRPStandAloneAck as u8);
8493
proto_tx.unset_reliable();
8594
}
95+
96+
pub fn send_mrp_standalone_ack(exch: &mut Exchange, sess: &mut SessionHandle) -> Result<(), Error> {
97+
info!("Sending standalone ACK");
98+
let mut ack_packet = Slab::<PacketPool>::try_new(Packet::new_tx()?).ok_or(Error::NoMemory)?;
99+
create_mrp_standalone_ack(&mut ack_packet);
100+
exch.send(ack_packet, sess)
101+
}

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/exchange.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl Exchange {
175175
}
176176
}
177177

178-
fn send(
178+
pub fn send(
179179
&mut self,
180180
mut proto_tx: BoxSlab<PacketPool>,
181181
session: &mut SessionHandle,

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;

0 commit comments

Comments
 (0)