1use core::convert::TryFrom;
2
3use crate::{KnxError, Result};
4
5pub const HEADER_LENGTH: u8 = 0x06;
6pub const PROTOCOL_VERSION_1_0: u8 = 0x10;
7
8#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(u16)]
11pub enum ServiceType {
12 SearchRequest = 0x0201,
13 SearchResponse = 0x0202,
14 DescriptionRequest = 0x0203,
15 DescriptionResponse = 0x0204,
16 ConnectRequest = 0x0205,
17 ConnectResponse = 0x0206,
18 ConnectionStateRequest = 0x0207,
19 ConnectionStateResponse = 0x0208,
20 DisconnectRequest = 0x0209,
21 DisconnectResponse = 0x020a,
22 TunnellingRequest = 0x0420,
23 TunnellingAck = 0x0421,
24 RoutingIndication = 0x0530,
25 RoutingLostMessage = 0x0531,
26 RoutingBusy = 0x0532,
27}
28
29impl ServiceType {
30 pub const ALL: [ServiceType; 15] = [
33 ServiceType::SearchRequest,
34 ServiceType::SearchResponse,
35 ServiceType::DescriptionRequest,
36 ServiceType::DescriptionResponse,
37 ServiceType::ConnectRequest,
38 ServiceType::ConnectResponse,
39 ServiceType::ConnectionStateRequest,
40 ServiceType::ConnectionStateResponse,
41 ServiceType::DisconnectRequest,
42 ServiceType::DisconnectResponse,
43 ServiceType::TunnellingRequest,
44 ServiceType::TunnellingAck,
45 ServiceType::RoutingIndication,
46 ServiceType::RoutingLostMessage,
47 ServiceType::RoutingBusy,
48 ];
49
50 pub const fn as_u16(self) -> u16 {
51 self as u16
52 }
53}
54
55impl TryFrom<u16> for ServiceType {
59 type Error = KnxError;
60
61 fn try_from(value: u16) -> Result<Self> {
62 ServiceType::ALL
63 .iter()
64 .copied()
65 .find(|st| st.as_u16() == value)
66 .ok_or(KnxError::UnsupportedServiceType(value))
67 }
68}
69
70#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub struct KnxNetIpHeader {
73 service_type: ServiceType,
74 total_length: u16,
75}
76
77impl KnxNetIpHeader {
78 pub const fn new(service_type: ServiceType, total_length: u16) -> Result<Self> {
79 if total_length < HEADER_LENGTH as u16 {
80 return Err(KnxError::InvalidFrame("total length shorter than header"));
81 }
82
83 Ok(Self {
84 service_type,
85 total_length,
86 })
87 }
88
89 pub const fn service_type(self) -> ServiceType {
90 self.service_type
91 }
92
93 pub const fn total_length(self) -> u16 {
94 self.total_length
95 }
96
97 pub fn decode(input: &[u8]) -> Result<(Self, &[u8])> {
98 if input.len() < HEADER_LENGTH as usize {
99 return Err(KnxError::BufferTooShort {
100 needed: HEADER_LENGTH as usize,
101 actual: input.len(),
102 });
103 }
104
105 if input[0] != HEADER_LENGTH {
106 return Err(KnxError::InvalidFrame("invalid KNXnet/IP header length"));
107 }
108 if input[1] != PROTOCOL_VERSION_1_0 {
109 return Err(KnxError::InvalidFrame("invalid KNXnet/IP protocol version"));
110 }
111
112 let service_type = ServiceType::try_from(u16::from_be_bytes([input[2], input[3]]))?;
113 let total_length = u16::from_be_bytes([input[4], input[5]]);
114 let header = Self::new(service_type, total_length)?;
115
116 let total_length = usize::from(total_length);
117 if input.len() < total_length {
118 return Err(KnxError::BufferTooShort {
119 needed: total_length,
120 actual: input.len(),
121 });
122 }
123
124 Ok((header, &input[HEADER_LENGTH as usize..total_length]))
125 }
126
127 #[cfg(feature = "std")]
128 pub fn encode(self, out: &mut std::vec::Vec<u8>) -> Result<()> {
129 out.extend_from_slice(&[
130 HEADER_LENGTH,
131 PROTOCOL_VERSION_1_0,
132 (self.service_type.as_u16() >> 8) as u8,
133 self.service_type.as_u16() as u8,
134 (self.total_length >> 8) as u8,
135 self.total_length as u8,
136 ]);
137 Ok(())
138 }
139}
140
141#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143#[repr(u8)]
144pub enum HostProtocol {
145 Ipv4Udp = 0x01,
146 Ipv4Tcp = 0x02,
147}
148
149const HOST_PROTOCOL_ALL: [HostProtocol; 2] = [HostProtocol::Ipv4Udp, HostProtocol::Ipv4Tcp];
152
153impl HostProtocol {
154 pub const fn as_u8(self) -> u8 {
155 self as u8
156 }
157}
158
159impl TryFrom<u8> for HostProtocol {
160 type Error = KnxError;
161
162 fn try_from(value: u8) -> Result<Self> {
163 HOST_PROTOCOL_ALL
164 .iter()
165 .copied()
166 .find(|hp| hp.as_u8() == value)
167 .ok_or(KnxError::InvalidFrame("unsupported host protocol"))
168 }
169}
170
171#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173pub struct Hpai {
174 protocol: HostProtocol,
175 address: [u8; 4],
176 port: u16,
177}
178
179impl Hpai {
180 pub const LENGTH: u8 = 0x08;
181
182 pub const fn new(protocol: HostProtocol, address: [u8; 4], port: u16) -> Self {
183 Self {
184 protocol,
185 address,
186 port,
187 }
188 }
189
190 pub const fn protocol(self) -> HostProtocol {
191 self.protocol
192 }
193
194 pub const fn address(self) -> [u8; 4] {
195 self.address
196 }
197
198 pub const fn port(self) -> u16 {
199 self.port
200 }
201
202 pub fn decode(input: &[u8]) -> Result<(Self, &[u8])> {
203 if input.len() < Self::LENGTH as usize {
204 return Err(KnxError::BufferTooShort {
205 needed: Self::LENGTH as usize,
206 actual: input.len(),
207 });
208 }
209 if input[0] != Self::LENGTH {
210 return Err(KnxError::InvalidFrame("invalid HPAI length"));
211 }
212
213 let protocol = HostProtocol::try_from(input[1])?;
214 let address = [input[2], input[3], input[4], input[5]];
215 let port = u16::from_be_bytes([input[6], input[7]]);
216
217 Ok((
218 Self::new(protocol, address, port),
219 &input[Self::LENGTH as usize..],
220 ))
221 }
222
223 #[cfg(feature = "std")]
224 pub fn encode(self, out: &mut std::vec::Vec<u8>) -> Result<()> {
225 out.extend_from_slice(&[
226 Self::LENGTH,
227 self.protocol.as_u8(),
228 self.address[0],
229 self.address[1],
230 self.address[2],
231 self.address[3],
232 (self.port >> 8) as u8,
233 self.port as u8,
234 ]);
235 Ok(())
236 }
237}
238
239#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
241pub struct ConnectionHeader {
242 channel_id: u8,
243 sequence_counter: u8,
244 status: u8,
245}
246
247impl ConnectionHeader {
248 pub const LENGTH: u8 = 0x04;
249
250 pub const fn new(channel_id: u8, sequence_counter: u8, status: u8) -> Self {
251 Self {
252 channel_id,
253 sequence_counter,
254 status,
255 }
256 }
257
258 pub const fn channel_id(self) -> u8 {
259 self.channel_id
260 }
261
262 pub const fn sequence_counter(self) -> u8 {
263 self.sequence_counter
264 }
265
266 pub const fn status(self) -> u8 {
267 self.status
268 }
269
270 pub fn decode(input: &[u8]) -> Result<(Self, &[u8])> {
271 if input.len() < Self::LENGTH as usize {
272 return Err(KnxError::BufferTooShort {
273 needed: Self::LENGTH as usize,
274 actual: input.len(),
275 });
276 }
277 if input[0] != Self::LENGTH {
278 return Err(KnxError::InvalidFrame("invalid connection header length"));
279 }
280
281 Ok((
282 Self::new(input[1], input[2], input[3]),
283 &input[Self::LENGTH as usize..],
284 ))
285 }
286
287 #[cfg(feature = "std")]
288 pub fn encode(self, out: &mut std::vec::Vec<u8>) -> Result<()> {
289 out.extend_from_slice(&[
290 Self::LENGTH,
291 self.channel_id,
292 self.sequence_counter,
293 self.status,
294 ]);
295 Ok(())
296 }
297}