1use umsh_core::{OptionNumber, PacketHeader, PacketType, ParsedOptions};
23
24use crate::cache::DupCacheKey;
25
26pub fn forwarding_dup_key_parsed(header: &PacketHeader, frame: &[u8]) -> Option<DupCacheKey> {
32 if !header.packet_type().is_secure() {
33 return Some(DupCacheKey::Hash32(normalized_routable_hash32(
34 header, frame,
35 )));
36 }
37 let options = ParsedOptions::extract(frame, header.options_range.clone()).ok()?;
38 let mic = frame.get(header.mic_range.clone())?;
39 if mic.is_empty() || mic.len() > 16 {
40 return None;
41 }
42 let mut bytes = [0u8; 16];
43 bytes[..mic.len()].copy_from_slice(mic);
44 Some(DupCacheKey::Mic {
45 bytes,
46 len: mic.len() as u8,
47 route_retry: options.route_retry,
48 })
49}
50
51pub fn forwarding_dup_key(frame: &[u8]) -> Option<DupCacheKey> {
56 let header = PacketHeader::parse(frame).ok()?;
57 forwarding_dup_key_parsed(&header, frame)
58}
59
60fn normalized_routable_hash32(header: &PacketHeader, frame: &[u8]) -> u32 {
63 let mut hash = 0x811C_9DC5u32;
64
65 hash_u8(&mut hash, header.packet_type() as u8);
66 hash_u8(&mut hash, header.fcf.full_source() as u8);
67
68 if !header.options_range.is_empty() {
69 for entry in umsh_core::iter_options(frame, header.options_range.clone()) {
70 let Ok((number, value)) = entry else {
71 continue;
72 };
73 let option = OptionNumber::from(number);
74 if option.is_dynamic() {
75 continue;
76 }
77 hash_u16(&mut hash, number);
78 hash_u16(&mut hash, value.len() as u16);
79 hash_bytes(&mut hash, value);
80 }
81 }
82
83 match header.packet_type() {
84 PacketType::Broadcast => {
85 match header.source {
86 umsh_core::SourceAddrRef::Hint(hint) => hash_bytes(&mut hash, &hint.0),
87 umsh_core::SourceAddrRef::FullKeyAt { offset } => {
88 if let Some(key) = frame.get(offset..offset + 32) {
89 hash_bytes(&mut hash, key);
90 }
91 }
92 umsh_core::SourceAddrRef::Encrypted { offset, len } => {
93 if let Some(src) = frame.get(offset..offset + len) {
94 hash_bytes(&mut hash, src);
95 }
96 }
97 umsh_core::SourceAddrRef::None => {}
98 }
99 if let Some(payload) = frame.get(header.body_range.clone()) {
100 hash_bytes(&mut hash, payload);
101 }
102 }
103 PacketType::MacAck => {
104 if let Some(trailer) = frame.get(header.mic_range.clone()) {
108 hash_bytes(&mut hash, trailer);
109 }
110 }
111 _ => {
112 if let Some(bytes) = frame.get(header.body_range.clone()) {
113 hash_bytes(&mut hash, bytes);
114 }
115 }
116 }
117 hash
118}
119
120fn hash_u8(hash: &mut u32, value: u8) {
121 *hash ^= u32::from(value);
122 *hash = hash.wrapping_mul(0x0100_0193);
123}
124
125fn hash_u16(hash: &mut u32, value: u16) {
126 hash_bytes(hash, &value.to_be_bytes());
127}
128
129fn hash_bytes(hash: &mut u32, bytes: &[u8]) {
130 for byte in bytes {
131 hash_u8(hash, *byte);
132 }
133}