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::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>(&self, matter: &'a Matter<'a>) -> Result<Accessor<'a>, Error> {
253 self.with_state(matter, |state| {
254 let sess = self.session(&mut state.sessions);
255
256 Ok(Accessor::for_session(sess, matter))
257 })
258 }
259
260 fn with_state<'a, F, T>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, Error>
261 where
262 F: FnOnce(&mut MatterState) -> Result<T, Error>,
263 {
264 self.with_state_ex(matter, f)
265 }
266
267 fn with_state_ex<'a, F, T, E>(&self, matter: &'a Matter<'a>, f: F) -> Result<T, E>
268 where
269 F: FnOnce(&mut MatterState) -> Result<T, E>,
270 E: From<Error>,
271 {
272 matter.with_state(|state| {
273 if state.sessions.get(self.session_id()).is_some() {
274 f(state)
275 } else {
276 warn!("Exchange {}: No session", self);
277 Err(Error::from(ErrorCode::NoSession).into())
278 }
279 })
280 }
281
282 async fn internal_wait_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
283 matter
284 .transport
285 .get_if_rx(|_| {
286 self.retrans_delay_ms(matter)
287 .map(|retrans| retrans.is_none())
288 .unwrap_or(true)
289 })
290 .await;
291
292 self.with_state(matter, |_| Ok(()))
293 }
294
295 fn retrans_delay_ms<'a>(&self, matter: &'a Matter<'a>) -> Result<Option<u64>, Error> {
296 self.with_state(matter, |state| {
297 let sess = self.session(&mut state.sessions);
298 let exch = self.exch(sess);
299
300 let mut jitter_rand = [0; 1];
301 // TODO XXX FIXME matter.rand()(&mut jitter_rand);
302 jitter_rand[0] = 100;
303
304 Ok(exch.retrans_delay_ms(jitter_rand[0]))
305 })
306 }
307
308 fn check_no_pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<(), Error> {
309 self.with_state(matter, |state| {
310 let sess = self.session(&mut state.sessions);
311 let exch = self.exch(sess);
312
313 if exch.mrp.is_retrans_pending() {
314 error!("Exchange {}: Retransmission pending", self.display(sess));
315 Err(ErrorCode::InvalidState)?;
316 }
317
318 Ok(())
319 })
320 }
321
322 fn pending_retrans<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
323 Ok(self.retrans_delay_ms(matter)?.is_some())
324 }
325
326 fn pending_ack<'a>(&self, matter: &'a Matter<'a>) -> Result<bool, Error> {
327 self.with_state(matter, |state| {
328 let sess = self.session(&mut state.sessions);
329 let exch = self.exch(sess);
330
331 Ok(exch.mrp.is_ack_pending())
332 })
333 }
334}
335
336impl Display for ExchangeId {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(f, "{}::{}", self.session_id(), self.exchange_index())
339 }
340}
341
342#[cfg(feature = "defmt")]
343impl defmt::Format for ExchangeId {
344 fn format(&self, f: defmt::Formatter<'_>) {
345 defmt::write!(f, "{}::{}", self.session_id(), self.exchange_index())
346 }
347}
348
349/// A display wrapper for `ExchangeId` which also displays
350/// the packet session ID, packet peer session ID and packet exchange ID.
351pub struct ExchangeIdDisplay<'a> {
352 id: &'a ExchangeId,
353 session: &'a Session,
354}
355
356impl Display for ExchangeIdDisplay<'_> {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 let state = self.session.exchanges[self.id.exchange_index()].as_ref();
359
360 if let Some(state) = state {
361 write!(
362 f,
363 "{} [SID:{:x},RSID:{:x},EID:{:x}]",
364 self.id,
365 self.session.get_local_sess_id(),
366 self.session.get_peer_sess_id(),
367 state.exch_id
368 )
369 } else {
370 // This should never happen, as that would mean we have invalid exchange index
371 // but let's not crash when displaying that
372 write!(f, "{}???", self.id)
373 }
374 }
375}
376
377#[cfg(feature = "defmt")]
378impl defmt::Format for ExchangeIdDisplay<'_> {
379 fn format(&self, f: defmt::Formatter<'_>) {
380 let state = self.session.exchanges[self.id.exchange_index()].as_ref();
381
382 if let Some(state) = state {
383 defmt::write!(
384 f,
385 "{} [SID:{:x},RSID:{:x},EID:{:x}]",
386 self.id,
387 self.session.get_local_sess_id(),
388 self.session.get_peer_sess_id(),
389 state.exch_id
390 )
391 } else {
392 // This should never happen, as that would mean we have invalid exchange index
393 // but let's not crash when displaying that
394 defmt::write!(f, "{}???", self.id)
395 }
396 }
397}
398
399#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
400#[cfg_attr(feature = "defmt", derive(defmt::Format))]
401pub(crate) enum InitiatorState {
402 #[default]
403 Owned,
404 Dropped,
405}
406
407#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
408#[cfg_attr(feature = "defmt", derive(defmt::Format))]
409pub(crate) enum ResponderState {
410 #[default]
411 AcceptPending,
412 Owned,
413 Dropped,
414}
415
416#[derive(Debug, PartialEq, Eq, Copy, Clone)]
417#[cfg_attr(feature = "defmt", derive(defmt::Format))]
418pub(crate) enum Role {
419 Initiator(InitiatorState),
420 Responder(ResponderState),
421}
422
423impl Role {
424 pub fn is_dropped_state(&self) -> bool {
425 match self {
426 Self::Initiator(state) => *state == InitiatorState::Dropped,
427 Self::Responder(state) => *state == ResponderState::Dropped,
428 }
429 }
430
431 pub fn set_dropped_state(&mut self) {
432 match self {
433 Self::Initiator(state) => *state = InitiatorState::Dropped,
434 Self::Responder(state) => *state = ResponderState::Dropped,
435 }
436 }
437}
438
439#[derive(Debug)]
440#[cfg_attr(feature = "defmt", derive(defmt::Format))]
441pub(crate) struct ExchangeState {
442 pub(crate) exch_id: u16,
443 pub(crate) role: Role,
444 pub(crate) mrp: ReliableMessage,
445}
446
447impl ExchangeState {
448 pub fn is_for_rx(&self, rx_proto: &ProtoHdr) -> bool {
449 self.exch_id == rx_proto.exch_id
450 && rx_proto.is_initiator() == matches!(self.role, Role::Responder(_))
451 }
452
453 pub fn post_recv(&mut self, rx_plain: &PlainHdr, rx_proto: &ProtoHdr) -> Result<(), Error> {
454 self.mrp.post_recv(rx_plain, rx_proto)?;
455
456 Ok(())
457 }
458
459 pub fn pre_send(
460 &mut self,
461 tx_plain: &PlainHdr,
462 tx_proto: &mut ProtoHdr,
463 session_active_interval_ms: Option<u32>,
464 session_idle_interval_ms: Option<u32>,
465 ) -> Result<(), Error> {
466 if matches!(self.role, Role::Initiator(_)) {
467 tx_proto.set_initiator();
468 } else {
469 tx_proto.unset_initiator();
470 }
471
472 tx_proto.exch_id = self.exch_id;
473
474 self.mrp.pre_send(
475 tx_plain,
476 tx_proto,
477 session_active_interval_ms,
478 session_idle_interval_ms,
479 )
480 }
481
482 pub fn retrans_delay_ms(&mut self, jitter_rand: u8) -> Option<u64> {
483 self.mrp
484 .retrans
485 .as_ref()
486 .map(|retrans| retrans.delay_ms(jitter_rand))
487 }
488}
489
490/// Meta-data when sending/receving messages via an Exchange.
491/// Basically, the protocol ID, the protocol opcode and whether the message should be set in a reliable manner.
492#[derive(Debug, Eq, PartialEq, Copy, Clone)]
493pub struct MessageMeta {
494 pub proto_id: u16,
495 pub proto_opcode: u8,
496 pub reliable: bool,
497}
498
499impl MessageMeta {
500 // Create a new message meta-data instance
501 pub const fn new(proto_id: u16, proto_opcode: u8, reliable: bool) -> Self {
502 Self {
503 proto_id,
504 proto_opcode,
505 reliable,
506 }
507 }
508
509 /// Try to cast the protocol opcode to a specific type
510 pub fn opcode<T: num::FromPrimitive>(&self) -> Result<T, Error> {
511 num::FromPrimitive::from_u8(self.proto_opcode).ok_or(ErrorCode::InvalidOpcode.into())
512 }
513
514 /// Check if the protocol opcode is equal to a specific value
515 pub fn check_opcode<T: num::FromPrimitive + PartialEq>(&self, opcode: T) -> Result<(), Error> {
516 if self.opcode::<T>()? == opcode {
517 Ok(())
518 } else {
519 Err(ErrorCode::Invalid.into())
520 }
521 }
522
523 /// Create an instance from a ProtoHdr instance
524 pub fn from(proto: &ProtoHdr) -> Self {
525 Self {
526 proto_id: proto.proto_id,
527 proto_opcode: proto.proto_opcode,
528 reliable: proto.is_reliable(),
529 }
530 }
531
532 /// Set the protocol ID and opcode into a ProtoHdr instance
533 pub fn set_into(&self, proto: &mut ProtoHdr) {
534 proto.proto_id = self.proto_id;
535 proto.proto_opcode = self.proto_opcode;
536 proto.set_vendor(None);
537
538 if self.reliable {
539 proto.set_reliable();
540 } else {
541 proto.unset_reliable();
542 }
543 }
544
545 pub fn reliable(self, reliable: bool) -> Self {
546 Self { reliable, ..self }
547 }
548
549 /// Utility method to check if the specific proto opcode in the instance is expecting a TLV payload.
550 pub(crate) fn is_tlv(&self) -> bool {
551 match self.proto_id {
552 PROTO_ID_SECURE_CHANNEL => self
553 .opcode::<sc::OpCode>()
554 .ok()
555 .map(|op| op.is_tlv())
556 .unwrap_or(false),
557 PROTO_ID_INTERACTION_MODEL => self
558 .opcode::<im::OpCode>()
559 .ok()
560 .map(|op| op.is_tlv())
561 .unwrap_or(false),
562 _ => false,
563 }
564 }
565
566 /// Utility method to check if the protocol is Secure Channel, and the opcode is a standalone ACK (`MrpStandaloneAck`).
567 pub(crate) fn is_standalone_ack(&self) -> bool {
568 self.proto_id == PROTO_ID_SECURE_CHANNEL
569 && self.proto_opcode == sc::OpCode::MRPStandAloneAck as u8
570 }
571
572 /// Utility method to check if the protocol is Secure Channel, and the opcode is Status.
573 pub(crate) fn is_sc_status(&self) -> bool {
574 self.proto_id == PROTO_ID_SECURE_CHANNEL
575 && self.proto_opcode == sc::OpCode::StatusReport as u8
576 }
577
578 /// Utility method to check if the protocol is Secure Channel, and the opcode is a new session request.
579 pub(crate) fn is_new_session(&self) -> bool {
580 self.proto_id == PROTO_ID_SECURE_CHANNEL
581 && (self.proto_opcode == sc::OpCode::PBKDFParamRequest as u8
582 || self.proto_opcode == sc::OpCode::CASESigma1 as u8)
583 }
584
585 /// Utility method to check if the meta-data indicates a new exchange
586 pub(crate) fn is_new_exchange(&self) -> bool {
587 // Don't create new exchanges for standalone ACKs and for SC status codes
588 !self.is_standalone_ack() && !self.is_sc_status()
589 }
590}
591
592impl Display for MessageMeta {
593 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594 match self.proto_id {
595 PROTO_ID_SECURE_CHANNEL => {
596 if let Ok(opcode) = self.opcode::<sc::OpCode>() {
597 write!(f, "SC::{:?}", opcode)
598 } else {
599 write!(f, "SC::{:02x}", self.proto_opcode)
600 }
601 }
602 PROTO_ID_INTERACTION_MODEL => {
603 if let Ok(opcode) = self.opcode::<im::OpCode>() {
604 write!(f, "IM::{:?}", opcode)
605 } else {
606 write!(f, "IM::{:02x}", self.proto_opcode)
607 }
608 }
609 PROTO_ID_BDX => {
610 if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
611 write!(f, "BDX::{:?}", opcode)
612 } else {
613 write!(f, "BDX::{:02x}", self.proto_opcode)
614 }
615 }
616 _ => write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
617 }
618 }
619}
620
621#[cfg(feature = "defmt")]
622impl defmt::Format for MessageMeta {
623 fn format(&self, f: defmt::Formatter<'_>) {
624 match self.proto_id {
625 PROTO_ID_SECURE_CHANNEL => {
626 if let Ok(opcode) = self.opcode::<sc::OpCode>() {
627 defmt::write!(f, "SC::{:?}", opcode)
628 } else {
629 defmt::write!(f, "SC::{:02x}", self.proto_opcode)
630 }
631 }
632 PROTO_ID_INTERACTION_MODEL => {
633 if let Ok(opcode) = self.opcode::<im::OpCode>() {
634 defmt::write!(f, "IM::{:?}", opcode)
635 } else {
636 defmt::write!(f, "IM::{:02x}", self.proto_opcode)
637 }
638 }
639 PROTO_ID_BDX => {
640 if let Ok(opcode) = self.opcode::<bdx::OpCode>() {
641 defmt::write!(f, "BDX::{:?}", opcode)
642 } else {
643 defmt::write!(f, "BDX::{:02x}", self.proto_opcode)
644 }
645 }
646 _ => defmt::write!(f, "{:02x}::{:02x}", self.proto_id, self.proto_opcode),
647 }
648 }
649}
650
651/// An RX message pending on an `Exchange` instance.
652pub struct RxMessage<'a>(PacketAccess<'a, MAX_RX_BUF_SIZE>);
653
654impl RxMessage<'_> {
655 /// Get the meta-data of the pending message
656 pub fn meta(&self) -> MessageMeta {
657 MessageMeta::from(&self.0.header.proto)
658 }
659
660 /// Get the payload of the pending message
661 pub fn payload(&self) -> &[u8] {
662 &self.0.buf[self.0.payload_start..]
663 }
664}
665
666/// Accessor to the TX message buffer of the underlying Matter transport stack.
667///
668/// This is used to construct a new TX message to be sent on an `Exchange` instance.
669///
670/// NOTE: It is strongly advised to use the `TxMessage` accessor in combination with the `Sender` utility,
671/// which takes care of all message retransmission logic. Alternatively, one can use the
672/// `Exchange::send` or `Exchange::send_with` which also take care of re-transmissions.
673pub struct TxMessage<'a> {
674 exchange_id: ExchangeId,
675 matter: &'a Matter<'a>,
676 packet: PacketAccess<'a, MAX_TX_BUF_SIZE>,
677}
678
679impl TxMessage<'_> {
680 /// Get a reference to the payload buffer of the TX message being built
681 pub fn payload(&mut self) -> &mut [u8] {
682 &mut self.packet.buf[PacketHdr::HDR_RESERVE..MAX_TX_BUF_SIZE - PacketHdr::TAIL_RESERVE]
683 }
684
685 /// Complete and send a TX message by providing:
686 /// - The payload size that was filled-in by user code in the payload buffer returned by `TxMessage::payload`
687 /// - The TX message meta-data
688 pub fn complete<M>(
689 mut self,
690 payload_start: usize,
691 payload_end: usize,
692 meta: M,
693 ) -> Result<(), Error>
694 where
695 M: Into<MessageMeta>,
696 {
697 if payload_start > payload_end
698 || payload_end - payload_start
699 > MAX_TX_BUF_SIZE - PacketHdr::HDR_RESERVE - PacketHdr::TAIL_RESERVE
700 {
701 Err(ErrorCode::Invalid)?;
702 }
703
704 let meta: MessageMeta = meta.into();
705
706 self.packet.header.reset();
707
708 meta.set_into(&mut self.packet.header.proto);
709
710 self.matter.with_state(|state| {
711 let session = state
712 .sessions
713 .get(self.exchange_id.session_id())
714 .ok_or(ErrorCode::NoSession)?;
715
716 // The session's `peer_active_interval_ms` / `peer_idle_interval_ms`
717 // are seeded from our own `BasicInfoConfig` at session-creation
718 // time and overwritten once Sigma1 / Sigma2 / PBKDFParamRequest /
719 // PBKDFParamResponse delivers a real peer value, so MRP
720 // retransmission backoff to this peer reflects whichever is
721 // more accurate (Matter Core spec).
722 let (peer, retransmission) = session.pre_send(
723 Some(self.exchange_id.exchange_index()),
724 &mut self.packet.header,
725 Some(session.get_peer_active_interval_ms()),
726 Some(session.get_peer_idle_interval_ms()),
727 )?;
728
729 self.packet.peer = peer;
730 self.packet.payload_start = PacketHdr::HDR_RESERVE + payload_start;
731 self.packet
732 .buf
733 .truncate(PacketHdr::HDR_RESERVE + payload_end);
734 self.packet.tx_info.payload_state = TxPayloadState::NotEncoded {
735 session_id: session.id,
736 };
737 self.packet.tx_info.retransmission = retransmission;
738 self.packet.clear_on_drop(false);
739
740 Ok(())
741 })
742 }
743}
744
745/// Outcome from calling `Exchange::wait_tx`
746#[derive(Copy, Clone, Eq, PartialEq, Debug)]
747#[cfg_attr(feature = "defmt", derive(defmt::Format))]
748pub enum TxOutcome {
749 /// The other side has acknowledged the last message or the last message was not using the MRP protocol
750 /// Stop re-sending.
751 Done,
752 /// Need to re-send the last message.
753 Retransmit,
754}
755
756impl TxOutcome {
757 /// Check if the outcome is `Done`
758 pub const fn is_done(&self) -> bool {
759 matches!(self, Self::Done)
760 }
761}
762
763pub struct SenderTx<'a, 'b> {
764 sender: &'b mut Sender<'a>,
765 message: TxMessage<'a>,
766}
767
768impl SenderTx<'_, '_> {
769 pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
770 (self.sender.exchange, self.message.payload())
771 }
772
773 pub fn payload(&mut self) -> &mut [u8] {
774 self.message.payload()
775 }
776
777 pub fn complete(
778 self,
779 payload_start: usize,
780 payload_end: usize,
781 meta: MessageMeta,
782 ) -> Result<(), Error> {
783 self.message.complete(payload_start, payload_end, meta)?;
784
785 self.sender.initial = false;
786
787 Ok(())
788 }
789}
790
791/// Utility struct for sending a message with potential retransmissions.
792pub struct Sender<'a> {
793 exchange: &'a Exchange<'a>,
794 initial: bool,
795 complete: bool,
796}
797
798impl<'a> Sender<'a> {
799 fn new(exchange: &'a Exchange<'a>) -> Result<Self, Error> {
800 exchange.id.check_no_pending_retrans(exchange.matter)?;
801
802 Ok(Self {
803 exchange,
804 initial: true,
805 complete: false,
806 })
807 }
808
809 /// Get the TX buffer of the underlying Matter stack for (re)constructing a new TX message,
810 /// waiting for the TX buffer to become available, if it is not.
811 ///
812 /// If the method returns `None`, it means that the message was already acknowledged by the other side,
813 /// or that the message does not need acknowledgement and re-transmissions.
814 ///
815 /// When called for the first time, the method will always return a `Some` value, as the message has not been sent even once yet.
816 /// Once the method returns `None`, it will always return `None` on subsequent calls, as the message has been acknowledged by the other side.
817 ///
818 /// Example:
819 /// ```ignore
820 /// let exchange = ...;
821 ///
822 /// let sender = exchange.sender()?;
823 ///
824 /// while let Some(mut tx) = sender.tx().await? {
825 /// let (exchange, payload) = tx.split()?;
826 ///
827 /// // Write the message payload in the `payload` `&mut [u8]` slice
828 /// // On every iteration of the loop, write the _same_ payload (as message re-transmission is idempotent w.r.t. the message)
829 /// ...
830 ///
831 /// // Complete the payload by providing `MessageMeta`, payload start and payload end
832 /// // On every iteration of the loop, proide the _same_ meta-data (as message re-transmission is idempotent w.r.t. the message)
833 /// let meta = ...;
834 /// let payload_start = ...;
835 /// let payload_end = ...;
836 ///
837 /// tx.complete(payload_start, payload_end, meta)?;
838 /// }
839 /// ```
840 pub async fn tx(&mut self) -> Result<Option<SenderTx<'a, '_>>, Error> {
841 trace!(
842 "Sender::tx called, initial={}, complete={}",
843 self.initial,
844 self.complete
845 );
846 if self.complete {
847 trace!("Sender::tx - already complete, returning None");
848 return Ok(None);
849 }
850
851 if !self.initial {
852 trace!("Sender::tx - not initial, calling wait_tx");
853 let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
854 trace!("Sender::tx - wait_tx returned {:?}", outcome);
855 if outcome.is_done() {
856 // No need to re-transmit
857 self.complete = true;
858 trace!("Sender::tx - ACK received, returning None");
859 return Ok(None);
860 }
861 trace!("Sender::tx - need to retransmit");
862 }
863
864 let id = self.exchange.id;
865 let matter = self.exchange.matter;
866
867 trace!("Sender::tx - calling init_send");
868 let tx = id.init_send(matter).await?;
869 trace!("Sender::tx - init_send returned");
870
871 if self.initial || id.pending_retrans(matter)? {
872 trace!("Sender::tx - returning Some(SenderTx)");
873 Ok(Some(SenderTx {
874 sender: self,
875 message: tx,
876 }))
877 } else {
878 self.complete = true;
879 trace!("Sender::tx - no pending retrans, returning None");
880 Ok(None)
881 }
882 }
883}
884
885/// Owned-`Self` counterpart to [`SenderTx`].
886///
887/// Holds the [`Exchange`] by value (rather than via a `&mut` through
888/// the parent [`Sender`]), so consumers that consume their exchange
889/// can drive the retransmit loop without a self-referential struct.
890/// Returned by [`OwnedSender::tx`] when the framework needs the
891/// message bytes (re-)built into a fresh TX slot.
892///
893/// Pair with [`SenderTx`] for the borrowed-self mirror.
894pub struct OwnedSenderTx<'a> {
895 exchange: Exchange<'a>,
896 message: TxMessage<'a>,
897}
898
899impl<'a> OwnedSenderTx<'a> {
900 /// Get a `(borrowed-exchange, payload-slice)` pair for inspecting
901 /// the exchange and writing the message bytes.
902 pub fn split(&mut self) -> (&Exchange<'_>, &mut [u8]) {
903 (&self.exchange, self.message.payload())
904 }
905
906 /// Get a mutable reference to the payload slice the bytes go into.
907 pub fn payload(&mut self) -> &mut [u8] {
908 self.message.payload()
909 }
910
911 /// Commit the bytes currently in the slot. The framework dispatches
912 /// the wire-level send asynchronously; this call returns the
913 /// [`OwnedSender`] so the caller can ask for the next event
914 /// (retransmit, ACK).
915 pub fn complete(
916 self,
917 payload_start: usize,
918 payload_end: usize,
919 meta: MessageMeta,
920 ) -> Result<OwnedSender<'a>, Error> {
921 self.message.complete(payload_start, payload_end, meta)?;
922
923 Ok(OwnedSender {
924 exchange: self.exchange,
925 initial: false,
926 complete: false,
927 })
928 }
929}
930
931/// Owned-`Self` counterpart to [`Sender`].
932///
933/// Consumes the [`Exchange`] on construction (via
934/// [`Exchange::into_sender`]) and returns it back to the caller when
935/// the retransmit loop is done (i.e. when [`OwnedSender::tx`]
936/// completes with `Either::Right`, meaning the message has been
937/// ACK-ed by the peer or did not require ACK).
938///
939/// Use cases:
940/// - The Interaction Model client (`im::client::ImClient`) consumes
941/// an exchange to drive a single IM transaction end-to-end, and
942/// wants to walk the retransmit loop while still owning the
943/// `Exchange` for the subsequent receive phase. The owned-`Self`
944/// shape avoids a self-referential struct that would otherwise hold
945/// both an owned `Exchange` and a `&mut`-Sender on it.
946///
947/// User loop pattern (mirrors [`Sender`]):
948///
949/// ```ignore
950/// let mut sender = exchange.into_sender()?;
951/// let exchange = loop {
952/// match sender.tx().await? {
953/// Either::Left(mut slot) => {
954/// let (_, payload) = slot.split();
955/// // Write the message bytes (same every iteration —
956/// // retransmission is idempotent w.r.t. the message).
957/// let (start, end, meta) = /* … */;
958/// sender = slot.complete(start, end, meta)?;
959/// }
960/// Either::Right(exchange) => break exchange,
961/// }
962/// };
963/// // `exchange` is yours again.
964/// ```
965pub struct OwnedSender<'a> {
966 exchange: Exchange<'a>,
967 initial: bool,
968 complete: bool,
969}
970
971impl<'a> OwnedSender<'a> {
972 fn new(exchange: Exchange<'a>) -> Result<Self, Error> {
973 exchange.id.check_no_pending_retrans(exchange.matter)?;
974
975 Ok(Self {
976 exchange,
977 initial: true,
978 complete: false,
979 })
980 }
981
982 /// Next event from the framework. Owned-`Self` mirror of
983 /// [`Sender::tx`]:
984 /// - `Either::Left(slot)` — a TX slot is ready; (re-)build the
985 /// message bytes into it and call `slot.complete(...)` to get
986 /// the `OwnedSender` back for the next iteration.
987 /// - `Either::Right(exchange)` — the message has been ACK-ed (or
988 /// did not need an ACK); the loop is done and the exchange is
989 /// returned to the caller.
990 pub async fn tx(mut self) -> Result<EitherIo<OwnedSenderTx<'a>, Exchange<'a>>, Error> {
991 trace!(
992 "OwnedSender::tx called, initial={}, complete={}",
993 self.initial,
994 self.complete
995 );
996 if self.complete {
997 trace!("OwnedSender::tx - already complete, returning exchange");
998 return Ok(EitherIo::Right(self.exchange));
999 }
1000
1001 if !self.initial {
1002 trace!("OwnedSender::tx - not initial, calling wait_tx");
1003 let outcome = self.exchange.id.wait_tx(self.exchange.matter).await?;
1004 trace!("OwnedSender::tx - wait_tx returned {:?}", outcome);
1005 if outcome.is_done() {
1006 // No need to re-transmit
1007 self.complete = true;
1008 trace!("OwnedSender::tx - ACK received, returning exchange");
1009 return Ok(EitherIo::Right(self.exchange));
1010 }
1011 trace!("OwnedSender::tx - need to retransmit");
1012 }
1013
1014 let id = self.exchange.id;
1015 let matter = self.exchange.matter;
1016
1017 trace!("OwnedSender::tx - calling init_send");
1018 let tx = id.init_send(matter).await?;
1019 trace!("OwnedSender::tx - init_send returned");
1020
1021 if self.initial || id.pending_retrans(matter)? {
1022 trace!("OwnedSender::tx - returning Left(OwnedSenderTx)");
1023 Ok(EitherIo::Left(OwnedSenderTx {
1024 exchange: self.exchange,
1025 message: tx,
1026 }))
1027 } else {
1028 trace!("OwnedSender::tx - no pending retrans, returning exchange");
1029 Ok(EitherIo::Right(self.exchange))
1030 }
1031 }
1032}
1033
1034/// An exchange within a Matter stack, representing a session and an exchange within that session.
1035///
1036/// This is the main API for sending and receiving messages within the Matter stack.
1037/// Used by upper-level layers like the Secure Channel and Interaction Model.
1038pub struct Exchange<'a> {
1039 id: ExchangeId,
1040 matter: &'a Matter<'a>,
1041 rx: Option<RxMessage<'a>>,
1042}
1043
1044impl<'a> Exchange<'a> {
1045 pub(crate) const fn new(id: ExchangeId, matter: &'a Matter<'a>) -> Self {
1046 Self {
1047 id,
1048 matter,
1049 rx: None,
1050 }
1051 }
1052
1053 /// Get the Id of the exchange
1054 pub fn id(&self) -> ExchangeId {
1055 self.id
1056 }
1057
1058 /// Get the Matter stack instance associated with this exchange
1059 pub fn matter(&self) -> &'a Matter<'a> {
1060 self.matter
1061 }
1062
1063 /// Open an exchange over a CASE session to an already-commissioned node.
1064 ///
1065 /// If a CASE session for `(fabric_idx, peer_node_id)` already exists, an
1066 /// exchange is opened on it directly via [`Exchange::initiate_for_session`]
1067 /// (the common case - session and peer address reused, no mDNS). Otherwise
1068 /// the peer's operational address is resolved over mDNS and a fresh CASE
1069 /// session is established (driving [`crate::sc::case::CaseInitiator`]) before
1070 /// the exchange is opened on it; the peer's MRP/session parameters advertised
1071 /// in the mDNS TXT records seed the session.
1072 ///
1073 /// Establishing requires a running mDNS responder (e.g.
1074 /// `BuiltinMdns::run`) to service the resolve; without one the
1075 /// resolve times out and this returns [`ErrorCode::NotFound`].
1076 #[inline(always)]
1077 pub async fn initiate<C: Crypto>(
1078 matter: &'a Matter<'a>,
1079 crypto: C,
1080 fabric_idx: NonZeroU8,
1081 peer_node_id: NodeId,
1082 ) -> Result<Self, Error> {
1083 matter
1084 .transport
1085 .initiate(matter, crypto, fabric_idx, peer_node_id)
1086 .await
1087 }
1088
1089 /// Open an exchange over a **PASE** session to a not-yet-commissioned node
1090 /// at the given peer address (use-case 2).
1091 ///
1092 /// If a PASE session **to that peer** already exists, an exchange is opened on
1093 /// it directly via [`Exchange::initiate_for_session`]. Otherwise a new PASE
1094 /// session is established: a plaintext session is opened and the PASE protocol
1095 /// ([`crate::sc::pase::PaseInitiator`]) is run with `passcode`, then an
1096 /// exchange is opened on the resulting PASE session.
1097 ///
1098 /// Reuse is keyed by peer address (not a single global PASE session), so a
1099 /// commissioner can drive several concurrent commissionings.
1100 ///
1101 /// The peer's MRP/session parameters are negotiated by PASE itself
1102 /// (PBKDFParamRequest/Response).
1103 ///
1104 /// Discovery of the address is out of scope here (it may come from mDNS - see
1105 /// [`crate::transport::Transport::browse_commissionable`] - or from a BLE/BTP
1106 /// advertisement, etc.); this method is transport-agnostic and takes the
1107 /// already-known address.
1108 #[inline(always)]
1109 pub async fn initiate_pase<C: Crypto>(
1110 matter: &'a Matter<'a>,
1111 crypto: C,
1112 peer_addr: network::Address,
1113 passcode: u32,
1114 ) -> Result<Self, Error> {
1115 matter
1116 .transport
1117 .initiate_pase(matter, crypto, peer_addr, passcode)
1118 .await
1119 }
1120
1121 /// Create a new initiator exchange on the provided Matter stack for the provided session ID.
1122 #[inline(always)]
1123 pub fn initiate_for_session(matter: &'a Matter<'a>, session_id: u32) -> Result<Self, Error> {
1124 matter.transport().initiate_for_session(matter, session_id)
1125 }
1126
1127 /// Create a new initiator exchange on a new unsecured (plain-text) session to
1128 /// the given peer address.
1129 ///
1130 /// Low-level primitive below the three high-level entry points
1131 /// ([`Exchange::initiate`], [`Exchange::initiate_pase`],
1132 /// [`Exchange::initiate_for_session`]): the returned exchange carries the
1133 /// first handshake message of PASE ([`crate::sc::pase::PaseInitiator`]) or
1134 /// CASE ([`crate::sc::case::CaseInitiator`]). Use this only when driving a handshake protocol
1135 /// directly; otherwise prefer `initiate_pase` (which runs PASE for you) or
1136 /// `initiate` (CASE). If there is no space for a new session, an existing
1137 /// session is evicted and the operation retried.
1138 #[inline(always)]
1139 pub async fn initiate_unsecured<C: Crypto>(
1140 matter: &'a Matter<'a>,
1141 crypto: C,
1142 peer_addr: network::Address,
1143 ) -> Result<Self, Error> {
1144 matter
1145 .transport
1146 .initiate_plaintext(matter, crypto, peer_addr)
1147 .await
1148 }
1149
1150 /// Accepts a new responder exchange pending on the provided Matter stack.
1151 ///
1152 /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1153 #[inline(always)]
1154 pub async fn accept(matter: &'a Matter<'a>) -> Result<Self, Error> {
1155 Self::accept_after(matter, 0).await
1156 }
1157
1158 /// Accepts a new responder exchange pending on the provided Matter stack, but only if the
1159 /// pending exchange was pending for longer than `received_timeout_ms`.
1160 ///
1161 /// If there is no new pending responder exchange, the method will wait indefinitely until one appears.
1162 pub async fn accept_after(
1163 matter: &'a Matter<'a>,
1164 received_timeout_ms: u32,
1165 ) -> Result<Self, Error> {
1166 if received_timeout_ms > 0 {
1167 loop {
1168 let mut accept = pin!(matter.transport().accept_if(matter, |_, exch, _| {
1169 exch.mrp.has_rx_timed_out(received_timeout_ms as _)
1170 }));
1171
1172 let mut timer = pin!(Timer::after(embassy_time::Duration::from_millis(
1173 received_timeout_ms as u64
1174 )));
1175
1176 if let Either::First(exchange) = select(&mut accept, &mut timer).await {
1177 break exchange;
1178 }
1179 }
1180 } else {
1181 matter.transport().accept_if(matter, |_, _, _| true).await
1182 }
1183 }
1184
1185 /// Get access to the pending RX message on this exchange, and consume it when the returned `RxMessage` instance is dropped.
1186 ///
1187 /// If there is no pending RX message, the method will wait indefinitely until one appears.
1188 ///
1189 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1190 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1191 #[inline(always)]
1192 pub async fn recv(&mut self) -> Result<RxMessage<'_>, Error> {
1193 self.recv_fetch().await?;
1194
1195 self.rx.take().ok_or(ErrorCode::InvalidState.into())
1196 }
1197
1198 /// Get access to the pending RX message on this exchange, and consume it
1199 /// by copying the payload into the provided `WriteBuf` instance.
1200 ///
1201 /// A syntax sugar for calling ```self.recv().await?``` and then copying the payload.
1202 ///
1203 /// Returns the exchange message meta-data.
1204 ///
1205 /// If there is no pending RX message, the method will wait indefinitely until one appears.
1206 ///
1207 /// If there is already a pending RX message, which was already fetched using `Exchange::recv_fetch` and that
1208 /// message is not cleared yet using `Exchange::rx_done` or via some of the `Exchange::send*` methods,
1209 /// the method will return that message.
1210 ///
1211 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1212 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1213 #[inline(always)]
1214 pub async fn recv_into(&mut self, wb: &mut WriteBuf<'_>) -> Result<MessageMeta, Error> {
1215 let rx = self.recv().await?;
1216
1217 wb.reset();
1218 wb.append(rx.payload())?;
1219
1220 Ok(rx.meta())
1221 }
1222
1223 /// Return a _reference_ to the pending RX message on this exchange.
1224 ///
1225 /// If there is no pending RX message, the method will wait indefinitely until one appears.
1226 ///
1227 /// Unlike `recv` which returns the actual message object which - when dropped - allows the transport to
1228 /// fetch the _next_ RX message for this or other exchanges, `recv_fetch` keeps the received message around,
1229 /// which is convenient when the message needs to be examined / processed by multiple layers of application code.
1230 ///
1231 /// Note however that this does not come for free - keeping the RX message around means that the transport cannot receive
1232 /// _other_ RX messages which blocks the whole transport layer, as the transport layer uses a single RX message buffer.
1233 ///
1234 /// Therefore, calling `recv_fetch` should be done with care and the message should be marked as processed (and thus dropped) -
1235 /// via `rx_done` as soon as possible, ideally without `await`-ing between `recv_fetch` and `rx_done`
1236 ///
1237 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1238 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1239 #[inline(always)]
1240 pub async fn recv_fetch(&mut self) -> Result<&RxMessage<'a>, Error> {
1241 if self.rx.is_none() {
1242 let rx = self.id.recv(self.matter).await?;
1243
1244 self.rx = Some(rx);
1245 }
1246
1247 self.rx()
1248 }
1249
1250 /// Returns the RX message which was already fetched using a previous call to `recv_fetch`.
1251 /// If there is no fetched RX message, the method will fail with `ErrorCode::InvalidState`.
1252 ///
1253 /// This method only exists as a slight optimization for the cases where the user is sure, that there is
1254 /// an RX message already fetched with `recv_fetch`, as - unlike `recv_fetch` - this method does not `await` and hence
1255 /// variables used after calling `rx` do not have to be stored in the generated future.
1256 ///
1257 /// But in general and putting optimizations aside, it is always safe to replace calls to `rx` with calls to `recv_fetch`.
1258 #[inline(always)]
1259 pub fn rx(&self) -> Result<&RxMessage<'a>, Error> {
1260 self.rx.as_ref().ok_or(ErrorCode::InvalidState.into())
1261 }
1262
1263 /// Clears the RX message which was already fetched using a previous call to `recv_fetch`.
1264 /// If there is no fetched RX message, the method will do nothing.
1265 #[inline(always)]
1266 pub fn rx_done(&mut self) -> Result<(), Error> {
1267 self.rx = None;
1268
1269 Ok(())
1270 }
1271
1272 /// Gets access to the TX buffer of the Matter stack for constructing a new TX message.
1273 /// If the TX buffer is not available, the method will wait indefinitely until it becomes available.
1274 ///
1275 /// NOTE:
1276 /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1277 /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1278 ///
1279 /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1280 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1281 #[inline(always)]
1282 pub async fn init_send(&mut self) -> Result<TxMessage<'_>, Error> {
1283 self.rx = None;
1284
1285 self.id.init_send(self.matter).await
1286 }
1287
1288 /// Waits until the other side acknowledges the last message sent on this exchange,
1289 /// or until time for a re-transmission had come.
1290 ///
1291 /// If the last sent message was not using the MRP protocol, the method will return immediately with `TxOutcome::Done`.
1292 ///
1293 /// NOTE:
1294 /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1295 /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1296 ///
1297 /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1298 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1299 #[inline(always)]
1300 pub async fn wait_tx(&mut self) -> Result<TxOutcome, Error> {
1301 self.rx = None;
1302
1303 self.id.wait_tx(self.matter).await
1304 }
1305
1306 /// Returns `true` if there is a pending message re-transmission.
1307 /// A re-transmission will be pending if the last sent message was using the MRP protocol, and
1308 /// an acknowledgement for the other side is still pending.
1309 ///
1310 /// NOTE:
1311 /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1312 /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1313 ///
1314 /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1315 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1316 pub fn pending_retrans(&self) -> Result<bool, Error> {
1317 self.id.pending_retrans(self.matter)
1318 }
1319
1320 /// Returns `true` if there is a pending message acknowledgement.
1321 /// An acknowledgement be pending if the last received message was using the MRP protocol, and we have to acknowledge it.
1322 ///
1323 /// NOTE:
1324 /// This is a low-level method that leaves the re-transmission logic on the shoulders of the user.
1325 /// Therefore, prefer using `Exchange::sender`, `Exchange::send` or `Exchange::send_with` instead.
1326 ///
1327 /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1328 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1329 pub fn pending_ack(&self) -> Result<bool, Error> {
1330 self.id.pending_ack(self.matter)
1331 }
1332
1333 /// Acknowledge the last message received on this exchange (by sending a `MrpStandaloneAck`).
1334 ///
1335 /// If the last message was already acknowledged
1336 /// (either by a previous call to this method, by piggy-backing on a reliable message, or by the Matter stack itself),
1337 /// this method does nothing.
1338 ///
1339 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1340 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1341 #[inline(always)]
1342 pub async fn acknowledge(&mut self) -> Result<(), Error> {
1343 if self.pending_ack()? {
1344 let tx = self.id.init_send(self.matter).await?;
1345
1346 if self.pending_ack()? {
1347 // Check whether we still need to send an ACK.
1348 // Necessary because we `.await` above, and while we are awaiting, the transport
1349 // might automatically send an ACK for us.
1350 // (That is, if the global RX transport buffer happens to be already empty and if the other peer re-sends the message.)
1351 tx.complete::<MessageMeta>(0, 0, sc::OpCode::MRPStandAloneAck.into())?;
1352 }
1353 }
1354
1355 Ok(())
1356 }
1357
1358 /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1359 /// in case the constructed message needs to be send reliably.
1360 ///
1361 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1362 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1363 pub fn sender(&mut self) -> Result<Sender<'_>, Error> {
1364 self.rx = None;
1365
1366 Sender::new(self)
1367 }
1368
1369 /// Owned-`Self` counterpart to [`Exchange::sender`].
1370 ///
1371 /// Consumes the exchange and returns an [`OwnedSender`] that
1372 /// owns it for the lifetime of the retransmit loop. Once the
1373 /// loop completes ([`OwnedSender::tx`] returns
1374 /// `Either::Right(exchange)`), the exchange is handed back to
1375 /// the caller.
1376 ///
1377 /// Used by consumers — such as the IM client — that take an
1378 /// `Exchange` by value and need to drive a send + receive cycle
1379 /// in sequence without giving up ownership.
1380 pub fn into_sender(mut self) -> Result<OwnedSender<'a>, Error> {
1381 self.rx = None;
1382
1383 OwnedSender::new(self)
1384 }
1385
1386 /// Utility for sending a message on this exchange that automatically handles all re-transmission logic
1387 /// in case the constructed message needs to be send reliably.
1388 ///
1389 /// The message is constructed by the provided closure, which is given a `WriteBuf` instance to write the message payload into.
1390 ///
1391 /// Note that the closure is expected to construct the exact same message when called multiple times.
1392 ///
1393 /// Note also that if the uderlying session or exchange tracked by the Matter stack is dropped
1394 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1395 pub async fn send_with<F>(&mut self, mut f: F) -> Result<(), Error>
1396 where
1397 F: FnMut(&Exchange, &mut WriteBuf) -> Result<Option<MessageMeta>, Error>,
1398 {
1399 let mut sender = self.sender()?;
1400
1401 while let Some(mut tx) = sender.tx().await? {
1402 let (exchange, payload) = tx.split();
1403
1404 let mut wb = WriteBuf::new(payload);
1405
1406 if let Some(meta) = f(exchange, &mut wb)? {
1407 let payload_start = wb.get_start();
1408 let payload_end = wb.get_tail();
1409 tx.complete(payload_start, payload_end, meta)?;
1410 } else {
1411 // Closure aborted sending
1412 break;
1413 }
1414 }
1415
1416 Ok(())
1417 }
1418
1419 /// Send the provided exchange meta-data and payload as part of this exchange.
1420 ///
1421 /// If the provided exchange meta-data indicates a reliable message, the message will be automatically re-transmitted until
1422 /// the other side acknowledges it.
1423 ///
1424 /// Note that if the uderlying session or exchange tracked by the Matter stack is dropped
1425 /// (say, because of lack of resources or a hard networking error), the method will return an error.
1426 pub async fn send<M>(&mut self, meta: M, payload: &[u8]) -> Result<(), Error>
1427 where
1428 M: Into<MessageMeta>,
1429 {
1430 let meta = meta.into();
1431
1432 self.send_with(|_, wb| {
1433 wb.append(payload)?;
1434
1435 Ok(Some(meta))
1436 })
1437 .await
1438 }
1439
1440 pub(crate) fn accessor(&self) -> Result<Accessor<'a>, Error> {
1441 self.id.accessor(self.matter)
1442 }
1443
1444 pub fn is_groupcast(&self) -> Result<bool, Error> {
1445 self.with_state(|state| {
1446 Ok(matches!(
1447 self.id().session(&mut state.sessions).get_session_mode(),
1448 SessionMode::Group { .. }
1449 ))
1450 })
1451 }
1452
1453 pub(crate) fn with_state<F, T>(&self, f: F) -> Result<T, Error>
1454 where
1455 F: FnOnce(&mut MatterState) -> Result<T, Error>,
1456 {
1457 self.id.with_state(self.matter, f)
1458 }
1459
1460 pub(crate) fn with_state_ex<F, T, E>(&self, f: F) -> Result<T, E>
1461 where
1462 F: FnOnce(&mut MatterState) -> Result<T, E>,
1463 E: From<Error>,
1464 {
1465 self.id.with_state_ex(self.matter, f)
1466 }
1467}
1468
1469impl Drop for Exchange<'_> {
1470 fn drop(&mut self) {
1471 let closed = self.with_state(|state| {
1472 let sess = self.id().session(&mut state.sessions);
1473 let exch_index = self.id.exchange_index();
1474
1475 let closed = sess.remove_exch(exch_index);
1476 if closed {
1477 if matches!(sess.get_session_mode(), SessionMode::Group { .. })
1478 && sess.exchanges.iter().all(Option::is_none)
1479 {
1480 // Group session with no remaining exchanges — remove it
1481 state.sessions.remove(self.id.session_id());
1482 self.matter.transport().notify_session_removed();
1483 }
1484
1485 Ok(true)
1486 } else {
1487 Ok(false)
1488 }
1489 });
1490
1491 if !matches!(closed, Ok(true)) {
1492 self.matter.transport().exchange_dropped.notify();
1493 }
1494 }
1495}
1496
1497impl Display for Exchange<'_> {
1498 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1499 write!(f, "{}", self.id)
1500 }
1501}
1502
1503#[cfg(test)]
1504mod tests {
1505 use super::*;
1506 use crate::crypto::test_only_crypto;
1507 use crate::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET};
1508 use crate::error::ErrorCode;
1509 use crate::transport::session::SessionMode;
1510 use crate::Matter;
1511 use futures_lite::future::block_on;
1512
1513 fn test_matter() -> Matter<'static> {
1514 Matter::new(&TEST_DEV_DET, TEST_DEV_COMM, &TEST_DEV_ATT, 0)
1515 }
1516
1517 fn fill_sessions(matter: &Matter<'_>, reserved: bool) {
1518 let dev_det = matter.dev_det();
1519 matter.with_state(|state| loop {
1520 if state
1521 .sessions
1522 .add(0, reserved, network::Address::new(), None, dev_det)
1523 .is_err()
1524 {
1525 break;
1526 }
1527 });
1528 }
1529
1530 #[test]
1531 fn test_initiate_unsecured_creates_initiator_exchange() {
1532 let matter = test_matter();
1533 let crypto = test_only_crypto();
1534 let peer = network::Address::new();
1535
1536 let exchange = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer)).unwrap();
1537
1538 exchange
1539 .with_state(|state| {
1540 let sess = exchange.id().session(&mut state.sessions);
1541 let exch = exchange.id().exch(sess);
1542
1543 assert!(matches!(exch.role, Role::Initiator(_)));
1544 assert_eq!(sess.id, exchange.id().session_id());
1545 assert!(!sess.is_encrypted());
1546 assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1547
1548 Ok(())
1549 })
1550 .unwrap();
1551 }
1552
1553 #[test]
1554 fn test_initiate_unsecured_retries_after_eviction() {
1555 let matter = test_matter();
1556 let crypto = test_only_crypto();
1557 let peer = network::Address::new();
1558
1559 fill_sessions(&matter, false);
1560
1561 let exchange = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer)).unwrap();
1562
1563 exchange
1564 .with_state(|state| {
1565 let sess = exchange.id().session(&mut state.sessions);
1566 let exch = exchange.id().exch(sess);
1567
1568 assert!(matches!(exch.role, Role::Initiator(_)));
1569 assert_eq!(sess.id, exchange.id().session_id());
1570 assert!(!sess.is_encrypted());
1571 assert_eq!(*sess.get_session_mode(), SessionMode::PlainText);
1572
1573 Ok(())
1574 })
1575 .unwrap();
1576 }
1577
1578 #[test]
1579 fn test_initiate_unsecured_fails_when_no_session_can_be_evicted() {
1580 let matter = test_matter();
1581 let crypto = test_only_crypto();
1582 let peer = network::Address::new();
1583
1584 fill_sessions(&matter, true);
1585
1586 let result = block_on(Exchange::initiate_unsecured(&matter, &crypto, peer));
1587
1588 match result {
1589 Err(err) => assert!(matches!(err.code(), ErrorCode::NoSpaceSessions)),
1590 Ok(_) => panic!("expected NoSpaceSessions error"),
1591 }
1592 }
1593}