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}