/* * * Copyright (c) 2020-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::utils::epoch::Epoch; use core::time::Duration; use crate::{error::*, secure_channel, transport::packet::Packet}; use log::error; // 200 ms const MRP_STANDALONE_ACK_TIMEOUT: u64 = 200; #[derive(Debug)] pub struct RetransEntry { // The msg counter that we are waiting to be acknowledged msg_ctr: u32, // This will additionally have retransmission count and periods once we implement it } impl RetransEntry { pub fn new(msg_ctr: u32) -> Self { Self { msg_ctr } } pub fn get_msg_ctr(&self) -> u32 { self.msg_ctr } } #[derive(Debug, Clone)] pub struct AckEntry { // The msg counter that we should acknowledge msg_ctr: u32, // The max time after which this entry must be ACK ack_timeout: Duration, } impl AckEntry { pub fn new(msg_ctr: u32, epoch: Epoch) -> Result { if let Some(ack_timeout) = epoch().checked_add(Duration::from_millis(MRP_STANDALONE_ACK_TIMEOUT)) { Ok(Self { msg_ctr, ack_timeout, }) } else { Err(ErrorCode::Invalid.into()) } } pub fn get_msg_ctr(&self) -> u32 { self.msg_ctr } pub fn has_timed_out(&self, epoch: Epoch) -> bool { self.ack_timeout > epoch() } } #[derive(Default, Debug)] pub struct ReliableMessage { retrans: Option, ack: Option, } impl ReliableMessage { pub fn new() -> Self { Self { ..Default::default() } } pub fn is_empty(&self) -> bool { self.retrans.is_none() && self.ack.is_none() } // Check any pending acknowledgements / retransmissions and take action pub fn is_ack_ready(&self, epoch: Epoch) -> bool { // Acknowledgements if let Some(ack_entry) = &self.ack { ack_entry.has_timed_out(epoch) } else { false } } pub fn prepare_ack(_exch_id: u16, proto_tx: &mut Packet) { secure_channel::common::create_mrp_standalone_ack(proto_tx); } pub fn pre_send(&mut self, proto_tx: &mut Packet) -> Result<(), Error> { // Check if any acknowledgements are pending for this exchange, // if so, piggy back in the encoded header here if let Some(ack_entry) = &self.ack { // Ack Entry exists, set ACK bit and remove from table proto_tx.proto.set_ack(ack_entry.get_msg_ctr()); self.ack = None; } if !proto_tx.is_reliable() { return Ok(()); } if self.retrans.is_some() { // This indicates there was some existing entry for same sess-id/exch-id, which shouldnt happen error!("Previous retrans entry for this exchange already exists"); Err(ErrorCode::Invalid)?; } self.retrans = Some(RetransEntry::new(proto_tx.plain.ctr)); Ok(()) } /* A note about Message ACKs, it is a bit asymmetric in the sense that: * - there can be only one pending ACK per exchange (so this is per-exchange) * - there can be only one pending retransmission per exchange (so this is per-exchange) * - duplicate detection should happen per session (obviously), so that part is per-session */ pub fn recv(&mut self, proto_rx: &Packet, epoch: Epoch) -> Result<(), Error> { if proto_rx.proto.is_ack() { // Handle received Acks let ack_msg_ctr = proto_rx.proto.get_ack_msg_ctr().ok_or(ErrorCode::Invalid)?; if let Some(entry) = &self.retrans { if entry.get_msg_ctr() != ack_msg_ctr { // TODO: XXX Fix this error!("Mismatch in retrans-table's msg counter and received msg counter: received {}, expected {}. This is expected for the timebeing", ack_msg_ctr, entry.get_msg_ctr()); } self.retrans = None; } } if proto_rx.proto.is_reliable() { if self.ack.is_some() { // This indicates there was some existing entry for same sess-id/exch-id, which shouldnt happen // TODO: As per the spec if this happens, we need to send out the previous ACK and note this new ACK error!("Previous ACK entry for this exchange already exists"); Err(ErrorCode::Invalid)?; } self.ack = Some(AckEntry::new(proto_rx.plain.ctr, epoch)?); } Ok(()) } }