Skip to main content

rs_matter/transport/
exchange.rs

1/*
2 *
3 *    Copyright (c) 2022-2026 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
18use core::fmt::{self, Display};
19use core::num::NonZeroU8;
20use core::pin::pin;
21
22use either::Either as EitherIo;
23use embassy_futures::select::{select, select3, Either, Either3};
24use embassy_time::{Duration, Instant, Timer};
25
26use crate::acl::Accessor;
27use crate::bdx::{self, PROTO_ID_BDX};
28use crate::crypto::Crypto;
29use crate::dm::{AuxAclCheck, Metadata, NodeId};
30use crate::error::{Error, ErrorCode};
31use crate::im::{self, PROTO_ID_INTERACTION_MODEL};
32use crate::sc::{self, PROTO_ID_SECURE_CHANNEL};
33use crate::transport::session::Sessions;
34use crate::transport::TxPayloadState;
35use crate::utils::storage::pooled::{PooledBuffers, DEFAULT_BUFFER_POOL_SIZE};
36use crate::utils::storage::WriteBuf;
37use crate::{Matter, MatterState};
38
39use super::mrp::{ReliableMessage, RetransEntry};
40use super::network;
41use super::packet::PacketHdr;
42use super::plain_hdr::PlainHdr;
43use super::proto_hdr::ProtoHdr;
44use super::session::{Session, SessionMode};
45use super::{PacketAccess, MAX_RX_BUF_SIZE, MAX_TX_BUF_SIZE};
46
47/// Minimum buffer which should be allocated by user code that wants to pull RX messages via `Exchange::recv_into`
48///
49/// When the `large-buffers` feature is enabled, this tracks the larger TCP-capable packet
50/// size, so that IM-level buffers (e.g. `IMBuffer`) can absorb a full Matter-over-TCP
51/// message. Otherwise it stays at the UDP-sized default.
52#[cfg(feature = "large-buffers")]
53pub const MAX_EXCHANGE_RX_BUF_SIZE: usize = network::MAX_RX_LARGE_PACKET_SIZE;
54#[cfg(not(feature = "large-buffers"))]
55pub const MAX_EXCHANGE_RX_BUF_SIZE: usize = network::MAX_RX_PACKET_SIZE;
56
57/// Maximum buffer which should be allocated and used by user code that wants to send messages via `Exchange::send`
58///
59/// Mirrors `MAX_EXCHANGE_RX_BUF_SIZE` with respect to the `large-buffers` feature.
60#[cfg(feature = "large-buffers")]
61pub const MAX_EXCHANGE_TX_BUF_SIZE: usize =
62    network::MAX_TX_LARGE_PACKET_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE;
63#[cfg(not(feature = "large-buffers"))]
64pub const MAX_EXCHANGE_TX_BUF_SIZE: usize =
65    network::MAX_TX_PACKET_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE;
66
67/// A protocol payload buffer, sized to hold a full exchange RX message
68/// ([`MAX_EXCHANGE_RX_BUF_SIZE`]).
69///
70/// This is the central buffer type that both [`IMBuffer`](crate::im::IMBuffer)
71/// and [`BdxBuffer`](crate::bdx::BdxBuffer) alias, so a single
72/// [`PooledBuffers`](crate::utils::storage::pooled::PooledBuffers) pool can be
73/// shared across the data model and BDX.
74pub type Buffer = crate::utils::storage::Vec<u8, MAX_EXCHANGE_RX_BUF_SIZE>;
75
76/// A [`PooledBuffers`] pool pre-configured with Matter defaults: it holds
77/// [`Buffer`]s (the central exchange-sized buffer) and [`DEFAULT_BUFFER_POOL_SIZE`]
78/// of them, behind the default Matter raw mutex.
79pub type MatterBuffers<const N: usize = DEFAULT_BUFFER_POOL_SIZE> = PooledBuffers<Buffer, N>;
80
81/// An exchange identifier, uniquely identifying a session and an exchange within that session for a given Matter stack.
82#[derive(Copy, Clone, Debug, Eq, PartialEq)]
83pub struct ExchangeId(u32);
84
85impl ExchangeId {
86    pub(crate) fn new(session_id: u32, exchange_index: usize) -> Self {
87        if session_id > 0x0fff_ffff {
88            panic!("Session ID out of range");
89        }
90
91        if exchange_index >= 16 {
92            panic!("Exchange index out of range");
93        }
94
95        Self(((exchange_index as u32) << 28) | session_id)
96    }
97
98    pub(crate) fn session_id(&self) -> u32 {
99        self.0 & 0x0fff_ffff
100    }
101
102    pub(crate) fn exchange_index(&self) -> usize {
103        (self.0 >> 28) as _
104    }
105
106    /// Get the session associated with this exchange from the given sessions store.
107    ///
108    /// ATTENTION: This method will panic if the session is not found in the store, so make sure to only call it when you are sure the session exists.
109    pub(crate) fn session<'a>(&self, sessions: &'a mut Sessions) -> &'a mut Session {
110        unwrap!(sessions.get(self.session_id()))
111    }
112
113    /// Get the exchange state associated with this exchange from the given sessions store.
114    pub(crate) fn exch<'a>(&self, session: &'a mut Session) -> &'a mut ExchangeState {
115        unwrap!(session.exchanges[self.exchange_index()].as_mut())
116    }
117
118    pub(crate) fn display<'a>(&'a self, session: &'a Session) -> ExchangeIdDisplay<'a> {
119        ExchangeIdDisplay { id: self, session }
120    }
121
122    async fn recv<'a>(&self, matter: &'a Matter<'a>) -> Result<RxMessage<'a>, Error> {
123        self.check_no_pending_retrans(matter)?;
124
125        loop {
126            let mut recv = pin!(matter.transport().get_if_rx(|packet| {
127                if packet.buf.is_empty() {
128                    false
129                } else {
130                    let for_us = self.with_state(matter, |state| {
131                        let sess = self.session(&mut state.sessions);
132                        if sess.is_for_rx(&packet.peer, &packet.header.plain) {
133                            let exch = self.exch(sess);
134
135                            return Ok(exch.is_for_rx(&packet.header.proto));
136                        }
137
138                        Ok(false)
139                    });
140
141                    for_us.unwrap_or(true)
142                }
143            }));
144
145            let mut session_removed = pin!(matter.transport().wait_session_removed());
146
147            let mut timeout = pin!(Timer::after(Duration::from_millis(
148                RetransEntry::new(matter.dev_det().sai, 0).max_delay_ms() * 3 / 2
149            )));
150
151            match select3(&mut recv, &mut session_removed, &mut timeout).await {
152                Either3::First(mut packet) => {
153                    packet.clear_on_drop(true);
154
155                    self.check_no_pending_retrans(matter)?;
156
157                    break Ok(RxMessage(packet));
158                }
159                Either3::Second(_) => {
160                    // Session removed
161
162                    // Bail out if it was ours
163                    self.with_state(matter, |_| Ok(()))?;
164
165                    // If not, go back waiting for a packet
166                    continue;
167                }
168                Either3::Third(_) => {
169                    // Timeout waiting for an answer from the other peer
170                    Err(ErrorCode::RxTimeout)?;
171                }
172            };
173        }
174    }
175
176    /// Gets access to the TX buffer of the Matter stack for constructing a new TX message.
177    /// If the TX buffer is not available, the method will wait indefinitely until it becomes available.
178    ///
179    /// NOTE:
180    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
181    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
182    ///
183    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
184    /// (say, because of lack of resources or a hard networking error), the method will return an error.
185    async fn init_send<'a>(&self, matter: &'a Matter<'a>) -> Result<TxMessage<'a>, Error> {
186        self.with_state(matter, |_| Ok(()))?;
187
188        let mut packet = matter
189            .transport
190            .get_if_tx(|packet| {
191                packet.buf.is_empty() || self.with_state(matter, |_| Ok(())).is_err()
192            })
193            .await;
194
195        // TODO: Resizing might be a bit expensive with large buffers
196        unwrap!(packet.buf.resize_default(MAX_TX_BUF_SIZE));
197
198        packet.clear_on_drop(true);
199
200        let tx = TxMessage {
201            exchange_id: *self,
202            matter,
203            packet,
204        };
205
206        self.with_state(matter, |_| Ok(()))?;
207
208        Ok(tx)
209    }
210
211    /// Waits until the other side acknowledges the last message sent on this exchange,
212    /// or until time for a re-transmission had come.
213    ///
214    /// If the last sent message was not using the MRP protocol, the method will return immediately with `TxOutcome::Done`.
215    ///
216    /// NOTE:
217    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
218    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
219    ///
220    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
221    /// (say, because of lack of resources or a hard networking error), the method will return an error.
222    async fn wait_tx<'a>(&self, matter: &'a Matter<'a>) -> Result<TxOutcome, Error> {
223        if let Some(delay) = self.retrans_delay_ms(matter)? {
224            let expired = unwrap!(Instant::now().checked_add(Duration::from_millis(delay)));
225
226            loop {
227                let mut notification = pin!(self.internal_wait_ack(matter));
228                let mut session_removed = pin!(matter.transport().wait_session_removed());
229                let mut timer = pin!(Timer::at(expired));
230
231                if !matches!(
232                    select3(&mut notification, &mut session_removed, &mut timer).await,
233                    Either3::Second(_)
234                ) {
235                    break;
236                }
237
238                // Bail out if the removed session was ours
239                self.with_state(matter, |_| Ok(()))?;
240            }
241
242            if self.retrans_delay_ms(matter)?.is_some() {
243                Ok(TxOutcome::Retransmit)
244            } else {
245                Ok(TxOutcome::Done)
246            }
247        } else {
248            Ok(TxOutcome::Done)
249        }
250    }
251
252    fn accessor<'a>(
253        &self,
254        matter: &'a Matter<'a>,
255        aux_acl_enabled: bool,
256    ) -> Result<Accessor<'a>, Error> {
257        self.with_state(matter, |state| {
258            let sess = self.session(&mut state.sessions);
259
260            Ok(Accessor::for_session(sess, matter, aux_acl_enabled))
261        })
262    }
263
264    fn with_state<'a, F, T>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, Error>
265    where
266        F: FnOnce(&mut MatterState) -> Result<T, Error>,
267    {
268        self.with_state_ex(matter, f)
269    }
270
271    fn with_state_ex<'a, F, T, E>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, E>
272    where
273        F: FnOnce(&mut MatterState) -> Result<T, E>,
274        E: From<Error>,
275    {
276        matter.with_state(|state| {
277            if state.sessions.get(self.session_id()).is_some() {
278                f(state)
279            } else {
280                warn!("Exchange {}: No session", self);
281                Err(Error::from(ErrorCode::NoSession).into())
282            }
283        })
284    }
285
286    async fn internal_wait_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
287        matter
288            .transport
289            .get_if_rx(|_| {
290                self.retrans_delay_ms(matter)
291                    .map(|retrans| retrans.is_none())
292                    .unwrap_or(true)
293            })
294            .await;
295
296        self.with_state(matter, |_| Ok(()))
297    }
298
299    fn retrans_delay_ms<'a>(&self, matter: &'a Matter<'a>) -> Result<Option<u64>, Error> {
300        self.with_state(matter, |state| {
301            let sess = self.session(&mut state.sessions);
302            let exch = self.exch(sess);
303
304            let mut jitter_rand = [0; 1];
305            // TODO XXX FIXME matter.rand()(&mut jitter_rand);
306            jitter_rand[0] = 100;
307
308            Ok(exch.retrans_delay_ms(jitter_rand[0]))
309        })
310    }
311
312    fn check_no_pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
313        self.with_state(matter, |state| {
314            let sess = self.session(&mut state.sessions);
315            let exch = self.exch(sess);
316
317            if exch.mrp.is_retrans_pending() {
318                error!("Exchange {}: Retransmission pending", self.display(sess));
319                Err(ErrorCode::InvalidState)?;
320            }
321
322            Ok(())
323        })
324    }
325
326    fn pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
327        Ok(self.retrans_delay_ms(matter)?.is_some())
328    }
329
330    fn pending_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
331        self.with_state(matter, |state| {
332            let sess = self.session(&mut state.sessions);
333            let exch = self.exch(sess);
334
335            Ok(exch.mrp.is_ack_pending())
336        })
337    }
338}
339
340impl Display for ExchangeId {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        write!(f, "{}::{}", self.session_id(), self.exchange_index())
343    }
344}
345
346#[cfg(feature = "defmt")]
347impl defmt::Format for ExchangeId {
348    fn format(&self, f: defmt::Formatter<'_>) {
349        defmt::write!(f, "{}::{}", self.session_id(), self.exchange_index())
350    }
351}
352
353/// A display wrapper for `ExchangeId` which also displays
354/// the packet session ID, packet peer session ID and packet exchange ID.
355pub struct ExchangeIdDisplay<'a> {
356    id: &'a ExchangeId,
357    session: &'a Session,
358}
359
360impl Display for ExchangeIdDisplay<'_> {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        let state = self.session.exchanges[self.id.exchange_index()].as_ref();
363
364        if let Some(state) = state {
365            write!(
366                f,
367                "{} [SID:{:x},RSID:{:x},EID:{:x}]",
368                self.id,
369                self.session.get_local_sess_id(),
370                self.session.get_peer_sess_id(),
371                state.exch_id
372            )
373        } else {
374            // This should never happen, as that would mean we have invalid exchange index
375            // but let's not crash when displaying that
376            write!(f, "{}???", self.id)
377        }
378    }
379}
380
381#[cfg(feature = "defmt")]
382impl defmt::Format for ExchangeIdDisplay<'_> {
383    fn format(&self, f: defmt::Formatter<'_>) {
384        let state = self.session.exchanges[self.id.exchange_index()].as_ref();
385
386        if let Some(state) = state {
387            defmt::write!(
388                f,
389                "{} [SID:{:x},RSID:{:x},EID:{:x}]",
390                self.id,
391                self.session.get_local_sess_id(),
392                self.session.get_peer_sess_id(),
393                state.exch_id
394            )
395        } else {
396            // This should never happen, as that would mean we have invalid exchange index
397            // but let's not crash when displaying that
398            defmt::write!(f, "{}???", self.id)
399        }
400    }
401}
402
403#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
404#[cfg_attr(feature = "defmt", derive(defmt::Format))]
405pub(crate) enum InitiatorState {
406    #[default]
407    Owned,
408    Dropped,
409}
410
411#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
412#[cfg_attr(feature = "defmt", derive(defmt::Format))]
413pub(crate) enum ResponderState {
414    #[default]
415    AcceptPending,
416    Owned,
417    Dropped,
418}
419
420#[derive(Debug, PartialEq, Eq, Copy, Clone)]
421#[cfg_attr(feature = "defmt", derive(defmt::Format))]
422pub(crate) enum Role {
423    Initiator(InitiatorState),
424    Responder(ResponderState),
425}
426
427impl Role {
428    pub fn is_dropped_state(&self) -> bool {
429        match self {
430            Self::Initiator(state) => *state == InitiatorState::Dropped,
431            Self::Responder(state) => *state == ResponderState::Dropped,
432        }
433    }
434
435    pub fn set_dropped_state(&mut self) {
436        match self {
437            Self::Initiator(state) => *state = InitiatorState::Dropped,
438            Self::Responder(state) => *state = ResponderState::Dropped,
439        }
440    }
441}
442
443#[derive(Debug)]
444#[cfg_attr(feature = "defmt", derive(defmt::Format))]
445pub(crate) struct ExchangeState {
446    pub(crate) exch_id: u16,
447    pub(crate) role: Role,
448    pub(crate) mrp: ReliableMessage,
449    /// The Global Group Encrypted Data Message Counter value reserved for
450    /// this exchange's (single) group data message - already covered by a
451    /// durably stored boundary, see [`Exchange::initiate_group`].
452    ///
453    /// Per-exchange rather than per-session, so that two concurrent sends to
454    /// the same group cannot consume each other's reservation. `None` for
455    /// every other exchange kind.
456    #[cfg(feature = "groups")]
457    pub(crate) group_data_ctr: Option<u32>,
458}
459
460impl ExchangeState {
461    pub fn is_for_rx(&self, rx_proto: &ProtoHdr) -> bool {
462        self.exch_id == rx_proto.exch_id
463            && rx_proto.is_initiator() == matches!(self.role, Role::Responder(_))
464    }
465
466    pub fn post_recv(&mut self, rx_plain: &PlainHdr, rx_proto: &ProtoHdr) -> Result<(), Error> {
467        self.mrp.post_recv(rx_plain, rx_proto)?;
468
469        Ok(())
470    }
471
472    pub fn pre_send(
473        &mut self,
474        tx_plain: &PlainHdr,
475        tx_proto: &mut ProtoHdr,
476        session_active_interval_ms: Option<u32>,
477        session_idle_interval_ms: Option<u32>,
478    ) -> Result<(), Error> {
479        if matches!(self.role, Role::Initiator(_)) {
480            tx_proto.set_initiator();
481        } else {
482            tx_proto.unset_initiator();
483        }
484
485        tx_proto.exch_id = self.exch_id;
486
487        self.mrp.pre_send(
488            tx_plain,
489            tx_proto,
490            session_active_interval_ms,
491            session_idle_interval_ms,
492        )
493    }
494
495    pub fn retrans_delay_ms(&mut self, jitter_rand: u8) -> Option<u64> {
496        self.mrp
497            .retrans
498            .as_ref()
499            .map(|retrans| retrans.delay_ms(jitter_rand))
500    }
501}
502
503/// Meta-data when sending/receving messages via an Exchange.
504/// Basically, the protocol ID, the protocol opcode and whether the message should be set in a reliable manner.
505#[derive(Debug, Eq, PartialEq, Copy, Clone)]
506pub struct MessageMeta {
507    pub proto_id: u16,
508    pub proto_opcode: u8,
509    pub reliable: bool,
510}
511
512impl MessageMeta {
513    // Create a new message meta-data instance
514    pub const fn new(proto_id: u16, proto_opcode: u8, reliable: bool) -> Self {
515        Self {
516            proto_id,
517            proto_opcode,
518            reliable,
519        }
520    }
521
522    /// Try to cast the protocol opcode to a specific type
523    pub fn opcode<T: num::FromPrimitive>(&self) -> Result<T, Error> {
524        num::FromPrimitive::from_u8(self.proto_opcode).ok_or(ErrorCode::InvalidOpcode.into())
525    }
526
527    /// Check if the protocol opcode is equal to a specific value
528    pub fn check_opcode<T: num::FromPrimitive + PartialEq>(&self, opcode: T) -> Result<(), Error> {
529        if self.opcode::<T>()? == opcode {
530            Ok(())
531        } else {
532            Err(ErrorCode::Invalid.into())
533        }
534    }
535
536    /// Create an instance from a ProtoHdr instance
537    pub fn from(proto: &ProtoHdr) -> Self {
538        Self {
539            proto_id: proto.proto_id,
540            proto_opcode: proto.proto_opcode,
541            reliable: proto.is_reliable(),
542        }
543    }
544
545    /// Set the protocol ID and opcode into a ProtoHdr instance
546    pub fn set_into(&self, proto: &mut ProtoHdr) {
547        proto.proto_id = self.proto_id;
548        proto.proto_opcode = self.proto_opcode;
549        proto.set_vendor(None);
550
551        if self.reliable {
552            proto.set_reliable();
553        } else {
554            proto.unset_reliable();
555        }
556    }
557
558    pub fn reliable(self, reliable: bool) -> Self {
559        Self { reliable, ..self }
560    }
561
562    /// Utility method to check if the specific proto opcode in the instance is expecting a TLV payload.
563    pub(crate) fn is_tlv(&self) -> bool {
564        match self.proto_id {
565            PROTO_ID_SECURE_CHANNEL => self
566                .opcode::<sc::OpCode>()
567                .ok()
568                .map(|op| op.is_tlv())
569                .unwrap_or(false),
570            PROTO_ID_INTERACTION_MODEL => self
571                .opcode::<im::OpCode>()
572                .ok()
573                .map(|op| op.is_tlv())
574                .unwrap_or(false),
575            _ => false,
576        }
577    }
578
579    /// Utility method to check if the protocol is Secure Channel, and the opcode is a standalone ACK (`MrpStandaloneAck`).
580    pub(crate) fn is_standalone_ack(&self) -> bool {
581        self.proto_id == PROTO_ID_SECURE_CHANNEL
582            && self.proto_opcode == sc::OpCode::MRPStandAloneAck as u8
583    }
584
585    /// Utility method to check if the protocol is Secure Channel, and the opcode is Status.
586    pub(crate) fn is_sc_status(&self) -> bool {
587        self.proto_id == PROTO_ID_SECURE_CHANNEL
588            && self.proto_opcode == sc::OpCode::StatusReport as u8
589    }
590
591    /// Utility method to check if the protocol is Secure Channel, and the opcode is a new session request.
592    pub(crate) fn is_new_session(&self) -> bool {
593        self.proto_id == PROTO_ID_SECURE_CHANNEL
594            && (self.proto_opcode == sc::OpCode::PBKDFParamRequest as u8
595                || self.proto_opcode == sc::OpCode::CASESigma1 as u8)
596    }
597
598    /// Utility method to check if the meta-data indicates a new exchange
599    pub(crate) fn is_new_exchange(&self) -> bool {
600        // Don't create new exchanges for standalone ACKs and for SC status codes
601        !self.is_standalone_ack() && !self.is_sc_status()
602    }
603
604    /// Whether this message is a *control* message. Control messages
605    /// travel on a separate counter space (the "control message
606    /// counter") and set the `C` bit in the plain-header Security
607    /// Flags. Today only the MCSP opcodes qualify; the transport
608    /// layer flips the `C` bit on the outgoing packet based on this.
609    pub fn is_control_msg(&self) -> bool {
610        self.proto_id == PROTO_ID_SECURE_CHANNEL
611            && (self.proto_opcode == sc::OpCode::MsgCounterSyncReq as u8
612                || self.proto_opcode == sc::OpCode::MsgCounterSyncResp as u8)
613    }
614}
615
616impl Display for MessageMeta {
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618        match self.proto_id {
619            PROTO_ID_SECURE_CHANNEL => {
620                if let Ok(opcode) = self.opcode::<sc::OpCode>() {
621                    write!(f, "SC::{:?}", opcode)
622                } else {
623                    write!(f, "SC::{:02x}", self.proto_opcode)
624                }
625            }
626            PROTO_ID_INTERACTION_MODEL => {
627                if let Ok(opcode) = self.opcode::<im::OpCode>() {
628                    write!(f, "IM::{:?}", opcode)
629                } else {
630                    write!(f, "IM::{:02x}", self.proto_opcode)
631                }
632            }
633            PROTO_ID_BDX => {
634                if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
635                    write!(f, "BDX::{:?}", opcode)
636                } else {
637                    write!(f, "BDX::{:02x}", self.proto_opcode)
638                }
639            }
640            _ => write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
641        }
642    }
643}
644
645#[cfg(feature = "defmt")]
646impl defmt::Format for MessageMeta {
647    fn format(&self, f: defmt::Formatter<'_>) {
648        match self.proto_id {
649            PROTO_ID_SECURE_CHANNEL => {
650                if let Ok(opcode) = self.opcode::<sc::OpCode>() {
651                    defmt::write!(f, "SC::{:?}", opcode)
652                } else {
653                    defmt::write!(f, "SC::{:02x}", self.proto_opcode)
654                }
655            }
656            PROTO_ID_INTERACTION_MODEL => {
657                if let Ok(opcode) = self.opcode::<im::OpCode>() {
658                    defmt::write!(f, "IM::{:?}", opcode)
659                } else {
660                    defmt::write!(f, "IM::{:02x}", self.proto_opcode)
661                }
662            }
663            PROTO_ID_BDX => {
664                if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
665                    defmt::write!(f, "BDX::{:?}", opcode)
666                } else {
667                    defmt::write!(f, "BDX::{:02x}", self.proto_opcode)
668                }
669            }
670            _ => defmt::write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
671        }
672    }
673}
674
675/// An RX message pending on an `Exchange` instance.
676pub struct RxMessage<'a>(PacketAccess<'a, MAX_RX_BUF_SIZE>);
677
678impl RxMessage<'_> {
679    /// Get the meta-data of the pending message
680    pub fn meta(&self) -> MessageMeta {
681        MessageMeta::from(&self.0.header.proto)
682    }
683
684    /// Get the payload of the pending message
685    pub fn payload(&self) -> &[u8] {
686        &self.0.buf[self.0.payload_start..]
687    }
688}
689
690/// Accessor to the TX message buffer of the underlying Matter transport stack.
691///
692/// This is used to construct a new TX message to be sent on an `Exchange` instance.
693///
694/// NOTE: It is strongly advised to use the `TxMessage` accessor in combination with the `Sender` utility,
695/// which takes care of all message retransmission logic. Alternatively, one can use the
696/// `Exchange::send` or `Exchange::send_with` which also take care of re-transmissions.
697pub struct TxMessage<'a> {
698    exchange_id: ExchangeId,
699    matter: &'a Matter<'a>,
700    packet: PacketAccess<'a, MAX_TX_BUF_SIZE>,
701}
702
703impl TxMessage<'_> {
704    /// Get a reference to the payload buffer of the TX message being built
705    pub fn payload(&mut self) -> &mut [u8] {
706        &mut self.packet.buf[PacketHdr::HDR_RESERVE..MAX_TX_BUF_SIZE - PacketHdr::TAIL_RESERVE]
707    }
708
709    /// Complete and send a TX message by providing:
710    /// - The payload size that was filled-in by user code in the payload buffer returned by `TxMessage::payload`
711    /// - The TX message meta-data
712    pub fn complete<M>(
713        mut self,
714        payload_start: usize,
715        payload_end: usize,
716        meta: M,
717    ) -> Result<(), Error>
718    where
719        M: Into<MessageMeta>,
720    {
721        if payload_start > payload_end
722            || payload_end - payload_start
723                > MAX_TX_BUF_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE
724        {
725            Err(ErrorCode::Invalid)?;
726        }
727
728        let meta: MessageMeta = meta.into();
729
730        self.packet.header.reset();
731
732        meta.set_into(&mut self.packet.header.proto);
733
734        self.matter.with_state(|state| {
735            let session = state
736                .sessions
737                .get(self.exchange_id.session_id())
738                .ok_or(ErrorCode::NoSession)?;
739
740            // The session's `peer_active_interval_ms` / `peer_idle_interval_ms`
741            // are seeded from our own `BasicInfoConfig` at session-creation
742            // time and overwritten once Sigma1 / Sigma2 / PBKDFParamRequest /
743            // PBKDFParamResponse delivers a real peer value, so MRP
744            // retransmission backoff to this peer reflects whichever is
745            // more accurate (Matter Core spec).
746            let (peer, retransmission) = session.pre_send(
747                Some(self.exchange_id.exchange_index()),
748                &mut self.packet.header,
749                Some(session.get_peer_active_interval_ms()),
750                Some(session.get_peer_idle_interval_ms()),
751            )?;
752
753            self.packet.peer = peer;
754            self.packet.payload_start = PacketHdr::HDR_RESERVE + payload_start;
755            self.packet
756                .buf
757                .truncate(PacketHdr::HDR_RESERVE + payload_end);
758            self.packet.tx_info.payload_state = TxPayloadState::NotEncoded {
759                session_id: session.id,
760            };
761            self.packet.tx_info.retransmission = retransmission;
762            self.packet.clear_on_drop(false);
763
764            Ok(())
765        })
766    }
767}
768
769/// Outcome from calling `Exchange::wait_tx`
770#[derive(Copy, Clone, Eq, PartialEq, Debug)]
771#[cfg_attr(feature = "defmt", derive(defmt::Format))]
772pub enum TxOutcome {
773    /// The other side has acknowledged the last message or the last message was not using the MRP protocol
774    /// Stop re-sending.
775    Done,
776    /// Need to re-send the last message.
777    Retransmit,
778}
779
780impl TxOutcome {
781    /// Check if the outcome is `Done`
782    pub const fn is_done(&self) -> bool {
783        matches!(self, Self::Done)
784    }
785}
786
787pub struct SenderTx<'a, 'b> {
788    sender: &'b mut Sender<'a>,
789    message: TxMessage<'a>,
790}
791
792impl SenderTx<'_, '_> {
793    pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
794        (self.sender.exchange, self.message.payload())
795    }
796
797    pub fn payload(&mut self) -> &mut [u8] {
798        self.message.payload()
799    }
800
801    pub fn complete(
802        self,
803        payload_start: usize,
804        payload_end: usize,
805        meta: MessageMeta,
806    ) -> Result<(), Error> {
807        self.message.complete(payload_start, payload_end, meta)?;
808
809        self.sender.initial = false;
810
811        Ok(())
812    }
813}
814
815/// Utility struct for sending a message with potential retransmissions.
816pub struct Sender<'a> {
817    exchange: &'a Exchange<'a>,
818    initial: bool,
819    complete: bool,
820}
821
822impl<'a> Sender<'a> {
823    fn new(exchange: &'a Exchange<'a>) -> Result<Self, Error> {
824        exchange.id.check_no_pending_retrans(exchange.matter)?;
825
826        Ok(Self {
827            exchange,
828            initial: true,
829            complete: false,
830        })
831    }
832
833    /// Get the TX buffer of the underlying Matter stack for (re)constructing a new TX message,
834    /// waiting for the TX buffer to become available, if it is not.
835    ///
836    /// If the method returns `None`, it means that the message was already acknowledged by the other side,
837    /// or that the message does not need acknowledgement and re-transmissions.
838    ///
839    /// When called for the first time, the method will always return a `Some` value, as the message has not been sent even once yet.
840    /// Once the method returns `None`, it will always return `None` on subsequent calls, as the message has been acknowledged by the other side.
841    ///
842    /// Example:
843    /// ```ignore
844    /// let exchange = ...;
845    ///
846    /// let sender = exchange.sender()?;
847    ///
848    /// while let Some(mut tx) = sender.tx().await? {
849    ///     let (exchange, payload) = tx.split()?;
850    ///
851    ///     // Write the message payload in the `payload` `&mut [u8]` slice
852    ///     // On every iteration of the loop, write the _same_ payload (as message re-transmission is idempotent w.r.t. the message)
853    ///     ...
854    ///
855    ///     // Complete the payload by providing `MessageMeta`, payload start and payload end
856    ///     // On every iteration of the loop, proide the _same_ meta-data (as message re-transmission is idempotent w.r.t. the message)
857    ///     let meta = ...;
858    ///     let payload_start = ...;
859    ///     let payload_end = ...;
860    ///
861    ///     tx.complete(payload_start, payload_end, meta)?;
862    /// }
863    /// ```
864    pub async fn tx(&mut self) -> Result<Option<SenderTx<'a, '_>>, Error> {
865        trace!(
866            "Sender::tx called, initial={}, complete={}",
867            self.initial,
868            self.complete
869        );
870        if self.complete {
871            trace!("Sender::tx - already complete, returning None");
872            return Ok(None);
873        }
874
875        if !self.initial {
876            trace!("Sender::tx - not initial, calling wait_tx");
877            let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
878            trace!("Sender::tx - wait_tx returned {:?}", outcome);
879            if outcome.is_done() {
880                // No need to re-transmit
881                self.complete = true;
882                trace!("Sender::tx - ACK received, returning None");
883                return Ok(None);
884            }
885            trace!("Sender::tx - need to retransmit");
886        }
887
888        let id = self.exchange.id;
889        let matter = self.exchange.matter;
890
891        trace!("Sender::tx - calling init_send");
892        let tx = id.init_send(matter).await?;
893        trace!("Sender::tx - init_send returned");
894
895        if self.initial || id.pending_retrans(matter)? {
896            trace!("Sender::tx - returning Some(SenderTx)");
897            Ok(Some(SenderTx {
898                sender: self,
899                message: tx,
900            }))
901        } else {
902            self.complete = true;
903            trace!("Sender::tx - no pending retrans, returning None");
904            Ok(None)
905        }
906    }
907}
908
909/// Owned-`Self` counterpart to [`SenderTx`].
910///
911/// Holds the [`Exchange`] by value (rather than via a `&mut` through
912/// the parent [`Sender`]), so consumers that consume their exchange
913/// can drive the retransmit loop without a self-referential struct.
914/// Returned by [`OwnedSender::tx`] when the framework needs the
915/// message bytes (re-)built into a fresh TX slot.
916///
917/// Pair with [`SenderTx`] for the borrowed-self mirror.
918pub struct OwnedSenderTx<'a> {
919    exchange: Exchange<'a>,
920    message: TxMessage<'a>,
921}
922
923impl<'a> OwnedSenderTx<'a> {
924    /// Get a `(borrowed-exchange, payload-slice)` pair for inspecting
925    /// the exchange and writing the message bytes.
926    pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
927        (&self.exchange, self.message.payload())
928    }
929
930    /// Get a mutable reference to the payload slice the bytes go into.
931    pub fn payload(&mut self) -> &mut [u8] {
932        self.message.payload()
933    }
934
935    /// Commit the bytes currently in the slot. The framework dispatches
936    /// the wire-level send asynchronously; this call returns the
937    /// [`OwnedSender`] so the caller can ask for the next event
938    /// (retransmit, ACK).
939    pub fn complete(
940        self,
941        payload_start: usize,
942        payload_end: usize,
943        meta: MessageMeta,
944    ) -> Result<OwnedSender<'a>, Error> {
945        self.message.complete(payload_start, payload_end, meta)?;
946
947        Ok(OwnedSender {
948            exchange: self.exchange,
949            initial: false,
950            complete: false,
951        })
952    }
953}
954
955/// Owned-`Self` counterpart to [`Sender`].
956///
957/// Consumes the [`Exchange`] on construction (via
958/// [`Exchange::into_sender`]) and returns it back to the caller when
959/// the retransmit loop is done (i.e. when [`OwnedSender::tx`]
960/// completes with `Either::Right`, meaning the message has been
961/// ACK-ed by the peer or did not require ACK).
962///
963/// Use cases:
964/// - The Interaction Model client (`im::client::ImClient`) consumes
965///   an exchange to drive a single IM transaction end-to-end, and
966///   wants to walk the retransmit loop while still owning the
967///   `Exchange` for the subsequent receive phase. The owned-`Self`
968///   shape avoids a self-referential struct that would otherwise hold
969///   both an owned `Exchange` and a `&mut`-Sender on it.
970///
971/// User loop pattern (mirrors [`Sender`]):
972///
973/// ```ignore
974/// let mut sender = exchange.into_sender()?;
975/// let exchange = loop {
976///     match sender.tx().await? {
977///         Either::Left(mut slot) => {
978///             let (_, payload) = slot.split();
979///             // Write the message bytes (same every iteration —
980///             // retransmission is idempotent w.r.t. the message).
981///             let (start, end, meta) = /* … */;
982///             sender = slot.complete(start, end, meta)?;
983///         }
984///         Either::Right(exchange) => break exchange,
985///     }
986/// };
987/// // `exchange` is yours again.
988/// ```
989pub struct OwnedSender<'a> {
990    exchange: Exchange<'a>,
991    initial: bool,
992    complete: bool,
993}
994
995impl<'a> OwnedSender<'a> {
996    fn new(exchange: Exchange<'a>) -> Result<Self, Error> {
997        exchange.id.check_no_pending_retrans(exchange.matter)?;
998
999        Ok(Self {
1000            exchange,
1001            initial: true,
1002            complete: false,
1003        })
1004    }
1005
1006    /// Next event from the framework. Owned-`Self` mirror of
1007    /// [`Sender::tx`]:
1008    /// - `Either::Left(slot)` — a TX slot is ready; (re-)build the
1009    ///   message bytes into it and call `slot.complete(...)` to get
1010    ///   the `OwnedSender` back for the next iteration.
1011    /// - `Either::Right(exchange)` — the message has been ACK-ed (or
1012    ///   did not need an ACK); the loop is done and the exchange is
1013    ///   returned to the caller.
1014    pub async fn tx(mut self) -> Result<EitherIo<OwnedSenderTx<'a>, Exchange<'a>>, Error> {
1015        trace!(
1016            "OwnedSender::tx called, initial={}, complete={}",
1017            self.initial,
1018            self.complete
1019        );
1020        if self.complete {
1021            trace!("OwnedSender::tx - already complete, returning exchange");
1022            return Ok(EitherIo::Right(self.exchange));
1023        }
1024
1025        if !self.initial {
1026            trace!("OwnedSender::tx - not initial, calling wait_tx");
1027            let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
1028            trace!("OwnedSender::tx - wait_tx returned {:?}", outcome);
1029            if outcome.is_done() {
1030                // No need to re-transmit
1031                self.complete = true;
1032                trace!("OwnedSender::tx - ACK received, returning exchange");
1033                return Ok(EitherIo::Right(self.exchange));
1034            }
1035            trace!("OwnedSender::tx - need to retransmit");
1036        }
1037
1038        let id = self.exchange.id;
1039        let matter = self.exchange.matter;
1040
1041        trace!("OwnedSender::tx - calling init_send");
1042        let tx = id.init_send(matter).await?;
1043        trace!("OwnedSender::tx - init_send returned");
1044
1045        if self.initial || id.pending_retrans(matter)? {
1046            trace!("OwnedSender::tx - returning Left(OwnedSenderTx)");
1047            Ok(EitherIo::Left(OwnedSenderTx {
1048                exchange: self.exchange,
1049                message: tx,
1050            }))
1051        } else {
1052            trace!("OwnedSender::tx - no pending retrans, returning exchange");
1053            Ok(EitherIo::Right(self.exchange))
1054        }
1055    }
1056}
1057
1058/// An exchange within a Matter stack, representing a session and an exchange within that session.
1059///
1060/// This is the main API for sending and receiving messages within the Matter stack.
1061/// Used by upper-level layers like the Secure Channel and Interaction Model.
1062pub struct Exchange<'a> {
1063    id: ExchangeId,
1064    matter: &'a Matter<'a>,
1065    rx: Option<RxMessage<'a>>,
1066}
1067
1068impl<'a> Exchange<'a> {
1069    pub(crate) const fn new(id: ExchangeId, matter: &'a Matter<'a>) -> Self {
1070        Self {
1071            id,
1072            matter,
1073            rx: None,
1074        }
1075    }
1076
1077    /// Get the Id of the exchange
1078    pub fn id(&self) -> ExchangeId {
1079        self.id
1080    }
1081
1082    /// Get the Matter stack instance associated with this exchange
1083    pub fn matter(&self) -> &'a Matter<'a> {
1084        self.matter
1085    }
1086
1087    /// Open an exchange over a CASE session to an already-commissioned node.
1088    ///
1089    /// If a CASE session for `(fabric_idx, peer_node_id)` already exists, an
1090    /// exchange is opened on it directly via [`Exchange::initiate_for_session`]
1091    /// (the common case - session and peer address reused, no mDNS). Otherwise
1092    /// the peer's operational address is resolved over mDNS and a fresh CASE
1093    /// session is established (driving [`crate::sc::case::CaseInitiator`]) before
1094    /// the exchange is opened on it; the peer's MRP/session parameters advertised
1095    /// in the mDNS TXT records seed the session.
1096    ///
1097    /// Establishing requires a running mDNS responder (e.g.
1098    /// `BuiltinMdns::run`) to service the resolve; without one the
1099    /// resolve times out and this returns [`ErrorCode::NotFound`].
1100    ///
1101    /// With the `case-responder-only` feature the establish path is compiled
1102    /// out: only an existing session is reused, and if none exists this returns
1103    /// [`ErrorCode::NoSession`] instead of opening a new one. This lets the linker
1104    /// drop the CASE initiator and mDNS resolver from a node that never needs to
1105    /// initiate CASE (a pure accessory that only reports over sessions its peers
1106    /// established).
1107    #[inline(always)]
1108    pub async fn initiate<C: Crypto>(
1109        matter: &'a Matter<'a>,
1110        crypto: C,
1111        fabric_idx: NonZeroU8,
1112        peer_node_id: NodeId,
1113    ) -> Result<Self, Error> {
1114        matter
1115            .transport
1116            .initiate(matter, crypto, fabric_idx, peer_node_id)
1117            .await
1118    }
1119
1120    /// Open an exchange over a **PASE** session to a not-yet-commissioned node
1121    /// at the given peer address (use-case 2).
1122    ///
1123    /// If a PASE session **to that peer** already exists, an exchange is opened on
1124    /// it directly via [`Exchange::initiate_for_session`]. Otherwise a new PASE
1125    /// session is established: a plaintext session is opened and the PASE protocol
1126    /// ([`crate::sc::pase::PaseInitiator`]) is run with `passcode`, then an
1127    /// exchange is opened on the resulting PASE session.
1128    ///
1129    /// Reuse is keyed by peer address (not a single global PASE session), so a
1130    /// commissioner can drive several concurrent commissionings.
1131    ///
1132    /// The peer's MRP/session parameters are negotiated by PASE itself
1133    /// (PBKDFParamRequest/Response).
1134    ///
1135    /// Discovery of the address is out of scope here (it may come from mDNS - see
1136    /// [`crate::transport::Transport::browse_commissionable`] - or from a BLE/BTP
1137    /// advertisement, etc.); this method is transport-agnostic and takes the
1138    /// already-known address.
1139    #[inline(always)]
1140    pub async fn initiate_pase<C: Crypto>(
1141        matter: &'a Matter<'a>,
1142        crypto: C,
1143        peer_addr: network::Address,
1144        passcode: u32,
1145    ) -> Result<Self, Error> {
1146        matter
1147            .transport
1148            .initiate_pase(matter, crypto, peer_addr, passcode)
1149            .await
1150    }
1151
1152    /// Create a new initiator exchange on the provided Matter stack for the provided session ID.
1153    #[inline(always)]
1154    pub fn initiate_for_session<C: Crypto>(
1155        matter: &'a Matter<'a>,
1156        crypto: C,
1157        session_id: u32,
1158    ) -> Result<Self, Error> {
1159        matter
1160            .transport()
1161            .initiate_for_session(matter, crypto, session_id)
1162    }
1163
1164    /// Create a new initiator exchange for sending group DATA messages to
1165    /// `(fab_idx, group_id)` — encrypted with the group's active operational
1166    /// key and addressed at the group's multicast address.
1167    ///
1168    /// Group messages are fire-and-forget: no MRP, no acknowledgements and
1169    /// no responses — so the exchange is only good for *sending* (e.g. an
1170    /// Interaction Model invoke with `SuppressResponse`); do not wait for
1171    /// replies on it. The group's security material (a `GroupKeyMap` entry
1172    /// plus its key set) must be provisioned on the fabric, else this fails
1173    /// with [`ErrorCode::NotFound`].
1174    #[cfg(feature = "groups")]
1175    pub fn initiate_group<C: Crypto, K: crate::persist::KvBlobStoreAccess>(
1176        matter: &'a Matter<'a>,
1177        crypto: C,
1178        kv: K,
1179        fab_idx: NonZeroU8,
1180        group_id: u16,
1181    ) -> Result<Self, Error> {
1182        let (session_id, group_data_ctr, boundary) = matter.with_state(|state| {
1183            let session = state.sessions.get_or_create_for_group_tx(
1184                &crypto,
1185                &state.fabrics,
1186                fab_idx,
1187                group_id,
1188                matter.dev_det(),
1189            )?;
1190            let session_id = session.id;
1191
1192            // One group data message per exchange, so exactly one counter
1193            // value is reserved here.
1194            let (ctr, boundary) = state.sessions.reserve_global_group_data_ctr(&crypto)?;
1195
1196            Ok::<_, Error>((session_id, ctr, boundary))
1197        })?;
1198
1199        // Store the moved boundary BEFORE the reserved value can reach the
1200        // wire: a restart then resumes past it, so receivers never see a
1201        // counter value replayed. Writes happen once per
1202        // `GROUP_DATA_CTR_EPOCH` messages, not per message.
1203        if let Some(boundary) = boundary {
1204            kv.access(|store, buf| {
1205                store.store(
1206                    crate::persist::GROUP_DATA_COUNTER_KEY,
1207                    &boundary.to_le_bytes(),
1208                    buf,
1209                )
1210            })?;
1211
1212            debug!(
1213                "Group data message counter boundary persisted: {}",
1214                boundary
1215            );
1216        }
1217
1218        let exchange = Self::initiate_for_session(matter, crypto, session_id)?;
1219
1220        // Stash the reservation in the per-exchange state, which is where
1221        // `Session::pre_send` picks it up when stamping the message.
1222        matter.with_state(|state| {
1223            let session = state
1224                .sessions
1225                .get(exchange.id().session_id())
1226                .ok_or(ErrorCode::NoSession)?;
1227
1228            let exch = session.exchanges[exchange.id().exchange_index()]
1229                .as_mut()
1230                .ok_or(ErrorCode::NoExchange)?;
1231
1232            exch.group_data_ctr = Some(group_data_ctr);
1233
1234            Ok::<_, Error>(())
1235        })?;
1236
1237        Ok(exchange)
1238    }
1239
1240    /// Create a new initiator exchange on a new plaintext session to
1241    /// the given peer address.
1242    ///
1243    /// Low-level primitive below the three high-level entry points
1244    /// ([`Exchange::initiate`], [`Exchange::initiate_pase`],
1245    /// [`Exchange::initiate_for_session`]): the returned exchange carries the
1246    /// first handshake message of PASE ([`crate::sc::pase::PaseInitiator`]) or
1247    /// CASE ([`crate::sc::case::CaseInitiator`]). Use this only when driving a handshake protocol
1248    /// directly; otherwise prefer `initiate_pase` (which runs PASE for you) or
1249    /// `initiate` (CASE). If there is no space for a new session, an existing
1250    /// session is evicted and the operation retried.
1251    #[inline(always)]
1252    pub async fn initiate_plaintext<C: Crypto>(
1253        matter: &'a Matter<'a>,
1254        crypto: C,
1255        peer_addr: network::Address,
1256    ) -> Result<Self, Error> {
1257        matter
1258            .transport
1259            .initiate_plaintext(matter, crypto, peer_addr)
1260            .await
1261    }
1262
1263    /// Open an exchange over a fresh plaintext session to an already-
1264    /// commissioned node, resolving its operational address over mDNS.
1265    ///
1266    /// Like [`initiate_plaintext`](Self::initiate_plaintext) but it discovers the
1267    /// peer address itself (as [`initiate`](Self::initiate) does for CASE), rather
1268    /// than taking a known one. For sessionless protocols that carry their own
1269    /// security and must not establish a CASE session — e.g. sending an ICD
1270    /// Check-In message to a registered client.
1271    ///
1272    /// Requires a running mDNS responder to service the resolve; without one the
1273    /// resolve times out and this returns [`ErrorCode::NotFound`].
1274    #[inline(always)]
1275    pub async fn initiate_plaintext_operational<C: Crypto>(
1276        matter: &'a Matter<'a>,
1277        crypto: C,
1278        fabric_idx: NonZeroU8,
1279        peer_node_id: NodeId,
1280    ) -> Result<Self, Error> {
1281        matter
1282            .transport
1283            .initiate_plaintext_operational(matter, crypto, fabric_idx, peer_node_id)
1284            .await
1285    }
1286
1287    /// Accepts a new responder exchange pending on the provided Matter stack.
1288    ///
1289    /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1290    #[inline(always)]
1291    pub async fn accept(matter: &'a Matter<'a>) -> Result<Self, Error> {
1292        Self::accept_after(matter, 0).await
1293    }
1294
1295    /// Accepts a new responder exchange pending on the provided Matter stack, but only if the
1296    /// pending exchange was pending for longer than `received_timeout_ms`.
1297    ///
1298    /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1299    pub async fn accept_after(
1300        matter: &'a Matter<'a>,
1301        received_timeout_ms: u32,
1302    ) -> Result<Self, Error> {
1303        if received_timeout_ms > 0 {
1304            loop {
1305                let mut accept = pin!(matter.transport().accept_if(matter, |_, exch, _| {
1306                    exch.mrp.has_rx_timed_out(received_timeout_ms as _)
1307                }));
1308
1309                let mut timer = pin!(Timer::after(embassy_time::Duration::from_millis(
1310                    received_timeout_ms as u64
1311                )));
1312
1313                if let Either::First(exchange) = select(&mut accept, &mut timer).await {
1314                    break exchange;
1315                }
1316            }
1317        } else {
1318            matter.transport().accept_if(matter, |_, _, _| true).await
1319        }
1320    }
1321
1322    /// Get access to the pending RX message on this exchange, and consume it when the returned `RxMessage` instance is dropped.
1323    ///
1324    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1325    ///
1326    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1327    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1328    #[inline(always)]
1329    pub async fn recv(&mut self) -> Result<RxMessage<'_>, Error> {
1330        self.recv_fetch().await?;
1331
1332        self.rx.take().ok_or(ErrorCode::InvalidState.into())
1333    }
1334
1335    /// Get access to the pending RX message on this exchange, and consume it
1336    /// by copying the payload into the provided `WriteBuf` instance.
1337    ///
1338    /// A syntax sugar for calling ```self.recv().await?``` and then copying the payload.
1339    ///
1340    /// Returns the exchange message meta-data.
1341    ///
1342    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1343    ///
1344    /// If there is already a pending RX message, which was already fetched using `Exchange::recv_fetch` and that
1345    /// message is not cleared yet using `Exchange::rx_done` or via some of the `Exchange::send*` methods,
1346    /// the method will return that message.
1347    ///
1348    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1349    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1350    #[inline(always)]
1351    pub async fn recv_into(&mut self, wb: &mut WriteBuf<'_>) -> Result<MessageMeta, Error> {
1352        let rx = self.recv().await?;
1353
1354        wb.reset();
1355        wb.append(rx.payload())?;
1356
1357        Ok(rx.meta())
1358    }
1359
1360    /// Return a _reference_ to the pending RX message on this exchange.
1361    ///
1362    /// If there is no pending RX message, the method will wait indefinitely until one appears.
1363    ///
1364    /// Unlike `recv` which returns the actual message object which - when dropped - allows the transport to
1365    /// fetch the _next_ RX message for this or other exchanges, `recv_fetch` keeps the received message around,
1366    /// which is convenient when the message needs to be examined / processed by multiple layers of application code.
1367    ///
1368    /// Note however that this does not come for free - keeping the RX message around means that the transport cannot receive
1369    /// _other_ RX messages which blocks the whole transport layer, as the transport layer uses a single RX message buffer.
1370    ///
1371    /// Therefore, calling `recv_fetch` should be done with care and the message should be marked as processed (and thus dropped) -
1372    /// via `rx_done` as soon as possible, ideally without `await`-ing between `recv_fetch` and `rx_done`
1373    ///
1374    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1375    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1376    #[inline(always)]
1377    pub async fn recv_fetch(&mut self) -> Result<&RxMessage<'a>, Error> {
1378        if self.rx.is_none() {
1379            let rx = self.id.recv(self.matter).await?;
1380
1381            self.rx = Some(rx);
1382        }
1383
1384        self.rx()
1385    }
1386
1387    /// Returns the RX message which was already fetched using a previous call to `recv_fetch`.
1388    /// If there is no fetched RX message, the method will fail with `ErrorCode::InvalidState`.
1389    ///
1390    /// This method only exists as a slight optimization for the cases where the user is sure, that there is
1391    /// an RX message already fetched with `recv_fetch`, as - unlike `recv_fetch` - this method does not `await` and hence
1392    /// variables used after calling `rx` do not have to be stored in the generated future.
1393    ///
1394    /// But in general and putting optimizations aside, it is always safe to replace calls to `rx` with calls to `recv_fetch`.
1395    #[inline(always)]
1396    pub fn rx(&self) -> Result<&RxMessage<'a>, Error> {
1397        self.rx.as_ref().ok_or(ErrorCode::InvalidState.into())
1398    }
1399
1400    /// Clears the RX message which was already fetched using a previous call to `recv_fetch`.
1401    /// If there is no fetched RX message, the method will do nothing.
1402    #[inline(always)]
1403    pub fn rx_done(&mut self) -> Result<(), Error> {
1404        self.rx = None;
1405
1406        Ok(())
1407    }
1408
1409    /// Gets access to the TX buffer of the Matter stack for constructing a new TX message.
1410    /// If the TX buffer is not available, the method will wait indefinitely until it becomes available.
1411    ///
1412    /// NOTE:
1413    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1414    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1415    ///
1416    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1417    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1418    #[inline(always)]
1419    pub async fn init_send(&mut self) -> Result<TxMessage<'_>, Error> {
1420        self.rx = None;
1421
1422        self.id.init_send(self.matter).await
1423    }
1424
1425    /// Waits until the other side acknowledges the last message sent on this exchange,
1426    /// or until time for a re-transmission had come.
1427    ///
1428    /// If the last sent message was not using the MRP protocol, the method will return immediately with `TxOutcome::Done`.
1429    ///
1430    /// NOTE:
1431    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1432    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1433    ///
1434    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1435    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1436    #[inline(always)]
1437    pub async fn wait_tx(&mut self) -> Result<TxOutcome, Error> {
1438        self.rx = None;
1439
1440        self.id.wait_tx(self.matter).await
1441    }
1442
1443    /// Returns `true` if there is a pending message re-transmission.
1444    /// A re-transmission will be pending if the last sent message was using the MRP protocol, and
1445    /// an acknowledgement for the other side is still pending.
1446    ///
1447    /// NOTE:
1448    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1449    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1450    ///
1451    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1452    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1453    pub fn pending_retrans(&self) -> Result<bool, Error> {
1454        self.id.pending_retrans(self.matter)
1455    }
1456
1457    /// Returns `true` if there is a pending message acknowledgement.
1458    /// An acknowledgement be pending if the last received message was using the MRP protocol, and we have to acknowledge it.
1459    ///
1460    /// NOTE:
1461    /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1462    /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1463    ///
1464    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1465    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1466    pub fn pending_ack(&self) -> Result<bool, Error> {
1467        self.id.pending_ack(self.matter)
1468    }
1469
1470    /// Acknowledge the last message received on this exchange (by sending a `MrpStandaloneAck`).
1471    ///
1472    /// If the last message was already acknowledged
1473    /// (either by a previous call to this method, by piggy-backing on a reliable message, or by the Matter stack itself),
1474    /// this method does nothing.
1475    ///
1476    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1477    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1478    #[inline(always)]
1479    pub async fn acknowledge(&mut self) -> Result<(), Error> {
1480        if self.pending_ack()? {
1481            let tx = self.id.init_send(self.matter).await?;
1482
1483            if self.pending_ack()? {
1484                // Check whether we still need to send an ACK.
1485                // Necessary because we `.await` above, and while we are awaiting, the transport
1486                // might automatically send an ACK for us.
1487                // (That is, if the global RX transport buffer happens to be already empty and if the other peer re-sends the message.)
1488                tx.complete::<MessageMeta>(0, 0, sc::OpCode::MRPStandAloneAck.into())?;
1489            }
1490        }
1491
1492        Ok(())
1493    }
1494
1495    /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1496    /// in case the constructed message needs to be send reliably.
1497    ///
1498    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1499    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1500    pub fn sender(&mut self) -> Result<Sender<'_>, Error> {
1501        self.rx = None;
1502
1503        Sender::new(self)
1504    }
1505
1506    /// Owned-`Self` counterpart to [`Exchange::sender`].
1507    ///
1508    /// Consumes the exchange and returns an [`OwnedSender`] that
1509    /// owns it for the lifetime of the retransmit loop. Once the
1510    /// loop completes ([`OwnedSender::tx`] returns
1511    /// `Either::Right(exchange)`), the exchange is handed back to
1512    /// the caller.
1513    ///
1514    /// Used by consumers — such as the IM client — that take an
1515    /// `Exchange` by value and need to drive a send + receive cycle
1516    /// in sequence without giving up ownership.
1517    pub fn into_sender(mut self) -> Result<OwnedSender<'a>, Error> {
1518        self.rx = None;
1519
1520        OwnedSender::new(self)
1521    }
1522
1523    /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1524    /// in case the constructed message needs to be send reliably.
1525    ///
1526    /// The message is constructed by the provided closure, which is given a `WriteBuf` instance to write the message payload into.
1527    ///
1528    /// Note that the closure is expected to construct the exact same message when called multiple times.
1529    ///
1530    /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1531    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1532    pub async fn send_with<F>(&mut self, mut f: F) -> Result<(), Error>
1533    where
1534        F: FnMut(&Exchange, &mut WriteBuf) -> Result<Option<MessageMeta>, Error>,
1535    {
1536        let mut sender = self.sender()?;
1537
1538        while let Some(mut tx) = sender.tx().await? {
1539            let (exchange, payload) = tx.split();
1540
1541            let mut wb = WriteBuf::new(payload);
1542
1543            if let Some(meta) = f(exchange, &mut wb)? {
1544                let payload_start = wb.get_start();
1545                let payload_end = wb.get_tail();
1546                tx.complete(payload_start, payload_end, meta)?;
1547            } else {
1548                // Closure aborted sending
1549                break;
1550            }
1551        }
1552
1553        Ok(())
1554    }
1555
1556    /// Send the provided exchange meta-data and payload as part of this exchange.
1557    ///
1558    /// If the provided exchange meta-data indicates a reliable message, the message will be automatically re-transmitted until
1559    /// the other side acknowledges it.
1560    ///
1561    /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1562    /// (say, because of lack of resources or a hard networking error), the method will return an error.
1563    pub async fn send<M>(&mut self, meta: M, payload: &[u8]) -> Result<(), Error>
1564    where
1565        M: Into<MessageMeta>,
1566    {
1567        let meta = meta.into();
1568
1569        self.send_with(|_, wb| {
1570            wb.append(payload)?;
1571
1572            Ok(Some(meta))
1573        })
1574        .await
1575    }
1576
1577    pub(crate) fn accessor<M>(&self, metadata: M) -> Result<Accessor<'a>, Error>
1578    where
1579        M: Metadata,
1580    {
1581        self.id.accessor(self.matter, metadata.aux_acl_enabled())
1582    }
1583
1584    pub fn is_groupcast(&self) -> Result<bool, Error> {
1585        self.with_state(|state| {
1586            Ok(matches!(
1587                self.id().session(&mut state.sessions).get_session_mode(),
1588                SessionMode::Group { .. }
1589            ))
1590        })
1591    }
1592
1593    pub(crate) fn with_state<F, T>(&self, f: F) -> Result<T, Error>
1594    where
1595        F: FnOnce(&mut MatterState) -> Result<T, Error>,
1596    {
1597        self.id.with_state(self.matter, f)
1598    }
1599
1600    pub(crate) fn with_state_ex<F, T, E>(&self, f: F) -> Result<T, E>
1601    where
1602        F: FnOnce(&mut MatterState) -> Result<T, E>,
1603        E: From<Error>,
1604    {
1605        self.id.with_state_ex(self.matter, f)
1606    }
1607}
1608
1609impl Drop for Exchange<'_> {
1610    fn drop(&mut self) {
1611        let closed = self.with_state(|state| {
1612            let sess = self.id().session(&mut state.sessions);
1613            let exch_index = self.id.exchange_index();
1614
1615            let closed = sess.remove_exch(exch_index);
1616            if closed {
1617                // RX group sessions (unicast peer = the sender's address) are
1618                // ephemeral, one per received message — remove them with their
1619                // last exchange. TX group sessions (multicast peer) stay: the
1620                // just-queued outgoing packet still needs the session for
1621                // encoding, and subsequent sends to the same group reuse it
1622                // (`get_or_create_for_group_tx`); LRU eviction reclaims them.
1623                if matches!(sess.get_session_mode(), SessionMode::Group { .. })
1624                    && sess.exchanges.iter().all(Option::is_none)
1625                    && !sess.is_peer_multicast()
1626                {
1627                    // Group session with no remaining exchanges — remove it
1628                    state.sessions.remove(self.id.session_id());
1629                    self.matter.transport().notify_session_removed();
1630                }
1631
1632                Ok(true)
1633            } else {
1634                Ok(false)
1635            }
1636        });
1637
1638        if !matches!(closed, Ok(true)) {
1639            self.matter.transport().exchange_dropped.notify();
1640        }
1641    }
1642}
1643
1644impl Display for Exchange<'_> {
1645    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1646        write!(f, "{}", self.id)
1647    }
1648}
1649
1650#[cfg(test)]
1651mod tests {
1652    use super::*;
1653    use crate::crypto::test_only_crypto;
1654    use crate::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET};
1655    use crate::error::ErrorCode;
1656    use crate::transport::session::SessionMode;
1657    use crate::Matter;
1658    use futures_lite::future::block_on;
1659
1660    fn test_matter() -> Matter<'static> {
1661        Matter::new(&TEST_DEV_DET, TEST_DEV_COMM, &TEST_DEV_ATT, 0)
1662    }
1663
1664    fn fill_sessions(matter: &Matter<'_>, reserved: bool) {
1665        let dev_det = matter.dev_det();
1666        matter.with_state(|state| loop {
1667            if state
1668                .sessions
1669                .add(0, reserved, network::Address::new(), None, dev_det)
1670                .is_err()
1671            {
1672                break;
1673            }
1674        });
1675    }
1676
1677    #[test]
1678    fn test_initiate_plaintext_creates_initiator_exchange() {
1679        let matter = test_matter();
1680        let crypto = test_only_crypto();
1681        let peer = network::Address::new();
1682
1683        let exchange = block_on(Exchange::initiate_plaintext(&matter, &crypto, peer)).unwrap();
1684
1685        exchange
1686            .with_state(|state| {
1687                let sess = exchange.id().session(&mut state.sessions);
1688                let exch = exchange.id().exch(sess);
1689
1690                assert!(matches!(exch.role, Role::Initiator(_)));
1691                assert_eq!(sess.id, exchange.id().session_id());
1692                assert!(!sess.is_encrypted());
1693                assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1694
1695                Ok(())
1696            })
1697            .unwrap();
1698    }
1699
1700    #[test]
1701    fn test_initiate_plaintext_retries_after_eviction() {
1702        let matter = test_matter();
1703        let crypto = test_only_crypto();
1704        let peer = network::Address::new();
1705
1706        fill_sessions(&matter, false);
1707
1708        let exchange = block_on(Exchange::initiate_plaintext(&matter, &crypto, peer)).unwrap();
1709
1710        exchange
1711            .with_state(|state| {
1712                let sess = exchange.id().session(&mut state.sessions);
1713                let exch = exchange.id().exch(sess);
1714
1715                assert!(matches!(exch.role, Role::Initiator(_)));
1716                assert_eq!(sess.id, exchange.id().session_id());
1717                assert!(!sess.is_encrypted());
1718                assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1719
1720                Ok(())
1721            })
1722            .unwrap();
1723    }
1724
1725    #[test]
1726    fn test_initiate_plaintext_fails_when_no_session_can_be_evicted() {
1727        let matter = test_matter();
1728        let crypto = test_only_crypto();
1729        let peer = network::Address::new();
1730
1731        fill_sessions(&matter, true);
1732
1733        let result = block_on(Exchange::initiate_plaintext(&matter, &crypto, peer));
1734
1735        match result {
1736            Err(err) => assert!(matches!(err.code(), ErrorCode::NoSpaceSessions)),
1737            Ok(_) => panic!("expected NoSpaceSessions error"),
1738        }
1739    }
1740}