use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
pub fn hash_flow(packet: &[u8], num_workers: usize) -> usize {
let ip_start: usize = if packet.len() > 14
&& ((packet[12] == 0x08 && packet[13] == 0x00)
|| (packet[12] == 0x86 && packet[13] == 0xDD))
{
14
} else {
0 };
let min_length = ip_start.saturating_add(40); if packet.len() < min_length {
return fallback_hash(packet, num_workers);
}
let ip_packet = &packet[ip_start..];
let version = (ip_packet[0] >> 4) & 0x0F;
match version {
4 => hash_ipv4_flow(ip_packet, num_workers),
6 => hash_ipv6_flow(ip_packet, num_workers),
_ => fallback_hash(packet, num_workers),
}
}
fn hash_ipv4_flow(ip_packet: &[u8], num_workers: usize) -> usize {
if ip_packet.len() < 20 {
return fallback_hash(ip_packet, num_workers);
}
let protocol = ip_packet[9];
if protocol != 6 {
let src_ip = &ip_packet[12..16];
return hash_bytes(src_ip).checked_rem(num_workers).unwrap_or(0);
}
let ihl = (ip_packet[0] & 0x0F) as usize;
let ip_header_len = ihl.saturating_mul(4);
if ip_packet.len() < ip_header_len.saturating_add(4) {
let src_ip = &ip_packet[12..16];
return hash_bytes(src_ip).checked_rem(num_workers).unwrap_or(0);
}
let src_ip = &ip_packet[12..16];
let dst_ip = &ip_packet[16..20];
let tcp_header = &ip_packet[ip_header_len..];
let src_port = u16::from_be_bytes([tcp_header[0], tcp_header[1]]);
let dst_port = u16::from_be_bytes([tcp_header[2], tcp_header[3]]);
let mut hasher = DefaultHasher::new();
src_ip.hash(&mut hasher);
dst_ip.hash(&mut hasher);
src_port.hash(&mut hasher);
dst_port.hash(&mut hasher);
(hasher.finish() as usize)
.checked_rem(num_workers)
.unwrap_or(0)
}
fn hash_ipv6_flow(ip_packet: &[u8], num_workers: usize) -> usize {
if ip_packet.len() < 40 {
return fallback_hash(ip_packet, num_workers);
}
let next_header = ip_packet[6];
if next_header != 6 {
let src_ip = &ip_packet[8..24];
return hash_bytes(src_ip).checked_rem(num_workers).unwrap_or(0);
}
if ip_packet.len() < 44 {
let src_ip = &ip_packet[8..24];
return hash_bytes(src_ip).checked_rem(num_workers).unwrap_or(0);
}
let src_ip = &ip_packet[8..24];
let dst_ip = &ip_packet[24..40];
let tcp_header = &ip_packet[40..];
let src_port = u16::from_be_bytes([tcp_header[0], tcp_header[1]]);
let dst_port = u16::from_be_bytes([tcp_header[2], tcp_header[3]]);
let mut hasher = DefaultHasher::new();
src_ip.hash(&mut hasher);
dst_ip.hash(&mut hasher);
src_port.hash(&mut hasher);
dst_port.hash(&mut hasher);
(hasher.finish() as usize)
.checked_rem(num_workers)
.unwrap_or(0)
}
fn hash_bytes(bytes: &[u8]) -> usize {
let mut hasher = DefaultHasher::new();
bytes.hash(&mut hasher);
hasher.finish() as usize
}
fn fallback_hash(packet: &[u8], num_workers: usize) -> usize {
hash_bytes(packet).checked_rem(num_workers).unwrap_or(0)
}