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