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}