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 let mut control = self.control;
48 if self.destination.is_some() {
49 control |= 0x20;
50 } else {
51 control &= !0x20;
52 }
53 if self.source.is_some() {
54 control |= 0x08;
55 } else {
56 control &= !0x08;
57 }
58 if self.message_type.is_some() {
59 control |= 0x80;
60 } else {
61 control &= !0x80;
62 }
63
64 w.write_u8(NPDU_VERSION)?;
65 w.write_u8(control)?;
66
67 if let Some(dest) = self.destination {
68 encode_addr(w, dest)?;
69 }
70 if let Some(src) = self.source {
71 encode_addr(w, src)?;
72 }
73 if self.destination.is_some() {
74 w.write_u8(self.hop_count.unwrap_or(255))?;
75 }
76 if let Some(mt) = self.message_type {
77 w.write_u8(mt)?;
78 if mt >= 0x80 {
79 w.write_be_u16(self.vendor_id.unwrap_or(0))?;
80 }
81 }
82 Ok(())
83 }
84
85 pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
86 let version = r.read_u8()?;
87 if version != NPDU_VERSION {
88 return Err(DecodeError::InvalidValue);
89 }
90
91 let control = r.read_u8()?;
92 let has_dest = (control & 0x20) != 0;
93 let has_src = (control & 0x08) != 0;
94 let is_network_msg = (control & 0x80) != 0;
95
96 let destination = if has_dest {
97 Some(decode_addr(r)?)
98 } else {
99 None
100 };
101 let source = if has_src { Some(decode_addr(r)?) } else { None };
102 let hop_count = if has_dest { Some(r.read_u8()?) } else { None };
103
104 let (message_type, vendor_id) = if is_network_msg {
105 let mt = r.read_u8()?;
106 let vid = if mt >= 0x80 {
107 Some(r.read_be_u16()?)
108 } else {
109 None
110 };
111 (Some(mt), vid)
112 } else {
113 (None, None)
114 };
115
116 Ok(Self {
117 control,
118 destination,
119 source,
120 hop_count,
121 message_type,
122 vendor_id,
123 })
124 }
125}
126
127fn encode_addr(w: &mut Writer<'_>, addr: NpduAddress) -> Result<(), EncodeError> {
128 if addr.mac_len as usize > addr.mac.len() {
129 return Err(EncodeError::InvalidLength);
130 }
131 w.write_be_u16(addr.network)?;
132 w.write_u8(addr.mac_len)?;
133 w.write_all(&addr.mac[..addr.mac_len as usize])
134}
135
136fn decode_addr(r: &mut Reader<'_>) -> Result<NpduAddress, DecodeError> {
137 let network = r.read_be_u16()?;
138 let mac_len = r.read_u8()?;
139 if mac_len as usize > 6 {
140 return Err(DecodeError::InvalidLength);
141 }
142 let mut mac = [0u8; 6];
143 let src = r.read_exact(mac_len as usize)?;
144 mac[..mac_len as usize].copy_from_slice(src);
145 Ok(NpduAddress {
146 network,
147 mac,
148 mac_len,
149 })
150}
151
152#[cfg(test)]
153mod tests {
154 use super::{Npdu, NpduAddress};
155 use crate::encoding::{reader::Reader, writer::Writer};
156
157 #[test]
158 fn npdu_roundtrip() {
159 let mut p = Npdu::new(0x20);
160 p.destination = Some(NpduAddress {
161 network: 1,
162 mac: [192, 168, 1, 2, 0xBA, 0xC0],
163 mac_len: 6,
164 });
165 p.hop_count = Some(255);
166
167 let mut buf = [0u8; 32];
168 let mut w = Writer::new(&mut buf);
169 p.encode(&mut w).unwrap();
170
171 let mut r = Reader::new(w.as_written());
172 let dec = Npdu::decode(&mut r).unwrap();
173 assert_eq!(dec.control, p.control);
174 assert_eq!(dec.destination.unwrap().network, 1);
175 }
176
177 #[test]
178 fn network_message_vendor_id_only_for_vendor_types() {
179 let mut p = Npdu::new(0x80);
180 p.message_type = Some(0x80);
181 p.vendor_id = Some(260);
182
183 let mut buf = [0u8; 16];
184 let mut w = Writer::new(&mut buf);
185 p.encode(&mut w).unwrap();
186
187 let mut r = Reader::new(w.as_written());
188 let dec = Npdu::decode(&mut r).unwrap();
189 assert_eq!(dec.message_type, Some(0x80));
190 assert_eq!(dec.vendor_id, Some(260));
191 }
192}