1#![allow(non_upper_case_globals)]
6
7mod array;
8mod buffer;
9mod hashmap;
10mod lock;
11mod lpm_trie;
12mod ring_buffer;
13mod vmar;
14
15pub use ring_buffer::RINGBUF_SIGNAL;
16pub(crate) use ring_buffer::{RingBuffer, RingBufferWakeupPolicy};
17
18use ebpf::{BpfValue, EbpfBufferPtr, MapFlags, MapReference, MapSchema};
19use fidl_fuchsia_ebpf as febpf;
20use inspect_stubs::track_stub;
21use linux_uapi::{
22 BPF_EXIST, BPF_NOEXIST, bpf_map_type, bpf_map_type_BPF_MAP_TYPE_ARENA,
23 bpf_map_type_BPF_MAP_TYPE_ARRAY, bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS,
24 bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER, bpf_map_type_BPF_MAP_TYPE_CGROUP_ARRAY,
25 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE,
26 bpf_map_type_BPF_MAP_TYPE_CPUMAP, bpf_map_type_BPF_MAP_TYPE_DEVMAP,
27 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH, bpf_map_type_BPF_MAP_TYPE_HASH,
28 bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS, bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE,
29 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE, bpf_map_type_BPF_MAP_TYPE_LRU_HASH,
30 bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH, bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY,
31 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH,
32 bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY, bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY,
33 bpf_map_type_BPF_MAP_TYPE_QUEUE, bpf_map_type_BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
34 bpf_map_type_BPF_MAP_TYPE_RINGBUF, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
35 bpf_map_type_BPF_MAP_TYPE_SOCKHASH, bpf_map_type_BPF_MAP_TYPE_SOCKMAP,
36 bpf_map_type_BPF_MAP_TYPE_STACK, bpf_map_type_BPF_MAP_TYPE_STACK_TRACE,
37 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS, bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE,
38 bpf_map_type_BPF_MAP_TYPE_UNSPEC, bpf_map_type_BPF_MAP_TYPE_USER_RINGBUF,
39 bpf_map_type_BPF_MAP_TYPE_XSKMAP,
40};
41use std::fmt::Debug;
42use std::ops::Deref;
43use std::pin::Pin;
44use std::sync::Arc;
45use zx::{AsHandleRef, HandleBased};
46
47use crate::maps::buffer::VmoOrName;
48
49#[derive(Debug, Eq, PartialEq)]
50pub enum MapError {
51 InvalidParam,
53
54 InvalidKey,
56
57 EntryExists,
59
60 SizeLimit,
62
63 NoMemory,
65
66 InvalidVmo,
68
69 MapTypeNotSupported,
71
72 NotSupported,
74
75 Internal,
77}
78const SUPPORTED_FLAGS: MapFlags = MapFlags::NoPrealloc
79 .union(MapFlags::SyscallReadOnly)
80 .union(MapFlags::SyscallWriteOnly)
81 .union(MapFlags::Mmapable);
82
83fn map_flags_from_fidl(flags: febpf::MapFlags) -> MapFlags {
84 let mut r = MapFlags::empty();
85 if flags.contains(febpf::MapFlags::NO_PREALLOC) {
86 r = r | MapFlags::NoPrealloc;
87 }
88 if flags.contains(febpf::MapFlags::SYSCALL_READ_ONLY) {
89 r = r | MapFlags::SyscallReadOnly;
90 }
91 if flags.contains(febpf::MapFlags::SYSCALL_WRITE_ONLY) {
92 r = r | MapFlags::SyscallWriteOnly;
93 }
94 if flags.contains(febpf::MapFlags::MMAPABLE) {
95 r = r | MapFlags::Mmapable;
96 }
97 r
98}
99
100fn map_flags_to_fidl(flags: MapFlags) -> Result<febpf::MapFlags, MapError> {
101 if flags.contains(!SUPPORTED_FLAGS) {
102 return Err(MapError::NotSupported);
103 }
104
105 let mut r = febpf::MapFlags::empty();
106 if flags.contains(MapFlags::NoPrealloc) {
107 r = r | febpf::MapFlags::NO_PREALLOC;
108 }
109 if flags.contains(MapFlags::SyscallReadOnly) {
110 r = r | febpf::MapFlags::SYSCALL_READ_ONLY;
111 }
112 if flags.contains(MapFlags::SyscallWriteOnly) {
113 r = r | febpf::MapFlags::SYSCALL_WRITE_ONLY;
114 }
115 if flags.contains(MapFlags::Mmapable) {
116 r = r | febpf::MapFlags::MMAPABLE;
117 }
118 Ok(r)
119}
120
121fn validate_map_flags(schema: &MapSchema) -> Result<(), MapError> {
122 let flags = schema.flags;
123 if flags.contains(!SUPPORTED_FLAGS) {
124 return Err(MapError::InvalidParam);
125 }
126
127 if flags.contains(MapFlags::SyscallReadOnly) && flags.contains(MapFlags::SyscallWriteOnly) {
129 return Err(MapError::InvalidParam);
130 }
131
132 if flags.contains(MapFlags::Mmapable) && schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
134 return Err(MapError::InvalidParam);
135 }
136
137 Ok(())
138}
139
140trait MapImpl: Send + Sync + Debug {
141 fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>>;
142 fn update(&self, key: MapKey, value: &[u8], flags: u64) -> Result<(), MapError>;
143 fn delete(&self, key: &[u8]) -> Result<(), MapError>;
144 fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError>;
145 fn vmo(&self) -> &Arc<zx::Vmo>;
146
147 fn can_read(&self) -> Option<bool> {
150 None
151 }
152
153 fn ringbuf_reserve(&self, _size: u32, _flags: u64) -> Result<usize, MapError> {
154 Err(MapError::InvalidParam)
155 }
156}
157
158#[derive(Debug)]
160pub struct Map {
161 pub schema: MapSchema,
162
163 map_impl: Pin<Box<dyn MapImpl + Sync>>,
166}
167
168#[derive(Debug, Clone)]
171pub struct PinnedMap(Pin<Arc<Map>>);
172
173impl Deref for PinnedMap {
174 type Target = Map;
175 fn deref(&self) -> &Self::Target {
176 self.0.deref()
177 }
178}
179
180impl MapReference for PinnedMap {
181 fn schema(&self) -> &MapSchema {
182 &self.0.schema
183 }
184
185 fn as_bpf_value(&self) -> BpfValue {
186 BpfValue::from(self.deref() as *const Map)
187 }
188
189 fn get_data_ptr(&self) -> Option<BpfValue> {
190 assert!(self.0.schema.map_type == bpf_map_type_BPF_MAP_TYPE_ARRAY);
191
192 let key = [0u8; 4];
193 self.0.lookup(&key).map(|v| BpfValue::from(v.ptr().raw_ptr()))
194 }
195}
196
197pub type MapKey = smallvec::SmallVec<[u8; 16]>;
199
200const BASE_MAP_RIGHTS: zx::Rights = zx::Rights::READ
205 .union(zx::Rights::WRITE)
206 .union(zx::Rights::MAP)
207 .union(zx::Rights::SIGNAL)
208 .union(zx::Rights::WAIT);
209const SHARED_MAP_RIGHTS: zx::Rights = BASE_MAP_RIGHTS.union(zx::Rights::TRANSFER);
213
214impl Map {
215 pub fn new(schema: MapSchema, name: &str) -> Result<PinnedMap, MapError> {
216 validate_map_flags(&schema)?;
217 let map_impl = create_map_impl(&schema, name.to_string())?;
218 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
219 }
220
221 pub fn new_shared(shared: febpf::Map) -> Result<PinnedMap, MapError> {
222 let febpf::Map { schema: Some(fidl_schema), vmo: Some(vmo), .. } = shared else {
223 return Err(MapError::InvalidParam);
224 };
225
226 let vmo_info = vmo.basic_info().map_err(|_| MapError::InvalidVmo)?;
228 if !vmo_info.rights.contains(BASE_MAP_RIGHTS) {
229 return Err(MapError::InvalidVmo);
230 }
231
232 let schema = MapSchema {
233 map_type: fidl_map_type_to_bpf_map_type(fidl_schema.type_),
234 key_size: fidl_schema.key_size,
235 value_size: fidl_schema.value_size,
236 max_entries: fidl_schema.max_entries,
237 flags: map_flags_from_fidl(fidl_schema.flags),
238 };
239
240 let map_impl = create_map_impl(&schema, vmo)?;
241 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
242 }
243
244 pub fn share(&self) -> Result<febpf::Map, MapError> {
245 Ok(febpf::Map {
246 schema: Some(febpf::MapSchema {
247 type_: bpf_map_type_to_fidl_map_type(self.schema.map_type),
248 key_size: self.schema.key_size,
249 value_size: self.schema.value_size,
250 max_entries: self.schema.max_entries,
251 flags: map_flags_to_fidl(self.schema.flags)?,
252 }),
253 vmo: Some(
254 self.map_impl
255 .vmo()
256 .duplicate_handle(SHARED_MAP_RIGHTS)
257 .map_err(|_| MapError::Internal)?,
258 ),
259 ..Default::default()
260 })
261 }
262
263 pub fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>> {
264 self.map_impl.lookup(key)
265 }
266
267 pub fn load(&self, key: &[u8]) -> Option<Vec<u8>> {
268 let mut result = self.lookup(key)?.ptr().load();
269
270 result.resize(self.schema.value_size as usize, 0);
272
273 Some(result)
274 }
275
276 pub fn update(&self, key: MapKey, value: &[u8], flags: u64) -> Result<(), MapError> {
277 if flags & (BPF_EXIST as u64) > 0 && flags & (BPF_NOEXIST as u64) > 0 {
278 return Err(MapError::InvalidParam);
279 }
280
281 self.map_impl.update(key, value, flags)
282 }
283
284 pub fn delete(&self, key: &[u8]) -> Result<(), MapError> {
285 self.map_impl.delete(key)
286 }
287
288 pub fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError> {
289 self.map_impl.get_next_key(key)
290 }
291
292 pub fn vmo(&self) -> &Arc<zx::Vmo> {
293 self.map_impl.vmo()
294 }
295
296 pub fn can_read(&self) -> Option<bool> {
297 self.map_impl.can_read()
298 }
299
300 pub fn ringbuf_reserve(&self, size: u32, flags: u64) -> Result<usize, MapError> {
301 self.map_impl.ringbuf_reserve(size, flags)
302 }
303
304 pub fn uses_locks(&self) -> bool {
305 self.schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY
306 }
307}
308
309pub enum MapValueRef<'a> {
310 PlainRef(EbpfBufferPtr<'a>),
311 HashMapRef(hashmap::HashMapEntryRef<'a>),
312 LpmTrieRef(lpm_trie::LpmTrieEntryRef<'a>),
313}
314
315impl<'a> MapValueRef<'a> {
316 fn new(buf: EbpfBufferPtr<'a>) -> Self {
317 Self::PlainRef(buf)
318 }
319
320 fn new_from_hashmap(hash_map_ref: hashmap::HashMapEntryRef<'a>) -> Self {
321 Self::HashMapRef(hash_map_ref)
322 }
323
324 fn new_from_lpm_trie(lpm_trie_ref: lpm_trie::LpmTrieEntryRef<'a>) -> Self {
325 Self::LpmTrieRef(lpm_trie_ref)
326 }
327
328 pub fn is_ref_counted(&self) -> bool {
329 match self {
330 Self::PlainRef(_) => false,
331 Self::HashMapRef(_) | Self::LpmTrieRef(_) => true,
332 }
333 }
334
335 pub fn ptr(&self) -> EbpfBufferPtr<'a> {
336 match self {
337 Self::PlainRef(buf) => *buf,
338 Self::HashMapRef(hash_map_ref) => hash_map_ref.ptr(),
339 Self::LpmTrieRef(lpm_trie_ref) => lpm_trie_ref.ptr(),
340 }
341 }
342}
343
344fn create_map_impl(
345 schema: &MapSchema,
346 vmo: impl Into<VmoOrName>,
347) -> Result<Pin<Box<dyn MapImpl>>, MapError> {
348 match schema.map_type {
351 bpf_map_type_BPF_MAP_TYPE_ARRAY => Ok(Box::pin(array::Array::new(schema, vmo)?)),
353 bpf_map_type_BPF_MAP_TYPE_HASH => Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?)),
354 bpf_map_type_BPF_MAP_TYPE_RINGBUF => Ok(ring_buffer::RingBuffer::new(schema, vmo)?),
355 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => Ok(Box::pin(lpm_trie::LpmTrie::new(schema, vmo)?)),
356
357 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => {
360 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP_HASH");
361 let schema =
364 MapSchema { flags: schema.flags.difference(MapFlags::ProgReadOnly), ..*schema };
365 Ok(Box::pin(hashmap::HashMap::new(&schema, vmo)?))
366 }
367 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => {
368 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_HASH");
369 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
370 }
371 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => {
372 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_ARRAY");
373 Ok(Box::pin(array::Array::new(schema, vmo)?))
374 }
375 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => {
376 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SK_STORAGE");
377 Ok(Box::pin(array::Array::new(&MapSchema { max_entries: 1, ..*schema }, vmo)?))
378 }
379 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => {
380 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_HASH");
381 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
382 }
383 bpf_map_type_BPF_MAP_TYPE_UNSPEC => {
387 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_UNSPEC");
388 Err(MapError::MapTypeNotSupported)
389 }
390 bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY => {
391 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PROG_ARRAY");
392 Err(MapError::MapTypeNotSupported)
393 }
394 bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY => {
395 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERF_EVENT_ARRAY");
396 Err(MapError::MapTypeNotSupported)
397 }
398 bpf_map_type_BPF_MAP_TYPE_STACK_TRACE => {
399 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK_TRACE");
400 Err(MapError::MapTypeNotSupported)
401 }
402 bpf_map_type_BPF_MAP_TYPE_CGROUP_ARRAY => {
403 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_ARRAY");
404 Err(MapError::MapTypeNotSupported)
405 }
406 bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH => {
407 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_PERCPU_HASH");
408 Err(MapError::MapTypeNotSupported)
409 }
410 bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS => {
411 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARRAY_OF_MAPS");
412 Err(MapError::MapTypeNotSupported)
413 }
414 bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS => {
415 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_HASH_OF_MAPS");
416 Err(MapError::MapTypeNotSupported)
417 }
418 bpf_map_type_BPF_MAP_TYPE_DEVMAP => {
419 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP");
420 Err(MapError::MapTypeNotSupported)
421 }
422 bpf_map_type_BPF_MAP_TYPE_SOCKMAP => {
423 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKMAP");
424 Err(MapError::MapTypeNotSupported)
425 }
426 bpf_map_type_BPF_MAP_TYPE_CPUMAP => {
427 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CPUMAP");
428 Err(MapError::MapTypeNotSupported)
429 }
430 bpf_map_type_BPF_MAP_TYPE_XSKMAP => {
431 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_XSKMAP");
432 Err(MapError::MapTypeNotSupported)
433 }
434 bpf_map_type_BPF_MAP_TYPE_SOCKHASH => {
435 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKHASH");
436 Err(MapError::MapTypeNotSupported)
437 }
438 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE => {
439 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_STORAGE");
440 Err(MapError::MapTypeNotSupported)
441 }
442 bpf_map_type_BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => {
443 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY");
444 Err(MapError::MapTypeNotSupported)
445 }
446 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE => {
447 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE");
448 Err(MapError::MapTypeNotSupported)
449 }
450 bpf_map_type_BPF_MAP_TYPE_QUEUE => {
451 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_QUEUE");
452 Err(MapError::MapTypeNotSupported)
453 }
454 bpf_map_type_BPF_MAP_TYPE_STACK => {
455 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK");
456 Err(MapError::MapTypeNotSupported)
457 }
458 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS => {
459 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STRUCT_OPS");
460 Err(MapError::MapTypeNotSupported)
461 }
462 bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE => {
463 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_INODE_STORAGE");
464 Err(MapError::MapTypeNotSupported)
465 }
466 bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE => {
467 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_TASK_STORAGE");
468 Err(MapError::MapTypeNotSupported)
469 }
470 bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER => {
471 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_BLOOM_FILTER");
472 Err(MapError::MapTypeNotSupported)
473 }
474 bpf_map_type_BPF_MAP_TYPE_USER_RINGBUF => {
475 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_USER_RINGBUF");
476 Err(MapError::MapTypeNotSupported)
477 }
478 bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE => {
479 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGRP_STORAGE");
480 Err(MapError::MapTypeNotSupported)
481 }
482 bpf_map_type_BPF_MAP_TYPE_ARENA => {
483 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARENA");
484 Err(MapError::MapTypeNotSupported)
485 }
486 _ => {
487 track_stub!(
488 TODO("https://fxbug.dev/323847465"),
489 "unknown bpf map type",
490 schema.map_type
491 );
492 Err(MapError::InvalidParam)
493 }
494 }
495}
496
497pub fn compute_map_storage_size(schema: &MapSchema) -> Result<usize, MapError> {
498 schema.value_size.checked_mul(schema.max_entries).map(|v| v as usize).ok_or(MapError::NoMemory)
499}
500
501fn bpf_map_type_to_fidl_map_type(map_type: bpf_map_type) -> febpf::MapType {
503 match map_type {
504 bpf_map_type_BPF_MAP_TYPE_ARRAY => febpf::MapType::Array,
505 bpf_map_type_BPF_MAP_TYPE_HASH => febpf::MapType::HashMap,
506 bpf_map_type_BPF_MAP_TYPE_RINGBUF => febpf::MapType::RingBuffer,
507 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => febpf::MapType::PercpuArray,
508 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => febpf::MapType::PercpuHash,
509 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => febpf::MapType::DevmapHash,
510 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => febpf::MapType::LpmTrie,
511 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => febpf::MapType::LruHash,
512 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => febpf::MapType::SkStorage,
513 _ =>
514 {
516 unreachable!("unsupported map type {:?}", map_type)
517 }
518 }
519}
520
521fn fidl_map_type_to_bpf_map_type(map_type: febpf::MapType) -> bpf_map_type {
522 match map_type {
523 febpf::MapType::Array => bpf_map_type_BPF_MAP_TYPE_ARRAY,
524 febpf::MapType::HashMap => bpf_map_type_BPF_MAP_TYPE_HASH,
525 febpf::MapType::RingBuffer => bpf_map_type_BPF_MAP_TYPE_RINGBUF,
526 febpf::MapType::PercpuArray => bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY,
527 febpf::MapType::PercpuHash => bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH,
528 febpf::MapType::DevmapHash => bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH,
529 febpf::MapType::LpmTrie => bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
530 febpf::MapType::LruHash => bpf_map_type_BPF_MAP_TYPE_LRU_HASH,
531 febpf::MapType::SkStorage => bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
532 }
533}
534#[cfg(test)]
537mod test {
538 use super::*;
539
540 #[fuchsia::test]
541 fn test_sharing_array() {
542 let schema = MapSchema {
543 map_type: bpf_map_type_BPF_MAP_TYPE_ARRAY,
544 key_size: 4,
545 value_size: 4,
546 max_entries: 10,
547 flags: MapFlags::empty(),
548 };
549
550 let map1 = Map::new(schema, "test").unwrap();
552 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
553
554 let key = vec![0, 0, 0, 0];
556 let value = [0, 1, 2, 3];
557 map1.update(MapKey::from_vec(key.clone()), &value, 0).unwrap();
558 assert_eq!(&map2.load(&key).unwrap(), &value);
559 }
560
561 #[fuchsia::test]
562 fn test_sharing_hash_map() {
563 let schema = MapSchema {
564 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
565 key_size: 4,
566 value_size: 4,
567 max_entries: 10,
568 flags: MapFlags::empty(),
569 };
570
571 let map1 = Map::new(schema, "test").unwrap();
573 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
574
575 let key = vec![0, 0, 0, 0];
577 let value = [0, 1, 2, 3];
578 map1.update(MapKey::from_vec(key.clone()), &value, 0).unwrap();
579 assert_eq!(&map2.load(&key).unwrap(), &value);
580 }
581
582 #[fuchsia::test]
583 fn test_hash_map() {
584 let schema = MapSchema {
585 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
586 key_size: 5,
587 value_size: 25,
588 max_entries: 10000,
589 flags: MapFlags::empty(),
590 };
591
592 let get_key = |i| {
593 MapKey::from_vec(vec![
594 (i & 0xffusize) as u8,
595 0,
596 ((i >> 4) & 0xffusize) as u8,
597 0,
598 ((i >> 8) & 0xffusize) as u8,
599 ])
600 };
601 let get_value = |i, v| format!("--{:010} {:010}--", i, v).into_bytes();
602
603 let map = Map::new(schema, "test").unwrap();
604
605 for i in 0..10000 {
606 assert!(map.update(get_key(i), &get_value(i, 0), 0).is_ok());
607 }
608
609 assert_eq!(map.update(get_key(10001), &get_value(10001, 1), 0), Err(MapError::SizeLimit));
611
612 for i in 0..10000 {
613 assert_eq!(map.load(&get_key(i)), Some(get_value(i, 0)));
614 }
615
616 for i in 8000..9000 {
618 assert!(map.update(get_key(i), &get_value(i, 1), 0).is_ok());
619 }
620 for i in 8000..9000 {
621 assert_eq!(map.load(&get_key(i)), Some(get_value(i, 1)));
622 }
623
624 for i in 5000..10000 {
626 assert!(map.delete(&get_key(i)).is_ok());
627 }
628 for i in 5000..10000 {
629 assert_eq!(map.load(&get_key(i)), None);
630 }
631
632 for i in 10000..15000 {
634 assert!(map.update(get_key(i), &get_value(i, 2), 0).is_ok());
635 }
636
637 for i in 0..5000 {
638 assert_eq!(map.load(&get_key(i)), Some(get_value(i, 0)));
639 }
640 for i in 10000..15000 {
641 assert_eq!(map.load(&get_key(i)), Some(get_value(i, 2)));
642 }
643 }
644
645 #[fuchsia::test]
646 fn test_hash_map_update_direct() {
647 let schema = MapSchema {
648 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
649 key_size: 5,
650 value_size: 11,
651 max_entries: 10,
652 flags: MapFlags::empty(),
653 };
654
655 let map = Map::new(schema, "test").unwrap();
656 let key = MapKey::from_vec("12345".to_string().into_bytes());
657 let value = (0..11).collect::<Vec<u8>>();
658 assert!(map.update(key.clone(), &value, 0).is_ok());
659
660 let value_ref = map.lookup(&key).unwrap();
662 #[allow(
663 clippy::undocumented_unsafe_blocks,
664 reason = "Force documented unsafe blocks in Starnix"
665 )]
666 unsafe {
667 *value_ref.ptr().get_ptr::<u32>(0).unwrap().deref_mut() = 0xabacadae;
668 }
669
670 assert_eq!(map.load(&key), Some(vec![0xae, 0xad, 0xac, 0xab, 4, 5, 6, 7, 8, 9, 10]));
671 }
672
673 #[fuchsia::test]
674 fn test_hash_map_ref_counting() {
675 let schema = MapSchema {
676 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
677 key_size: 5,
678 value_size: 11,
679 max_entries: 2,
680 flags: MapFlags::empty(),
681 };
682
683 let map = Map::new(schema, "test").unwrap();
684 let key = MapKey::from_vec("12345".to_string().into_bytes());
685 let key2 = MapKey::from_vec("24122".to_string().into_bytes());
686 let value = (0..11).collect::<Vec<u8>>();
687 assert!(map.update(key.clone(), &value, 0).is_ok());
688 assert!(map.update(key2.clone(), &value, 0).is_ok());
689
690 let value_ref = map.lookup(&key).unwrap();
691
692 assert!(map.delete(&key).is_ok());
695 assert_eq!(map.update(key.clone(), &value, 0), Err(MapError::SizeLimit));
696 drop(value_ref);
697 assert!(map.update(key.clone(), &value, 0).is_ok());
698 }
699
700 #[fuchsia::test]
701 fn test_ringbug_sharing() {
702 let schema = MapSchema {
703 map_type: bpf_map_type_BPF_MAP_TYPE_RINGBUF,
704 key_size: 0,
705 value_size: 0,
706 max_entries: 4096 * 2,
707 flags: MapFlags::empty(),
708 };
709
710 let map = Map::new(schema, "test").unwrap();
711 map.ringbuf_reserve(8000, 0).expect("ringbuf_reserve failed");
712
713 let map2 = Map::new_shared(map.share().unwrap()).unwrap();
714
715 map2.ringbuf_reserve(2000, 0).expect_err("ringbuf_reserve expected to fail");
717 }
718
719 #[fuchsia::test]
721 fn test_all_maps_shareable() {
722 for map_type in 1..linux_uapi::bpf_map_type___MAX_BPF_MAP_TYPE {
723 let (key_size, value_size, max_entries, flags) = match map_type {
724 bpf_map_type_BPF_MAP_TYPE_RINGBUF => (0, 0, 4096, MapFlags::empty()),
725 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => (8, 4, 4096, MapFlags::NoPrealloc),
726 _ => (4, 4, 1, MapFlags::empty()),
727 };
728 let schema = MapSchema { map_type, key_size, value_size, max_entries, flags };
729
730 let map = match Map::new(schema, "test") {
731 Ok(map) => map,
732 Err(MapError::MapTypeNotSupported) => {
733 continue;
734 }
735 Err(e) => {
736 panic!("Failed to create map of type {:?}: {:?}", map_type, e);
737 }
738 };
739
740 let map_fidl = map.share().expect("Failed to share map");
741 let _: PinnedMap = Map::new_shared(map_fidl).expect("Failed to initialize shared map");
742 }
743 }
744}