1use crate::encoding::{reader::Reader, writer::Writer};
2use crate::{DecodeError, EncodeError};
3
4pub const NPDU_VERSION: u8 = 0x01;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct NpduAddress {
10 pub network: u16,
12 pub mac: [u8; 6],
14 pub mac_len: u8,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct Npdu {
24 pub control: u8,
25 pub destination: Option<NpduAddress>,
26 pub source: Option<NpduAddress>,
27 pub hop_count: Option<u8>,
28 pub message_type: Option<u8>,
29 pub vendor_id: Option<u16>,
30}
31
32impl Npdu {
33 pub const fn new(control: u8) -> Self {
34 Self {
35 control,
36 destination: None,
37 source: None,
38 hop_count: None,
39 message_type: None,
40 vendor_id: None,
41 }
42 }
43
44 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
45 w.write_u8(NPDU_VERSION)?;
46 w.write_u8(self.control)?;
47
48 if let Some(dest) = self.destination {
49 encode_addr(w, dest)?;
50 }
51 if let Some(src) = self.source {
52 encode_addr(w, src)?;
53 }
54 if self.destination.is_some() {
55 w.write_u8(self.hop_count.unwrap_or(255))?;
56 }
57 if (self.control & 0x80) != 0 {
58 w.write_u8(self.message_type.unwrap_or(0))?;
59 if matches!(self.message_type, Some(0x80..=0xFF)) {
60 w.write_be_u16(self.vendor_id.unwrap_or(0))?;
61 }
62 }
63 Ok(())
64 }
65
66 pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
67 let version = r.read_u8()?;
68 if version != NPDU_VERSION {
69 return Err(DecodeError::InvalidValue);
70 }
71
72 let control = r.read_u8()?;
73 let has_dest = (control & 0x20) != 0;
74 let has_src = (control & 0x08) != 0;
75 let is_network_msg = (control & 0x80) != 0;
76
77 let destination = if has_dest {
78 Some(decode_addr(r)?)
79 } else {
80 None
81 };
82 let source = if has_src { Some(decode_addr(r)?) } else { None };
83 let hop_count = if has_dest { Some(r.read_u8()?) } else { None };
84
85 let (message_type, vendor_id) = if is_network_msg {
86 let mt = r.read_u8()?;
87 let vid = if mt >= 0x80 {
88 Some(r.read_be_u16()?)
89 } else {
90 None
91 };
92 (Some(mt), vid)
93 } else {
94 (None, None)
95 };
96
97 Ok(Self {
98 control,
99 destination,
100 source,
101 hop_count,
102 message_type,
103 vendor_id,
104 })
105 }
106}
107
108fn encode_addr(w: &mut Writer<'_>, addr: NpduAddress) -> Result<(), EncodeError> {
109 if addr.mac_len as usize > addr.mac.len() {
110 return Err(EncodeError::InvalidLength);
111 }
112 w.write_be_u16(addr.network)?;
113 w.write_u8(addr.mac_len)?;
114 w.write_all(&addr.mac[..addr.mac_len as usize])
115}
116
117fn decode_addr(r: &mut Reader<'_>) -> Result<NpduAddress, DecodeError> {
118 let network = r.read_be_u16()?;
119 let mac_len = r.read_u8()?;
120 if mac_len as usize > 6 {
121 return Err(DecodeError::InvalidLength);
122 }
123 let mut mac = [0u8; 6];
124 let src = r.read_exact(mac_len as usize)?;
125 mac[..mac_len as usize].copy_from_slice(src);
126 Ok(NpduAddress {
127 network,
128 mac,
129 mac_len,
130 })
131}
132
133#[cfg(test)]
134mod tests {
135 use super::{Npdu, NpduAddress};
136 use crate::encoding::{reader::Reader, writer::Writer};
137
138 #[test]
139 fn npdu_roundtrip() {
140 let mut p = Npdu::new(0x20);
141 p.destination = Some(NpduAddress {
142 network: 1,
143 mac: [192, 168, 1, 2, 0xBA, 0xC0],
144 mac_len: 6,
145 });
146 p.hop_count = Some(255);
147
148 let mut buf = [0u8; 32];
149 let mut w = Writer::new(&mut buf);
150 p.encode(&mut w).unwrap();
151
152 let mut r = Reader::new(w.as_written());
153 let dec = Npdu::decode(&mut r).unwrap();
154 assert_eq!(dec.control, p.control);
155 assert_eq!(dec.destination.unwrap().network, 1);
156 }
157
158 #[test]
159 fn network_message_vendor_id_only_for_vendor_types() {
160 let mut p = Npdu::new(0x80);
161 p.message_type = Some(0x80);
162 p.vendor_id = Some(260);
163
164 let mut buf = [0u8; 16];
165 let mut w = Writer::new(&mut buf);
166 p.encode(&mut w).unwrap();
167
168 let mut r = Reader::new(w.as_written());
169 let dec = Npdu::decode(&mut r).unwrap();
170 assert_eq!(dec.message_type, Some(0x80));
171 assert_eq!(dec.vendor_id, Some(260));
172 }
173}