1use alloc::vec::Vec;
2use core::fmt;
3
4use crate::constants;
5use crate::hash;
6
7#[derive(Debug)]
8pub enum PacketError {
9 TooShort,
10 ExceedsMtu,
11 MissingTransportId,
12 InvalidHeaderType,
13 InvalidHopCount(u8),
15}
16
17impl fmt::Display for PacketError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 PacketError::TooShort => write!(f, "Packet too short"),
21 PacketError::ExceedsMtu => write!(f, "Packet exceeds MTU"),
22 PacketError::MissingTransportId => write!(f, "HEADER_2 requires transport_id"),
23 PacketError::InvalidHeaderType => write!(f, "Invalid header type"),
24 PacketError::InvalidHopCount(hops) => write!(f, "Invalid hop count: {}", hops),
25 }
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct PacketFlags {
35 pub header_type: u8,
36 pub context_flag: u8,
37 pub transport_type: u8,
38 pub destination_type: u8,
39 pub packet_type: u8,
40}
41
42impl PacketFlags {
43 pub fn pack(&self) -> u8 {
54 (self.header_type << 6)
55 | (self.context_flag << 5)
56 | (self.transport_type << 4)
57 | (self.destination_type << 2)
58 | self.packet_type
59 }
60
61 pub fn unpack(byte: u8) -> Self {
63 PacketFlags {
64 header_type: (byte & 0b01000000) >> 6,
65 context_flag: (byte & 0b00100000) >> 5,
66 transport_type: (byte & 0b00010000) >> 4,
67 destination_type: (byte & 0b00001100) >> 2,
68 packet_type: byte & 0b00000011,
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
78pub struct RawPacket {
79 pub flags: PacketFlags,
80 pub hops: u8,
81 pub transport_id: Option<[u8; 16]>,
82 pub destination_hash: [u8; 16],
83 pub context: u8,
84 pub data: Vec<u8>,
85 pub raw: Vec<u8>,
86 pub packet_hash: [u8; 32],
87 pub rssi: Option<i16>,
88 pub snr: Option<f32>,
89}
90
91impl RawPacket {
92 pub fn pack(
94 flags: PacketFlags,
95 hops: u8,
96 destination_hash: &[u8; 16],
97 transport_id: Option<&[u8; 16]>,
98 context: u8,
99 data: &[u8],
100 ) -> Result<Self, PacketError> {
101 Self::pack_with_max_mtu(
102 flags,
103 hops,
104 destination_hash,
105 transport_id,
106 context,
107 data,
108 constants::MTU,
109 )
110 }
111
112 pub fn pack_raw_with_hash(
114 flags: PacketFlags,
115 hops: u8,
116 destination_hash: &[u8; 16],
117 transport_id: Option<&[u8; 16]>,
118 context: u8,
119 data: &[u8],
120 ) -> Result<(Vec<u8>, [u8; 32]), PacketError> {
121 Self::pack_raw_with_hash_with_max_mtu(
122 flags,
123 hops,
124 destination_hash,
125 transport_id,
126 context,
127 data,
128 constants::MTU,
129 )
130 }
131
132 pub fn pack_with_max_mtu(
134 flags: PacketFlags,
135 hops: u8,
136 destination_hash: &[u8; 16],
137 transport_id: Option<&[u8; 16]>,
138 context: u8,
139 data: &[u8],
140 max_mtu: usize,
141 ) -> Result<Self, PacketError> {
142 let (raw, packet_hash) = Self::pack_raw_with_hash_with_max_mtu(
143 flags,
144 hops,
145 destination_hash,
146 transport_id,
147 context,
148 data,
149 max_mtu,
150 )?;
151
152 Ok(RawPacket {
153 flags,
154 hops,
155 transport_id: transport_id.copied(),
156 destination_hash: *destination_hash,
157 context,
158 data: data.to_vec(),
159 raw,
160 packet_hash,
161 rssi: None,
162 snr: None,
163 })
164 }
165
166 pub fn pack_raw_with_hash_with_max_mtu(
168 flags: PacketFlags,
169 hops: u8,
170 destination_hash: &[u8; 16],
171 transport_id: Option<&[u8; 16]>,
172 context: u8,
173 data: &[u8],
174 max_mtu: usize,
175 ) -> Result<(Vec<u8>, [u8; 32]), PacketError> {
176 if flags.header_type == constants::HEADER_2 && transport_id.is_none() {
177 return Err(PacketError::MissingTransportId);
178 }
179
180 let mut raw = Vec::new();
181 raw.push(flags.pack());
182 raw.push(hops);
183
184 if let Some(transport_id) = transport_id {
185 if flags.header_type == constants::HEADER_2 {
186 raw.extend_from_slice(transport_id);
187 }
188 }
189
190 raw.extend_from_slice(destination_hash);
191 raw.push(context);
192 raw.extend_from_slice(data);
193
194 if raw.len() > max_mtu {
195 return Err(PacketError::ExceedsMtu);
196 }
197
198 let packet_hash = hash::full_hash(&Self::compute_hashable_part(flags.header_type, &raw));
199 Ok((raw, packet_hash))
200 }
201
202 pub fn unpack(raw: &[u8]) -> Result<Self, PacketError> {
204 if raw.len() < constants::HEADER_MINSIZE {
205 return Err(PacketError::TooShort);
206 }
207
208 let flags = PacketFlags::unpack(raw[0]);
209 let hops = raw[1];
210 if hops >= constants::PATHFINDER_M {
211 return Err(PacketError::InvalidHopCount(hops));
212 }
213
214 let dst_len = constants::TRUNCATED_HASHLENGTH / 8; if flags.header_type == constants::HEADER_2 {
217 let min_len = 2 + dst_len * 2 + 1;
219 if raw.len() < min_len {
220 return Err(PacketError::TooShort);
221 }
222
223 let mut transport_id = [0u8; 16];
224 transport_id.copy_from_slice(&raw[2..2 + dst_len]);
225
226 let mut destination_hash = [0u8; 16];
227 destination_hash.copy_from_slice(&raw[2 + dst_len..2 + 2 * dst_len]);
228
229 let context = raw[2 + 2 * dst_len];
230 let data = raw[2 + 2 * dst_len + 1..].to_vec();
231
232 let packet_hash = hash::full_hash(&Self::compute_hashable_part(flags.header_type, raw));
233
234 Ok(RawPacket {
235 flags,
236 hops,
237 transport_id: Some(transport_id),
238 destination_hash,
239 context,
240 data,
241 raw: raw.to_vec(),
242 packet_hash,
243 rssi: None,
244 snr: None,
245 })
246 } else if flags.header_type == constants::HEADER_1 {
247 let min_len = 2 + dst_len + 1;
249 if raw.len() < min_len {
250 return Err(PacketError::TooShort);
251 }
252
253 let mut destination_hash = [0u8; 16];
254 destination_hash.copy_from_slice(&raw[2..2 + dst_len]);
255
256 let context = raw[2 + dst_len];
257 let data = raw[2 + dst_len + 1..].to_vec();
258
259 let packet_hash = hash::full_hash(&Self::compute_hashable_part(flags.header_type, raw));
260
261 Ok(RawPacket {
262 flags,
263 hops,
264 transport_id: None,
265 destination_hash,
266 context,
267 data,
268 raw: raw.to_vec(),
269 packet_hash,
270 rssi: None,
271 snr: None,
272 })
273 } else {
274 Err(PacketError::InvalidHeaderType)
275 }
276 }
277
278 pub fn get_hashable_part(&self) -> Vec<u8> {
285 Self::compute_hashable_part(self.flags.header_type, &self.raw)
286 }
287
288 fn compute_hashable_part(header_type: u8, raw: &[u8]) -> Vec<u8> {
289 let mut hashable = Vec::new();
290 hashable.push(raw[0] & 0b00001111);
291 if header_type == constants::HEADER_2 {
292 hashable.extend_from_slice(&raw[(constants::TRUNCATED_HASHLENGTH / 8 + 2)..]);
294 } else {
295 hashable.extend_from_slice(&raw[2..]);
296 }
297 hashable
298 }
299
300 pub fn get_hash(&self) -> [u8; 32] {
302 self.packet_hash
303 }
304
305 pub fn get_truncated_hash(&self) -> [u8; 16] {
307 let mut result = [0u8; 16];
308 result.copy_from_slice(&self.packet_hash[..16]);
309 result
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_flags_pack_header1_data_single_broadcast() {
319 let flags = PacketFlags {
320 header_type: constants::HEADER_1,
321 context_flag: constants::FLAG_UNSET,
322 transport_type: constants::TRANSPORT_BROADCAST,
323 destination_type: constants::DESTINATION_SINGLE,
324 packet_type: constants::PACKET_TYPE_DATA,
325 };
326 assert_eq!(flags.pack(), 0x00);
327 }
328
329 #[test]
330 fn test_flags_pack_header2_announce_single_transport() {
331 let flags = PacketFlags {
332 header_type: constants::HEADER_2,
333 context_flag: constants::FLAG_UNSET,
334 transport_type: constants::TRANSPORT_TRANSPORT,
335 destination_type: constants::DESTINATION_SINGLE,
336 packet_type: constants::PACKET_TYPE_ANNOUNCE,
337 };
338 assert_eq!(flags.pack(), 0x51);
340 }
341
342 #[test]
343 fn test_flags_roundtrip() {
344 for byte in 0..=0x7Fu8 {
345 let flags = PacketFlags::unpack(byte);
346 assert_eq!(flags.pack(), byte);
347 }
348 }
349
350 #[test]
351 fn test_pack_header1() {
352 let dest_hash = [0xAA; 16];
353 let data = b"hello";
354 let flags = PacketFlags {
355 header_type: constants::HEADER_1,
356 context_flag: constants::FLAG_UNSET,
357 transport_type: constants::TRANSPORT_BROADCAST,
358 destination_type: constants::DESTINATION_SINGLE,
359 packet_type: constants::PACKET_TYPE_DATA,
360 };
361
362 let pkt =
363 RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, data).unwrap();
364
365 assert_eq!(pkt.raw.len(), 24);
367 assert_eq!(pkt.raw[0], 0x00); assert_eq!(pkt.raw[1], 0x00); assert_eq!(&pkt.raw[2..18], &dest_hash); assert_eq!(pkt.raw[18], 0x00); assert_eq!(&pkt.raw[19..], b"hello"); }
373
374 #[test]
375 fn test_pack_header2() {
376 let dest_hash = [0xAA; 16];
377 let transport_id = [0xBB; 16];
378 let data = b"world";
379 let flags = PacketFlags {
380 header_type: constants::HEADER_2,
381 context_flag: constants::FLAG_UNSET,
382 transport_type: constants::TRANSPORT_TRANSPORT,
383 destination_type: constants::DESTINATION_SINGLE,
384 packet_type: constants::PACKET_TYPE_ANNOUNCE,
385 };
386
387 let pkt = RawPacket::pack(
388 flags,
389 3,
390 &dest_hash,
391 Some(&transport_id),
392 constants::CONTEXT_NONE,
393 data,
394 )
395 .unwrap();
396
397 assert_eq!(pkt.raw.len(), 40);
399 assert_eq!(pkt.raw[0], flags.pack());
400 assert_eq!(pkt.raw[1], 3);
401 assert_eq!(&pkt.raw[2..18], &transport_id);
402 assert_eq!(&pkt.raw[18..34], &dest_hash);
403 assert_eq!(pkt.raw[34], 0x00);
404 assert_eq!(&pkt.raw[35..], b"world");
405 }
406
407 #[test]
408 fn test_unpack_roundtrip_header1() {
409 let dest_hash = [0x11; 16];
410 let data = b"test data";
411 let flags = PacketFlags {
412 header_type: constants::HEADER_1,
413 context_flag: constants::FLAG_UNSET,
414 transport_type: constants::TRANSPORT_BROADCAST,
415 destination_type: constants::DESTINATION_SINGLE,
416 packet_type: constants::PACKET_TYPE_DATA,
417 };
418
419 let pkt = RawPacket::pack(
420 flags,
421 5,
422 &dest_hash,
423 None,
424 constants::CONTEXT_RESOURCE,
425 data,
426 )
427 .unwrap();
428 let unpacked = RawPacket::unpack(&pkt.raw).unwrap();
429
430 assert_eq!(unpacked.flags, flags);
431 assert_eq!(unpacked.hops, 5);
432 assert!(unpacked.transport_id.is_none());
433 assert_eq!(unpacked.destination_hash, dest_hash);
434 assert_eq!(unpacked.context, constants::CONTEXT_RESOURCE);
435 assert_eq!(unpacked.data, data);
436 assert_eq!(unpacked.packet_hash, pkt.packet_hash);
437 }
438
439 #[test]
440 fn test_unpack_roundtrip_header2() {
441 let dest_hash = [0x22; 16];
442 let transport_id = [0x33; 16];
443 let data = b"transported";
444 let flags = PacketFlags {
445 header_type: constants::HEADER_2,
446 context_flag: constants::FLAG_SET,
447 transport_type: constants::TRANSPORT_TRANSPORT,
448 destination_type: constants::DESTINATION_SINGLE,
449 packet_type: constants::PACKET_TYPE_ANNOUNCE,
450 };
451
452 let pkt = RawPacket::pack(
453 flags,
454 2,
455 &dest_hash,
456 Some(&transport_id),
457 constants::CONTEXT_NONE,
458 data,
459 )
460 .unwrap();
461 let unpacked = RawPacket::unpack(&pkt.raw).unwrap();
462
463 assert_eq!(unpacked.flags, flags);
464 assert_eq!(unpacked.hops, 2);
465 assert_eq!(unpacked.transport_id.unwrap(), transport_id);
466 assert_eq!(unpacked.destination_hash, dest_hash);
467 assert_eq!(unpacked.context, constants::CONTEXT_NONE);
468 assert_eq!(unpacked.data, data);
469 assert_eq!(unpacked.packet_hash, pkt.packet_hash);
470 }
471
472 #[test]
473 fn test_unpack_too_short() {
474 assert!(RawPacket::unpack(&[0x00; 5]).is_err());
475 }
476
477 #[test]
478 fn unpack_enforces_pathfinder_hop_boundary_but_pack_remains_permissive() {
479 let flags = PacketFlags {
480 header_type: constants::HEADER_1,
481 context_flag: constants::FLAG_UNSET,
482 transport_type: constants::TRANSPORT_BROADCAST,
483 destination_type: constants::DESTINATION_SINGLE,
484 packet_type: constants::PACKET_TYPE_DATA,
485 };
486 let accepted = RawPacket::pack(flags, 127, &[1; 16], None, 0, b"x").unwrap();
487 assert_eq!(RawPacket::unpack(&accepted.raw).unwrap().hops, 127);
488
489 for hops in [128, 255] {
490 let packed = RawPacket::pack(flags, hops, &[1; 16], None, 0, b"x").unwrap();
491 assert!(matches!(
492 RawPacket::unpack(&packed.raw),
493 Err(PacketError::InvalidHopCount(value)) if value == hops
494 ));
495 }
496 }
497
498 #[test]
499 fn test_pack_exceeds_mtu() {
500 let flags = PacketFlags {
501 header_type: constants::HEADER_1,
502 context_flag: constants::FLAG_UNSET,
503 transport_type: constants::TRANSPORT_BROADCAST,
504 destination_type: constants::DESTINATION_SINGLE,
505 packet_type: constants::PACKET_TYPE_DATA,
506 };
507 let data = [0u8; 500]; let result = RawPacket::pack(flags, 0, &[0; 16], None, 0, &data);
509 assert!(result.is_err());
510 }
511
512 #[test]
513 fn test_header2_missing_transport_id() {
514 let flags = PacketFlags {
515 header_type: constants::HEADER_2,
516 context_flag: constants::FLAG_UNSET,
517 transport_type: constants::TRANSPORT_TRANSPORT,
518 destination_type: constants::DESTINATION_SINGLE,
519 packet_type: constants::PACKET_TYPE_ANNOUNCE,
520 };
521 let result = RawPacket::pack(flags, 0, &[0; 16], None, 0, b"data");
522 assert!(result.is_err());
523 }
524
525 #[test]
526 fn test_hashable_part_header1_masks_upper_flags() {
527 let dest_hash = [0xCC; 16];
528 let flags = PacketFlags {
529 header_type: constants::HEADER_1,
530 context_flag: constants::FLAG_SET,
531 transport_type: constants::TRANSPORT_BROADCAST,
532 destination_type: constants::DESTINATION_SINGLE,
533 packet_type: constants::PACKET_TYPE_DATA,
534 };
535
536 let pkt =
537 RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, b"test").unwrap();
538 let hashable = pkt.get_hashable_part();
539
540 assert_eq!(hashable[0], pkt.raw[0] & 0x0F);
542 assert_eq!(&hashable[1..], &pkt.raw[2..]);
544 }
545
546 #[test]
547 fn test_hashable_part_header2_strips_transport_id() {
548 let dest_hash = [0xDD; 16];
549 let transport_id = [0xEE; 16];
550 let flags = PacketFlags {
551 header_type: constants::HEADER_2,
552 context_flag: constants::FLAG_UNSET,
553 transport_type: constants::TRANSPORT_TRANSPORT,
554 destination_type: constants::DESTINATION_SINGLE,
555 packet_type: constants::PACKET_TYPE_ANNOUNCE,
556 };
557
558 let pkt = RawPacket::pack(
559 flags,
560 0,
561 &dest_hash,
562 Some(&transport_id),
563 constants::CONTEXT_NONE,
564 b"data",
565 )
566 .unwrap();
567 let hashable = pkt.get_hashable_part();
568
569 assert_eq!(hashable[0], pkt.raw[0] & 0x0F);
571 assert_eq!(&hashable[1..], &pkt.raw[18..]);
573 }
574}