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