Skip to main content

rs_matter/transport/
network.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, Debug, Display};
19use core::future::Future;
20use core::pin::pin;
21
22pub use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
23
24use embassy_futures::select::{select, Either};
25
26use crate::error::{Error, ErrorCode};
27
28pub mod btp;
29pub mod mdns;
30pub mod tcp;
31pub mod thread;
32pub mod udp;
33pub mod wifi;
34
35// Maximum UDP RX packet size per Matter spec
36pub const MAX_RX_PACKET_SIZE: usize = 1583;
37
38// Maximum UDP TX packet size per Matter spec
39pub const MAX_TX_PACKET_SIZE: usize = 1280 - 40/*IPV6 header size*/ - 8/*UDP header size*/;
40
41// Maximum TCP RX packet size per Matter spec
42pub const MAX_RX_LARGE_PACKET_SIZE: usize = 1024 * 1024;
43
44// Maximum TCP TX packet size per Matter spec
45pub const MAX_TX_LARGE_PACKET_SIZE: usize = MAX_RX_LARGE_PACKET_SIZE;
46
47/// A Matter service that **this** node advertises (publishes) over a discovery
48/// transport such as mDNS.
49///
50/// This is the *publish-side* identity; the *query-side* analog is
51/// [`MatterRemoteService`]. The discovery-transport encoding (e.g. the mDNS
52/// `MdnsLocalService` record) lives in the [`mdns`] module
53/// (`MatterLocalService::service`).
54#[derive(Debug, Clone, Eq, PartialEq, Hash)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum MatterLocalService {
57    /// A commissioned Matter service for a particular fabric
58    ///
59    /// The published name is in the form `<compressed-fabric-id-hex>-<node-id-hex>`.
60    Commissioned {
61        compressed_fabric_id: u64,
62        node_id: u64,
63    },
64    /// A non-commissioned Matter service
65    ///
66    /// The published name is in the form `<id-hex>`. The discriminator should be used as an mDNS TXT entry
67    Commissionable {
68        id: u64,
69        /// The discriminator to be communicated over mDNS
70        discriminator: u16,
71        /// Whether this is an enhanced (ECM) commissioning window (`CM=2`) vs basic (`CM=1`)
72        enhanced: bool,
73    },
74}
75
76/// A Matter service **elsewhere** that this node resolves / looks up over a
77/// discovery transport such as mDNS.
78///
79/// This is the *query-side* analog of the *publish-side* [`MatterLocalService`]:
80/// it identifies a single Matter service instance to resolve (SRV/TXT/A/AAAA),
81/// rather than describing one to advertise. The discovery-transport encoding
82/// (e.g. the mDNS instance name) lives in the [`mdns`] module
83/// (`MatterRemoteService::instance_name`).
84///
85/// Note that *browsing* (enumerating all commissionable or operational nodes)
86/// does not need a `MatterRemoteService` - it is a PTR query against the bare
87/// service type.
88#[derive(Debug, Clone, Eq, PartialEq, Hash)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub enum MatterRemoteService {
91    /// A specific operational (commissioned) node.
92    ///
93    /// The instance name is `<compressed-fabric-id-hex>-<node-id-hex>._matter._tcp.local`.
94    Operational {
95        compressed_fabric_id: u64,
96        node_id: u64,
97    },
98    /// A specific commissionable instance.
99    ///
100    /// The instance name is `<id-hex>._matterc._udp.local`.
101    Commissionable { id: u64 },
102}
103
104/// A Bluetooth address.
105#[derive(Copy, Clone, Eq, PartialEq, Debug)]
106pub struct BtAddr(pub [u8; 6]);
107
108impl Display for BtAddr {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(
111            f,
112            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
113            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
114        )
115    }
116}
117
118#[cfg(feature = "defmt")]
119impl defmt::Format for BtAddr {
120    fn format(&self, f: defmt::Formatter<'_>) {
121        defmt::write!(
122            f,
123            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
124            self.0[0],
125            self.0[1],
126            self.0[2],
127            self.0[3],
128            self.0[4],
129            self.0[5]
130        )
131    }
132}
133
134/// An enum representing a network address for all supported protocols by the Matter specification (UDP, TCP and BTP).
135#[derive(Eq, PartialEq, Copy, Clone)]
136pub enum Address {
137    Udp(SocketAddr),
138    Tcp(SocketAddr),
139    Btp(BtAddr),
140}
141
142impl Address {
143    pub const fn new() -> Self {
144        Self::Udp(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0))
145    }
146
147    pub const fn is_reliable(&self) -> bool {
148        matches!(self, Self::Tcp(_) | Self::Btp(_))
149    }
150
151    pub const fn is_udp(&self) -> bool {
152        matches!(self, Self::Udp(_))
153    }
154
155    pub const fn is_tcp(&self) -> bool {
156        matches!(self, Self::Tcp(_))
157    }
158
159    pub const fn is_btp(&self) -> bool {
160        matches!(self, Self::Btp(_))
161    }
162
163    /// Return this address with its IP canonicalized: an IPv4-mapped IPv6
164    /// address (`::ffff:a.b.c.d`) is rewritten to its true IPv4 form, leaving
165    /// genuine IPv4 / IPv6 / BTP addresses unchanged.
166    ///
167    /// This matters because a dual-stack IPv6 socket reports an IPv4 peer's
168    /// packets to `recv_from` in IPv4-mapped form, whereas the same peer is
169    /// usually *sent to* (and stored on the session) as a plain `V4` address.
170    /// `Address` derives `PartialEq`/`Eq` over the `SocketAddr` (family
171    /// included), so without canonicalization `V4(x)` and `V6(::ffff:x)` would
172    /// compare unequal and session lookup by peer address would fail (PASE then
173    /// reports "PAKE session not found").
174    ///
175    /// This is used only when *comparing* a session's peer address against a
176    /// received one (see `Session::is_for_rx` / `is_pase_for_addr`); the address
177    /// stored on the session is left untouched so it still routes replies to the
178    /// exact address the peer was reached at.
179    pub fn canonical(self) -> Self {
180        match self {
181            Self::Udp(addr) => Self::Udp(canonical_sockaddr(addr)),
182            Self::Tcp(addr) => Self::Tcp(canonical_sockaddr(addr)),
183            other => other,
184        }
185    }
186
187    pub const fn udp(self) -> Option<SocketAddr> {
188        match self {
189            Self::Udp(addr) => Some(addr),
190            _ => None,
191        }
192    }
193
194    pub const fn tcp(self) -> Option<SocketAddr> {
195        match self {
196            Self::Tcp(addr) => Some(addr),
197            _ => None,
198        }
199    }
200
201    pub const fn btp(self) -> Option<BtAddr> {
202        match self {
203            Self::Btp(addr) => Some(addr),
204            _ => None,
205        }
206    }
207}
208
209/// Canonicalize a [`SocketAddr`]: an IPv4-mapped IPv6 address
210/// (`::ffff:a.b.c.d`) is rewritten to its true IPv4 form (preserving port),
211/// everything else is returned unchanged. See [`Address::canonical`].
212fn canonical_sockaddr(addr: SocketAddr) -> SocketAddr {
213    match addr {
214        SocketAddr::V6(v6) => match v6.ip().to_canonical() {
215            // `IpAddr::to_canonical` (stable since Rust 1.75) maps
216            // `::ffff:a.b.c.d` to `a.b.c.d` and leaves true IPv6 untouched.
217            IpAddr::V4(v4) => SocketAddr::new(IpAddr::V4(v4), v6.port()),
218            IpAddr::V6(_) => addr,
219        },
220        SocketAddr::V4(_) => addr,
221    }
222}
223
224impl Default for Address {
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230impl Display for Address {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        match self {
233            Address::Udp(addr) => write!(f, "UDP {}", addr),
234            Address::Tcp(addr) => write!(f, "TCP {}", addr),
235            Address::Btp(addr) => write!(f, "BTP {}", addr),
236        }
237    }
238}
239
240impl Debug for Address {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            Address::Udp(addr) => writeln!(f, "{}", addr),
244            Address::Tcp(addr) => writeln!(f, "{}", addr),
245            Address::Btp(addr) => writeln!(f, "{:?}", addr),
246        }
247    }
248}
249
250#[cfg(feature = "defmt")]
251impl defmt::Format for Address {
252    fn format(&self, f: defmt::Formatter<'_>) {
253        match self {
254            Address::Udp(addr) => defmt::write!(f, "UDP {}", addr),
255            Address::Tcp(addr) => defmt::write!(f, "TCP {}", addr),
256            Address::Btp(addr) => defmt::write!(f, "BTP {}", addr),
257        }
258    }
259}
260
261/// A trait for sending data to a network address.
262///
263/// All network communication in the Matter transport is packetized (including via TCP and Bluetooth), hence
264/// this trait models the sending of a single Matter packet of data to a network address.
265///
266/// Data packetization is expected to be handled by the implementation of this trait, and is trivial
267/// for e.g. the UDP transport which is packetized by default, but more complex for e.g. the TCP transport and especially for BTP.
268pub trait NetworkSend {
269    /// Send a Matter packet represented as a sequence of bytes (`data`) to the specified address.
270    ///
271    /// Might return an error if the address is not supported, or if there is a general error on the network interface.
272    async fn send_to(&mut self, data: &[u8], addr: Address) -> Result<(), Error>;
273}
274
275impl<T> NetworkSend for &mut T
276where
277    T: NetworkSend,
278{
279    fn send_to(&mut self, data: &[u8], addr: Address) -> impl Future<Output = Result<(), Error>> {
280        (*self).send_to(data, addr)
281    }
282}
283
284/// A trait for receiving data from a network address.
285///
286/// All network communication in the Matter transport is packetized (including via TCP and Bluetooth), hence
287/// this trait models the receiving of a single Matter packet of data from a network address.
288///
289/// Data packetization is expected to be handled by the implementation of this trait, and is trivial
290/// for e.g. the UDP transport which is packetized by default, but more complex for e.g. the TCP transport and especially for BTP.
291pub trait NetworkReceive {
292    /// Wait until a data packet is available to be received.
293    ///
294    /// Allows the Matter transport layer to re-use a single RX buffer accross all network protocol implementatiins.
295    ///
296    /// Might return an error if there is a general error on the network interface.
297    async fn wait_available(&mut self) -> Result<(), Error>;
298
299    /// Receive a single data packet from the network.
300    ///
301    /// Might return an error if there is a general error on the network interface.
302    async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Address), Error>;
303}
304
305impl<T> NetworkReceive for &mut T
306where
307    T: NetworkReceive,
308{
309    fn wait_available(&mut self) -> impl Future<Output = Result<(), Error>> {
310        (*self).wait_available()
311    }
312
313    fn recv_from(
314        &mut self,
315        buffer: &mut [u8],
316    ) -> impl Future<Output = Result<(usize, Address), Error>> {
317        (*self).recv_from(buffer)
318    }
319}
320
321/// A trait to listen for IPv6 multicast on supported network types
322///
323/// This is used for listening to groupcast messages
324pub trait NetworkMulticast {
325    /// Join a multicast group with the specified address.
326    async fn join(&mut self, addr: IpAddr) -> Result<(), Error>;
327
328    /// Leave a multicast group with the specified address.
329    async fn leave(&mut self, addr: IpAddr) -> Result<(), Error>;
330}
331
332impl<T> NetworkMulticast for &mut T
333where
334    T: NetworkMulticast,
335{
336    fn join(&mut self, addr: IpAddr) -> impl Future<Output = Result<(), Error>> {
337        (*self).join(addr)
338    }
339
340    fn leave(&mut self, addr: IpAddr) -> impl Future<Output = Result<(), Error>> {
341        (*self).leave(addr)
342    }
343}
344
345/// A network implementation that does not support any network communication:
346/// - Trying to send a packet always results in a `ErrorCode::NoNetworkInterface` error.
347/// - Trying to wait/receive a packet pends forever.
348/// - Joining/leaving multicast groups is a no-op that always succeeds.
349///
350/// Useful when chaining multiple network interfaces together to serve as the last network interface in the chain.
351pub struct NoNetwork;
352
353impl NetworkSend for NoNetwork {
354    async fn send_to(&mut self, _data: &[u8], _addr: Address) -> Result<(), Error> {
355        Err(ErrorCode::NoNetworkInterface.into())
356    }
357}
358
359impl NetworkReceive for NoNetwork {
360    async fn wait_available(&mut self) -> Result<(), Error> {
361        core::future::pending().await
362    }
363
364    async fn recv_from(&mut self, _buffer: &mut [u8]) -> Result<(usize, Address), Error> {
365        core::future::pending().await
366    }
367}
368
369impl NetworkMulticast for NoNetwork {
370    async fn join(&mut self, _addr: IpAddr) -> Result<(), Error> {
371        Ok(())
372    }
373
374    async fn leave(&mut self, _addr: IpAddr) -> Result<(), Error> {
375        Ok(())
376    }
377}
378
379/// A network implementation that chains two network implementations together in a composite network interface.
380///
381/// This allows for e.g. a network implementation that can send/receive data to/from both a UDP and a TCP network interface - or -
382/// with e.g. further chaining - from all of UDP, TCP and BTP network interfaces.
383#[derive(Clone)]
384pub struct ChainedNetwork<H, T, F> {
385    pub handler_can_send: F,
386    pub handler: H,
387    pub next: T,
388}
389
390impl<H, T, F> ChainedNetwork<H, T, F> {
391    /// Construct a chained handler that works as follows:
392    /// - When a packet is about to be send, the `handler_can_send` function is called with the destination address.
393    ///   If it returns `true`, the packet is sent via the `handler` network interface, otherwise it is sent via the `next` network interface.
394    /// - When `wait_available` is called, the function waits until a packet is available on either network interface.
395    /// - When `recv_from` is called, the function receives a packet from the first network interface that has a packet available.
396    pub const fn new(handler_can_send: F, handler: H, next: T) -> Self {
397        Self {
398            handler_can_send,
399            handler,
400            next,
401        }
402    }
403
404    /// Chain itself with another handler.
405    ///
406    /// The returned chained handler works as follows:
407    /// - When a packet is about to be send, the `handler_can_send` function is called with the destination address.
408    ///   If it returns `true`, the packet is sent via the `handler` network interface, otherwise it is sent via `self`.
409    /// - When `wait_available` is called, the function waits until a packet is available on either network interface.
410    /// - When `recv_from` is called, the function receives a packet from the first network interface that has a packet available.
411    pub const fn chain<H2, F2>(
412        self,
413        handler_can_send: F2,
414        handler: H2,
415    ) -> ChainedNetwork<H2, Self, F2> {
416        ChainedNetwork::new(handler_can_send, handler, self)
417    }
418}
419
420impl<H, T, F> NetworkReceive for ChainedNetwork<H, T, F>
421where
422    H: NetworkReceive,
423    T: NetworkReceive,
424{
425    async fn wait_available(&mut self) -> Result<(), Error> {
426        let mut first = pin!(self.handler.wait_available());
427        let mut second = pin!(self.next.wait_available());
428
429        select(&mut first, &mut second).await;
430
431        Ok(())
432    }
433
434    async fn recv_from(&mut self, buffer: &mut [u8]) -> Result<(usize, Address), Error> {
435        let first = {
436            let mut first_available = pin!(self.handler.wait_available());
437            let mut second_available = pin!(self.next.wait_available());
438
439            matches!(
440                select(&mut first_available, &mut second_available).await,
441                Either::First(_)
442            )
443        };
444
445        if first {
446            self.handler.recv_from(buffer).await
447        } else {
448            self.next.recv_from(buffer).await
449        }
450    }
451}
452
453impl<H, T, F> NetworkSend for ChainedNetwork<H, T, F>
454where
455    H: NetworkSend,
456    T: NetworkSend,
457    F: Fn(&Address) -> bool,
458{
459    async fn send_to(&mut self, data: &[u8], addr: Address) -> Result<(), Error> {
460        if (self.handler_can_send)(&addr) {
461            self.handler.send_to(data, addr).await
462        } else {
463            self.next.send_to(data, addr).await
464        }
465    }
466}
467
468impl<H, T, F> NetworkMulticast for ChainedNetwork<H, T, F>
469where
470    H: NetworkMulticast,
471    T: NetworkMulticast,
472{
473    async fn join(&mut self, addr: IpAddr) -> Result<(), Error> {
474        self.handler.join(addr).await?;
475        self.next.join(addr).await
476    }
477
478    async fn leave(&mut self, addr: IpAddr) -> Result<(), Error> {
479        self.handler.leave(addr).await?;
480        self.next.leave(addr).await
481    }
482}