use super::super::idr::translate_any_kva;
use super::{
AccessorCtx, BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH,
BPF_MAP_TYPE_PERCPU_HASH, BpfMapInfo,
};
pub(super) const HTAB_ITER_MAX: usize = 1_000_000;
pub(super) const HTAB_BUCKETS_MAX: u32 = 1 << 16;
pub(super) fn iter_htab_entries(
ctx: &AccessorCtx<'_>,
map: &BpfMapInfo,
) -> Vec<(Vec<u8>, Vec<u8>)> {
if map.map_type != BPF_MAP_TYPE_HASH && map.map_type != BPF_MAP_TYPE_LRU_HASH {
return Vec::new();
}
walk_htab(
ctx,
map,
|elem_pa, key_off_in_elem, value_off_in_elem, key_size, value_size, mem| {
let mut key_buf: Vec<u8> = Vec::with_capacity(key_size);
let key_slice =
unsafe { std::slice::from_raw_parts_mut(key_buf.as_mut_ptr(), key_size) };
let kn = mem.read_bytes(elem_pa + key_off_in_elem as u64, key_slice);
if kn != key_size {
return None;
}
unsafe {
key_buf.set_len(key_size);
}
let mut val_buf: Vec<u8> = Vec::with_capacity(value_size);
let val_slice =
unsafe { std::slice::from_raw_parts_mut(val_buf.as_mut_ptr(), value_size) };
let vn = mem.read_bytes(elem_pa + value_off_in_elem as u64, val_slice);
if vn != value_size {
return None;
}
unsafe {
val_buf.set_len(value_size);
}
Some((key_buf, val_buf))
},
)
}
pub(super) fn iter_percpu_htab_entries(
ctx: &AccessorCtx<'_>,
map: &BpfMapInfo,
per_cpu_offsets: &[u64],
) -> super::PerCpuHashEntries {
if map.map_type != BPF_MAP_TYPE_PERCPU_HASH && map.map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH {
return Vec::new();
}
let value_size = map.value_size as usize;
walk_htab(
ctx,
map,
|elem_pa, key_off_in_elem, value_off_in_elem, key_size, _value_size_unused, mem| {
let mut key_buf: Vec<u8> = Vec::with_capacity(key_size);
let key_slice =
unsafe { std::slice::from_raw_parts_mut(key_buf.as_mut_ptr(), key_size) };
let kn = mem.read_bytes(elem_pa + key_off_in_elem as u64, key_slice);
if kn != key_size {
return None;
}
unsafe {
key_buf.set_len(key_size);
}
let percpu_base = mem.read_u64(elem_pa, value_off_in_elem);
if percpu_base == 0 {
return Some((key_buf, Vec::new()));
}
let mut per_cpu = Vec::with_capacity(per_cpu_offsets.len());
for (cpu_index, &cpu_off) in per_cpu_offsets.iter().enumerate() {
if cpu_off == 0 && cpu_index > 0 {
per_cpu.push(None);
continue;
}
let cpu_kva = percpu_base.wrapping_add(cpu_off);
match translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
cpu_kva,
ctx.l5,
ctx.tcr_el1,
) {
Some(cpu_pa)
if cpu_pa
.checked_add(value_size as u64)
.is_some_and(|end| end <= ctx.mem.size()) =>
{
let mut buf: Vec<u8> = Vec::with_capacity(value_size);
let slice =
unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr(), value_size) };
let n = ctx.mem.read_bytes(cpu_pa, slice);
if n == value_size {
unsafe {
buf.set_len(value_size);
}
per_cpu.push(Some(buf));
} else {
per_cpu.push(None);
}
}
_ => per_cpu.push(None),
}
}
Some((key_buf, per_cpu))
},
)
}
fn walk_htab<T, F>(ctx: &AccessorCtx<'_>, map: &BpfMapInfo, mut extract: F) -> Vec<T>
where
F: FnMut(u64, usize, usize, usize, usize, &super::super::reader::GuestMem) -> Option<T>,
{
let Some(htab) = &ctx.offsets.htab_offsets else {
return Vec::new();
};
let htab_kva = map.map_kva;
let Some(htab_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
htab_kva,
ctx.l5,
ctx.tcr_el1,
) else {
return Vec::new();
};
let n_buckets = ctx.mem.read_u32(htab_pa, htab.htab_n_buckets);
let buckets_kva = ctx.mem.read_u64(htab_pa, htab.htab_buckets);
if n_buckets == 0 || n_buckets > HTAB_BUCKETS_MAX || buckets_kva == 0 {
return Vec::new();
}
let key_size = map.key_size as usize;
let value_size = map.value_size as usize;
if key_size > super::MAX_VALUE_SIZE || value_size > super::MAX_VALUE_SIZE {
return Vec::new();
}
let value_off_in_elem = htab.htab_elem_size_base + ((key_size + 7) & !7);
let key_off_in_elem = htab.htab_elem_size_base;
let mut out = Vec::new();
let mut total_visited = 0usize;
for i in 0..n_buckets {
let bucket_kva = buckets_kva + (i as u64) * (htab.bucket_size as u64);
let Some(bucket_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
bucket_kva,
ctx.l5,
ctx.tcr_el1,
) else {
continue;
};
let first_ptr = ctx
.mem
.read_u64(bucket_pa, htab.bucket_head + htab.hlist_nulls_head_first);
let mut node_ptr = first_ptr;
loop {
if node_ptr & 1 != 0 || node_ptr == 0 {
break;
}
total_visited += 1;
if total_visited > HTAB_ITER_MAX {
return out;
}
let elem_kva = node_ptr;
let Some(elem_pa) = translate_any_kva(
ctx.mem,
ctx.cr3_pa.0,
ctx.page_offset.0,
elem_kva,
ctx.l5,
ctx.tcr_el1,
) else {
break;
};
if let Some(item) = extract(
elem_pa,
key_off_in_elem,
value_off_in_elem,
key_size,
value_size,
ctx.mem,
) {
out.push(item);
}
node_ptr = ctx.mem.read_u64(elem_pa, htab.hlist_nulls_node_next);
}
}
out
}