Skip to main content

rs_matter/transport/
plain_hdr.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;
19
20use crate::error::*;
21use crate::utils::storage::{ParseBuf, WriteBuf};
22
23bitflags::bitflags! {
24    #[repr(transparent)]
25    #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
26    pub struct MsgFlags: u8 {
27        const DSIZ_UNICAST_NODEID = 0x01;
28        const DSIZ_GROUPCAST_NODEID = 0x02;
29        const SRC_ADDR_PRESENT = 0x04;
30    }
31
32    #[repr(transparent)]
33    #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
34    pub struct SecFlags: u8 {
35        /// Session Type is a Group Session (Session Type = 1).
36        const GROUP_SESSION = 0x01;
37        /// Message Extensions present.
38        const MSG_EXT = 0x20;
39        /// Control message. Messages with this bit use the peer's control
40        /// message counter (not the data counter) for AEAD-nonce framing.
41        const CONTROL_MSG = 0x40;
42        /// Privacy-encoded message. Not currently produced or accepted by
43        /// this crate; the bit is declared so an incoming value survives
44        /// a round-trip through the bitflag rather than triggering
45        /// `from_bits`-fails.
46        const PRIVACY = 0x80;
47    }
48}
49
50const DSIZ_MASK: MsgFlags = MsgFlags::DSIZ_UNICAST_NODEID.union(MsgFlags::DSIZ_GROUPCAST_NODEID);
51
52impl fmt::Display for MsgFlags {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        let mut sep = false;
55        for flag in [
56            Self::SRC_ADDR_PRESENT,
57            Self::DSIZ_UNICAST_NODEID,
58            Self::DSIZ_GROUPCAST_NODEID,
59        ] {
60            if self.contains(flag) {
61                if sep {
62                    write!(f, "|")?;
63                }
64
65                let str = match flag {
66                    Self::DSIZ_UNICAST_NODEID => "U",
67                    Self::DSIZ_GROUPCAST_NODEID => "G",
68                    Self::SRC_ADDR_PRESENT => "S",
69                    _ => "?",
70                };
71
72                write!(f, "{}", str)?;
73                sep = true;
74            }
75        }
76
77        Ok(())
78    }
79}
80
81#[cfg(feature = "defmt")]
82impl defmt::Format for MsgFlags {
83    fn format(&self, f: defmt::Formatter<'_>) {
84        let mut sep = false;
85        for flag in [
86            Self::SRC_ADDR_PRESENT,
87            Self::DSIZ_UNICAST_NODEID,
88            Self::DSIZ_GROUPCAST_NODEID,
89        ] {
90            if self.contains(flag) {
91                if sep {
92                    defmt::write!(f, "|");
93                }
94
95                let str = match flag {
96                    Self::DSIZ_UNICAST_NODEID => "U",
97                    Self::DSIZ_GROUPCAST_NODEID => "G",
98                    Self::SRC_ADDR_PRESENT => "S",
99                    _ => "?",
100                };
101
102                defmt::write!(f, "{}", str);
103                sep = true;
104            }
105        }
106    }
107}
108
109// This is the unencrypted message
110#[derive(Debug, Default, Clone)]
111pub struct PlainHdr {
112    flags: MsgFlags,
113    pub sess_id: u16,
114    pub(crate) sec_flags: SecFlags,
115    pub ctr: u32,
116    src_nodeid: u64,
117    dst_nodeid: u64,
118}
119
120impl PlainHdr {
121    pub const MAX_LEN: usize =
122        // [optional] msg len only for TCP
123        2
124        // flags
125        + 1
126        // security flags
127        + 1
128        // session ID
129        + 2
130        // message ctr
131        + 4
132        // [optional] source node ID
133        + 8
134        // [optional] destination node ID
135        + 8;
136
137    #[inline(always)]
138    pub const fn new() -> Self {
139        Self {
140            flags: MsgFlags::empty(),
141            sess_id: 0,
142            sec_flags: SecFlags::empty(),
143            ctr: 0,
144            src_nodeid: 0,
145            dst_nodeid: 0,
146        }
147    }
148
149    pub fn get_src_nodeid(&self) -> Option<u64> {
150        if self.flags.contains(MsgFlags::SRC_ADDR_PRESENT) {
151            Some(self.src_nodeid)
152        } else {
153            None
154        }
155    }
156
157    pub fn set_src_nodeid(&mut self, id: Option<u64>) {
158        if let Some(id) = id {
159            self.flags |= MsgFlags::SRC_ADDR_PRESENT;
160            self.src_nodeid = id;
161        } else {
162            self.flags.remove(MsgFlags::SRC_ADDR_PRESENT);
163            self.src_nodeid = 0;
164        }
165    }
166
167    pub fn get_dst_unicast_nodeid(&self) -> Option<u64> {
168        if self.flags.intersection(DSIZ_MASK) == MsgFlags::DSIZ_UNICAST_NODEID {
169            Some(self.dst_nodeid)
170        } else {
171            None
172        }
173    }
174
175    pub fn set_dst_unicast_nodeid(&mut self, id: Option<u64>) {
176        if let Some(id) = id {
177            self.flags |= MsgFlags::DSIZ_UNICAST_NODEID;
178            self.flags.remove(MsgFlags::DSIZ_GROUPCAST_NODEID);
179            self.dst_nodeid = id;
180        } else {
181            self.flags.remove(DSIZ_MASK);
182            self.dst_nodeid = 0;
183        }
184    }
185
186    pub fn get_dst_groupcast_nodeid(&self) -> Option<u16> {
187        if self.flags.intersection(DSIZ_MASK) == MsgFlags::DSIZ_GROUPCAST_NODEID {
188            Some(self.dst_nodeid as u16)
189        } else {
190            None
191        }
192    }
193
194    pub fn set_dst_groupcast_nodeid(&mut self, id: Option<u16>) {
195        if let Some(id) = id {
196            self.flags |= MsgFlags::DSIZ_GROUPCAST_NODEID;
197            self.flags.remove(MsgFlags::DSIZ_UNICAST_NODEID);
198            self.dst_nodeid = id as u64;
199        } else {
200            self.flags.remove(DSIZ_MASK);
201            self.dst_nodeid = 0;
202        }
203    }
204
205    // it will have an additional 'message length' field first
206    pub fn decode(&mut self, msg: &mut ParseBuf) -> Result<(), Error> {
207        self.flags = MsgFlags::from_bits(msg.le_u8()?).ok_or(ErrorCode::Invalid)?;
208        self.sess_id = msg.le_u16()?;
209        self.sec_flags = SecFlags::from_bits(msg.le_u8()?).ok_or(ErrorCode::Invalid)?;
210        self.ctr = msg.le_u32()?;
211
212        if self.flags.contains(MsgFlags::SRC_ADDR_PRESENT) {
213            self.src_nodeid = msg.le_u64()?;
214        }
215
216        if !self.flags.contains(DSIZ_MASK) {
217            if self.flags.contains(MsgFlags::DSIZ_UNICAST_NODEID) {
218                self.dst_nodeid = msg.le_u64()?;
219            } else if self.flags.contains(MsgFlags::DSIZ_GROUPCAST_NODEID) {
220                self.dst_nodeid = msg.le_u16()? as u64;
221            }
222        }
223
224        trace!("[decode] {}", self);
225        Ok(())
226    }
227
228    pub fn encode(&self, resp_buf: &mut WriteBuf) -> Result<(), Error> {
229        trace!("[encode] {}", self);
230        resp_buf.le_u8(self.flags.bits())?;
231        resp_buf.le_u16(self.sess_id)?;
232        resp_buf.le_u8(self.sec_flags.bits())?;
233        resp_buf.le_u32(self.ctr)?;
234
235        if self.flags.contains(MsgFlags::SRC_ADDR_PRESENT) {
236            resp_buf.le_u64(self.src_nodeid)?;
237        }
238
239        if !self.flags.contains(DSIZ_MASK) {
240            if self.flags.contains(MsgFlags::DSIZ_UNICAST_NODEID) {
241                resp_buf.le_u64(self.dst_nodeid)?;
242            } else if self.flags.contains(MsgFlags::DSIZ_GROUPCAST_NODEID) {
243                resp_buf.le_u16(self.dst_nodeid as u16)?;
244            }
245        }
246
247        Ok(())
248    }
249
250    pub fn is_group_session(&self) -> bool {
251        self.sec_flags.contains(SecFlags::GROUP_SESSION)
252    }
253
254    /// Set or clear the Group Session bit on the outgoing message.
255    pub fn set_group_session(&mut self, group: bool) {
256        if group {
257            self.sec_flags |= SecFlags::GROUP_SESSION;
258        } else {
259            self.sec_flags.remove(SecFlags::GROUP_SESSION);
260        }
261    }
262
263    /// Whether the message is a control message (Security Flags `C` bit).
264    pub fn is_control_msg(&self) -> bool {
265        self.sec_flags.contains(SecFlags::CONTROL_MSG)
266    }
267
268    /// Set or clear the control message (`C`) bit on the outgoing message.
269    pub fn set_control_msg(&mut self, control: bool) {
270        if control {
271            self.sec_flags |= SecFlags::CONTROL_MSG;
272        } else {
273            self.sec_flags.remove(SecFlags::CONTROL_MSG);
274        }
275    }
276
277    /// Whether the message declares itself privacy-encoded (Security Flags
278    /// `P` bit). This crate does not yet produce or accept such messages;
279    /// callers may use this to log or reject them explicitly.
280    pub fn is_privacy(&self) -> bool {
281        self.sec_flags.contains(SecFlags::PRIVACY)
282    }
283
284    pub fn is_encrypted(&self) -> bool {
285        self.sess_id != 0 || self.is_group_session()
286    }
287}
288
289impl fmt::Display for PlainHdr {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        if !self.flags.is_empty() {
292            write!(f, "{},", self.flags)?;
293        }
294
295        write!(f, "SID:{:x},CTR:{:x}", self.sess_id, self.ctr)?;
296
297        if self.is_control_msg() {
298            write!(f, ",C")?;
299        }
300        if self.is_privacy() {
301            write!(f, ",P")?;
302        }
303
304        if let Some(src_nodeid) = self.get_src_nodeid() {
305            write!(f, ",SRC:{:x}", src_nodeid)?;
306        }
307
308        if let Some(dst_nodeid) = self.get_dst_unicast_nodeid() {
309            write!(f, ",DST:{:x}", dst_nodeid)?;
310        }
311
312        if let Some(dst_group_nodeid) = self.get_dst_groupcast_nodeid() {
313            write!(f, ",GRP:{:x}", dst_group_nodeid)?;
314        }
315
316        Ok(())
317    }
318}
319
320#[cfg(feature = "defmt")]
321impl defmt::Format for PlainHdr {
322    fn format(&self, f: defmt::Formatter<'_>) {
323        if !self.flags.is_empty() {
324            defmt::write!(f, "{},", self.flags);
325        }
326
327        defmt::write!(f, "SID:{:x},CTR:{:x}", self.sess_id, self.ctr);
328
329        if self.is_control_msg() {
330            defmt::write!(f, ",C");
331        }
332        if self.is_privacy() {
333            defmt::write!(f, ",P");
334        }
335
336        if let Some(src_nodeid) = self.get_src_nodeid() {
337            defmt::write!(f, ",SRC:{:x}", src_nodeid);
338        }
339
340        if let Some(dst_nodeid) = self.get_dst_unicast_nodeid() {
341            defmt::write!(f, ",DST:{:x}", dst_nodeid);
342        }
343
344        if let Some(dst_group_nodeid) = self.get_dst_groupcast_nodeid() {
345            defmt::write!(f, ",GRP:{:x}", dst_group_nodeid);
346        }
347    }
348}