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