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;
45
46use crate::maps::buffer::VmoOrName;
47
48#[derive(Debug, Eq, PartialEq)]
49pub enum MapError {
50 InvalidParam,
52
53 InvalidKey,
55
56 EntryExists,
58
59 SizeLimit,
61
62 NoMemory,
64
65 InvalidVmo,
67
68 MapTypeNotSupported,
70
71 NotSupported,
73
74 Internal,
76}
77const SUPPORTED_FLAGS: MapFlags = MapFlags::NoPrealloc
78 .union(MapFlags::SyscallReadOnly)
79 .union(MapFlags::SyscallWriteOnly)
80 .union(MapFlags::Mmapable)
81 .union(MapFlags::Clone);
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 if flags.contains(febpf::MapFlags::CLONE) {
98 r = r | MapFlags::Clone;
99 }
100 r
101}
102
103fn map_flags_to_fidl(flags: MapFlags) -> Result<febpf::MapFlags, MapError> {
104 if flags.contains(!SUPPORTED_FLAGS) {
105 return Err(MapError::NotSupported);
106 }
107
108 let mut r = febpf::MapFlags::empty();
109 if flags.contains(MapFlags::NoPrealloc) {
110 r = r | febpf::MapFlags::NO_PREALLOC;
111 }
112 if flags.contains(MapFlags::SyscallReadOnly) {
113 r = r | febpf::MapFlags::SYSCALL_READ_ONLY;
114 }
115 if flags.contains(MapFlags::SyscallWriteOnly) {
116 r = r | febpf::MapFlags::SYSCALL_WRITE_ONLY;
117 }
118 if flags.contains(MapFlags::Mmapable) {
119 r = r | febpf::MapFlags::MMAPABLE;
120 }
121 if flags.contains(MapFlags::Clone) {
122 r = r | febpf::MapFlags::CLONE;
123 }
124 Ok(r)
125}
126
127fn validate_map_flags(schema: &MapSchema) -> Result<(), MapError> {
128 let flags = schema.flags;
129 if flags.contains(!SUPPORTED_FLAGS) {
130 return Err(MapError::InvalidParam);
131 }
132
133 if flags.contains(MapFlags::SyscallReadOnly) && flags.contains(MapFlags::SyscallWriteOnly) {
135 return Err(MapError::InvalidParam);
136 }
137
138 if flags.contains(MapFlags::Mmapable) && schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
140 return Err(MapError::InvalidParam);
141 }
142
143 if flags.contains(MapFlags::Clone) && schema.map_type != bpf_map_type_BPF_MAP_TYPE_SK_STORAGE {
145 return Err(MapError::InvalidParam);
146 }
147
148 Ok(())
149}
150
151trait MapImpl: Send + Sync + Debug {
152 fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>>;
153 fn update(&self, key: &[u8], value: EbpfBufferPtr<'_>, flags: u64) -> Result<(), MapError>;
154 fn delete(&self, key: &[u8]) -> Result<(), MapError>;
155 fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError>;
156 fn vmo(&self) -> &Arc<zx::Vmo>;
157
158 fn can_read(&self) -> Option<bool> {
161 None
162 }
163
164 fn ringbuf_reserve(&self, _size: u32, _flags: u64) -> Result<usize, MapError> {
165 Err(MapError::InvalidParam)
166 }
167}
168
169#[derive(Debug)]
171pub struct Map {
172 pub schema: MapSchema,
173
174 map_impl: Pin<Box<dyn MapImpl + Sync>>,
177}
178
179#[derive(Debug, Clone)]
182pub struct PinnedMap(Pin<Arc<Map>>);
183
184impl Deref for PinnedMap {
185 type Target = Map;
186 fn deref(&self) -> &Self::Target {
187 self.0.deref()
188 }
189}
190
191impl MapReference for PinnedMap {
192 fn schema(&self) -> &MapSchema {
193 &self.0.schema
194 }
195
196 fn as_bpf_value(&self) -> BpfValue {
197 BpfValue::from(self.deref() as *const Map)
198 }
199
200 fn get_data_ptr(&self) -> Option<BpfValue> {
201 assert!(self.0.schema.map_type == bpf_map_type_BPF_MAP_TYPE_ARRAY);
202
203 let key = [0u8; 4];
204 self.0.lookup(&key).map(|v| BpfValue::from(v.ptr().raw_ptr()))
205 }
206}
207
208pub type MapKey = smallvec::SmallVec<[u8; 16]>;
210
211pub type MapValue = smallvec::SmallVec<[u8; 64]>;
213
214const BASE_MAP_RIGHTS: zx::Rights = zx::Rights::READ
219 .union(zx::Rights::WRITE)
220 .union(zx::Rights::MAP)
221 .union(zx::Rights::SIGNAL)
222 .union(zx::Rights::WAIT);
223const SHARED_MAP_RIGHTS: zx::Rights = BASE_MAP_RIGHTS.union(zx::Rights::TRANSFER);
227
228impl Map {
229 pub fn new(schema: MapSchema, name: &str) -> Result<PinnedMap, MapError> {
230 validate_map_flags(&schema)?;
231 let map_impl = create_map_impl(&schema, name.to_string())?;
232 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
233 }
234
235 pub fn new_shared(shared: febpf::Map) -> Result<PinnedMap, MapError> {
236 let febpf::Map { schema: Some(fidl_schema), vmo: Some(vmo), .. } = shared else {
237 return Err(MapError::InvalidParam);
238 };
239
240 let vmo_info = vmo.basic_info().map_err(|_| MapError::InvalidVmo)?;
242 if !vmo_info.rights.contains(BASE_MAP_RIGHTS) {
243 return Err(MapError::InvalidVmo);
244 }
245
246 let schema = MapSchema {
247 map_type: fidl_map_type_to_bpf_map_type(fidl_schema.type_),
248 key_size: fidl_schema.key_size,
249 value_size: fidl_schema.value_size,
250 max_entries: fidl_schema.max_entries,
251 flags: map_flags_from_fidl(fidl_schema.flags),
252 };
253
254 let map_impl = create_map_impl(&schema, vmo)?;
255 Ok(PinnedMap(Arc::pin(Self { schema, map_impl })))
256 }
257
258 pub fn share(&self) -> Result<febpf::Map, MapError> {
259 Ok(febpf::Map {
260 schema: Some(febpf::MapSchema {
261 type_: bpf_map_type_to_fidl_map_type(self.schema.map_type),
262 key_size: self.schema.key_size,
263 value_size: self.schema.value_size,
264 max_entries: self.schema.max_entries,
265 flags: map_flags_to_fidl(self.schema.flags)?,
266 }),
267 vmo: Some(
268 self.map_impl
269 .vmo()
270 .duplicate_handle(SHARED_MAP_RIGHTS)
271 .map_err(|_| MapError::Internal)?,
272 ),
273 ..Default::default()
274 })
275 }
276
277 pub fn lookup<'a>(&'a self, key: &[u8]) -> Option<MapValueRef<'a>> {
278 self.map_impl.lookup(key)
279 }
280
281 pub fn load(&self, key: &[u8]) -> Option<MapValue> {
282 self.lookup(key).map(|v| v.ptr().load())
283 }
284
285 pub fn update(&self, key: &[u8], value: EbpfBufferPtr<'_>, flags: u64) -> Result<(), MapError> {
286 if flags & (BPF_EXIST as u64) > 0 && flags & (BPF_NOEXIST as u64) > 0 {
287 return Err(MapError::InvalidParam);
288 }
289
290 self.map_impl.update(key, value, flags)
291 }
292
293 pub fn delete(&self, key: &[u8]) -> Result<(), MapError> {
294 self.map_impl.delete(key)
295 }
296
297 pub fn get_next_key(&self, key: Option<&[u8]>) -> Result<MapKey, MapError> {
298 self.map_impl.get_next_key(key)
299 }
300
301 pub fn vmo(&self) -> &Arc<zx::Vmo> {
302 self.map_impl.vmo()
303 }
304
305 pub fn can_read(&self) -> Option<bool> {
306 self.map_impl.can_read()
307 }
308
309 pub fn ringbuf_reserve(&self, size: u32, flags: u64) -> Result<usize, MapError> {
310 self.map_impl.ringbuf_reserve(size, flags)
311 }
312
313 pub fn uses_locks(&self) -> bool {
314 self.schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY
315 }
316}
317
318pub enum MapValueRef<'a> {
319 PlainRef(EbpfBufferPtr<'a>),
320 HashMapRef(hashmap::HashMapEntryRef<'a>),
321 LpmTrieRef(lpm_trie::LpmTrieEntryRef<'a>),
322}
323
324impl<'a> MapValueRef<'a> {
325 fn new(buf: EbpfBufferPtr<'a>) -> Self {
326 Self::PlainRef(buf)
327 }
328
329 fn new_from_hashmap(hash_map_ref: hashmap::HashMapEntryRef<'a>) -> Self {
330 Self::HashMapRef(hash_map_ref)
331 }
332
333 fn new_from_lpm_trie(lpm_trie_ref: lpm_trie::LpmTrieEntryRef<'a>) -> Self {
334 Self::LpmTrieRef(lpm_trie_ref)
335 }
336
337 pub fn is_ref_counted(&self) -> bool {
338 match self {
339 Self::PlainRef(_) => false,
340 Self::HashMapRef(_) | Self::LpmTrieRef(_) => true,
341 }
342 }
343
344 pub fn ptr(&self) -> EbpfBufferPtr<'a> {
345 match self {
346 Self::PlainRef(buf) => *buf,
347 Self::HashMapRef(hash_map_ref) => hash_map_ref.ptr(),
348 Self::LpmTrieRef(lpm_trie_ref) => lpm_trie_ref.ptr(),
349 }
350 }
351}
352
353const SK_STORAGE_MAX_ENTRIES: u32 = 8192;
354
355fn create_map_impl(
356 schema: &MapSchema,
357 vmo: impl Into<VmoOrName>,
358) -> Result<Pin<Box<dyn MapImpl>>, MapError> {
359 match schema.map_type {
362 bpf_map_type_BPF_MAP_TYPE_ARRAY => Ok(Box::pin(array::Array::new(schema, vmo)?)),
364 bpf_map_type_BPF_MAP_TYPE_HASH => Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?)),
365 bpf_map_type_BPF_MAP_TYPE_RINGBUF => Ok(ring_buffer::RingBuffer::new(schema, vmo)?),
366 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => Ok(Box::pin(lpm_trie::LpmTrie::new(schema, vmo)?)),
367 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => {
368 if schema.key_size != 4 || schema.max_entries != 0 {
369 return Err(MapError::InvalidParam);
370 }
371
372 let schema = MapSchema {
374 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
375 key_size: 8,
376 max_entries: SK_STORAGE_MAX_ENTRIES,
377 value_size: schema.value_size,
378 flags: MapFlags::NoPrealloc,
379 };
380 Ok(Box::pin(hashmap::HashMap::new(&schema, vmo)?))
381 }
382
383 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => {
386 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP_HASH");
387 let schema =
390 MapSchema { flags: schema.flags.difference(MapFlags::ProgReadOnly), ..*schema };
391 Ok(Box::pin(hashmap::HashMap::new(&schema, vmo)?))
392 }
393 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => {
394 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_HASH");
395 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
396 }
397 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => {
398 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_ARRAY");
399 Ok(Box::pin(array::Array::new(schema, vmo)?))
400 }
401 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => {
402 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_HASH");
403 Ok(Box::pin(hashmap::HashMap::new(schema, vmo)?))
404 }
405 bpf_map_type_BPF_MAP_TYPE_UNSPEC => {
409 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_UNSPEC");
410 Err(MapError::MapTypeNotSupported)
411 }
412 bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY => {
413 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PROG_ARRAY");
414 Err(MapError::MapTypeNotSupported)
415 }
416 bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY => {
417 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERF_EVENT_ARRAY");
418 Err(MapError::MapTypeNotSupported)
419 }
420 bpf_map_type_BPF_MAP_TYPE_STACK_TRACE => {
421 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK_TRACE");
422 Err(MapError::MapTypeNotSupported)
423 }
424 bpf_map_type_BPF_MAP_TYPE_CGROUP_ARRAY => {
425 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_ARRAY");
426 Err(MapError::MapTypeNotSupported)
427 }
428 bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH => {
429 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_LRU_PERCPU_HASH");
430 Err(MapError::MapTypeNotSupported)
431 }
432 bpf_map_type_BPF_MAP_TYPE_ARRAY_OF_MAPS => {
433 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARRAY_OF_MAPS");
434 Err(MapError::MapTypeNotSupported)
435 }
436 bpf_map_type_BPF_MAP_TYPE_HASH_OF_MAPS => {
437 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_HASH_OF_MAPS");
438 Err(MapError::MapTypeNotSupported)
439 }
440 bpf_map_type_BPF_MAP_TYPE_DEVMAP => {
441 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP");
442 Err(MapError::MapTypeNotSupported)
443 }
444 bpf_map_type_BPF_MAP_TYPE_SOCKMAP => {
445 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKMAP");
446 Err(MapError::MapTypeNotSupported)
447 }
448 bpf_map_type_BPF_MAP_TYPE_CPUMAP => {
449 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CPUMAP");
450 Err(MapError::MapTypeNotSupported)
451 }
452 bpf_map_type_BPF_MAP_TYPE_XSKMAP => {
453 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_XSKMAP");
454 Err(MapError::MapTypeNotSupported)
455 }
456 bpf_map_type_BPF_MAP_TYPE_SOCKHASH => {
457 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_SOCKHASH");
458 Err(MapError::MapTypeNotSupported)
459 }
460 bpf_map_type_BPF_MAP_TYPE_CGROUP_STORAGE => {
461 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGROUP_STORAGE");
462 Err(MapError::MapTypeNotSupported)
463 }
464 bpf_map_type_BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => {
465 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY");
466 Err(MapError::MapTypeNotSupported)
467 }
468 bpf_map_type_BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE => {
469 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE");
470 Err(MapError::MapTypeNotSupported)
471 }
472 bpf_map_type_BPF_MAP_TYPE_QUEUE => {
473 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_QUEUE");
474 Err(MapError::MapTypeNotSupported)
475 }
476 bpf_map_type_BPF_MAP_TYPE_STACK => {
477 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STACK");
478 Err(MapError::MapTypeNotSupported)
479 }
480 bpf_map_type_BPF_MAP_TYPE_STRUCT_OPS => {
481 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_STRUCT_OPS");
482 Err(MapError::MapTypeNotSupported)
483 }
484 bpf_map_type_BPF_MAP_TYPE_INODE_STORAGE => {
485 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_INODE_STORAGE");
486 Err(MapError::MapTypeNotSupported)
487 }
488 bpf_map_type_BPF_MAP_TYPE_TASK_STORAGE => {
489 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_TASK_STORAGE");
490 Err(MapError::MapTypeNotSupported)
491 }
492 bpf_map_type_BPF_MAP_TYPE_BLOOM_FILTER => {
493 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_BLOOM_FILTER");
494 Err(MapError::MapTypeNotSupported)
495 }
496 bpf_map_type_BPF_MAP_TYPE_USER_RINGBUF => {
497 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_USER_RINGBUF");
498 Err(MapError::MapTypeNotSupported)
499 }
500 bpf_map_type_BPF_MAP_TYPE_CGRP_STORAGE => {
501 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_CGRP_STORAGE");
502 Err(MapError::MapTypeNotSupported)
503 }
504 bpf_map_type_BPF_MAP_TYPE_ARENA => {
505 track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_ARENA");
506 Err(MapError::MapTypeNotSupported)
507 }
508 _ => {
509 track_stub!(
510 TODO("https://fxbug.dev/323847465"),
511 "unknown bpf map type",
512 schema.map_type
513 );
514 Err(MapError::InvalidParam)
515 }
516 }
517}
518
519pub fn compute_map_storage_size(schema: &MapSchema) -> Result<usize, MapError> {
520 schema.value_size.checked_mul(schema.max_entries).map(|v| v as usize).ok_or(MapError::NoMemory)
521}
522
523fn bpf_map_type_to_fidl_map_type(map_type: bpf_map_type) -> febpf::MapType {
525 match map_type {
526 bpf_map_type_BPF_MAP_TYPE_ARRAY => febpf::MapType::Array,
527 bpf_map_type_BPF_MAP_TYPE_HASH => febpf::MapType::HashMap,
528 bpf_map_type_BPF_MAP_TYPE_RINGBUF => febpf::MapType::RingBuffer,
529 bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY => febpf::MapType::PercpuArray,
530 bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH => febpf::MapType::PercpuHash,
531 bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => febpf::MapType::DevmapHash,
532 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => febpf::MapType::LpmTrie,
533 bpf_map_type_BPF_MAP_TYPE_LRU_HASH => febpf::MapType::LruHash,
534 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => febpf::MapType::SkStorage,
535 _ =>
536 {
538 unreachable!("unsupported map type {:?}", map_type)
539 }
540 }
541}
542
543fn fidl_map_type_to_bpf_map_type(map_type: febpf::MapType) -> bpf_map_type {
544 match map_type {
545 febpf::MapType::Array => bpf_map_type_BPF_MAP_TYPE_ARRAY,
546 febpf::MapType::HashMap => bpf_map_type_BPF_MAP_TYPE_HASH,
547 febpf::MapType::RingBuffer => bpf_map_type_BPF_MAP_TYPE_RINGBUF,
548 febpf::MapType::PercpuArray => bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY,
549 febpf::MapType::PercpuHash => bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH,
550 febpf::MapType::DevmapHash => bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH,
551 febpf::MapType::LpmTrie => bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
552 febpf::MapType::LruHash => bpf_map_type_BPF_MAP_TYPE_LRU_HASH,
553 febpf::MapType::SkStorage => bpf_map_type_BPF_MAP_TYPE_SK_STORAGE,
554 }
555}
556#[cfg(test)]
559mod test {
560 use super::*;
561
562 #[fuchsia::test]
563 fn test_sharing_array() {
564 let schema = MapSchema {
565 map_type: bpf_map_type_BPF_MAP_TYPE_ARRAY,
566 key_size: 4,
567 value_size: 4,
568 max_entries: 10,
569 flags: MapFlags::empty(),
570 };
571
572 let map1 = Map::new(schema, "test").unwrap();
574 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
575
576 let key = vec![0, 0, 0, 0];
578 let mut value = [0, 1, 2, 3];
579 map1.update(&MapKey::from_vec(key.clone()), (&mut value).into(), 0).unwrap();
580 assert_eq!(&map2.load(&key).unwrap()[..], &value);
581 }
582
583 #[fuchsia::test]
584 fn test_sharing_hash_map() {
585 let schema = MapSchema {
586 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
587 key_size: 4,
588 value_size: 4,
589 max_entries: 10,
590 flags: MapFlags::empty(),
591 };
592
593 let map1 = Map::new(schema, "test").unwrap();
595 let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
596
597 let key = vec![0, 0, 0, 0];
599 let mut value = [0, 1, 2, 3];
600 map1.update(&MapKey::from_vec(key.clone()), (&mut value).into(), 0).unwrap();
601 assert_eq!(&map2.load(&key).unwrap()[..], &value);
602 }
603
604 #[fuchsia::test]
605 fn test_hash_map() {
606 let schema = MapSchema {
607 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
608 key_size: 5,
609 value_size: 25,
610 max_entries: 10000,
611 flags: MapFlags::empty(),
612 };
613
614 let get_key = |i| {
615 MapKey::from_vec(vec![
616 (i & 0xffusize) as u8,
617 0,
618 ((i >> 4) & 0xffusize) as u8,
619 0,
620 ((i >> 8) & 0xffusize) as u8,
621 ])
622 };
623 let get_value = |i, v| format!("--{:010} {:010}--", i, v).into_bytes();
624
625 let map = Map::new(schema, "test").unwrap();
626
627 for i in 0..10000 {
628 assert!(map.update(&get_key(i), (&mut get_value(i, 0)).into(), 0).is_ok());
629 }
630
631 assert_eq!(
633 map.update(&get_key(10001), (&mut get_value(10001, 1)).into(), 0),
634 Err(MapError::SizeLimit)
635 );
636
637 for i in 0..10000 {
638 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 0));
639 }
640
641 for i in 8000..9000 {
643 assert!(map.update(&get_key(i), (&mut get_value(i, 1)).into(), 0).is_ok());
644 }
645 for i in 8000..9000 {
646 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 1));
647 }
648
649 for i in 5000..10000 {
651 assert!(map.delete(&get_key(i)).is_ok());
652 }
653 for i in 5000..10000 {
654 assert_eq!(map.load(&get_key(i)), None);
655 }
656
657 for i in 10000..15000 {
659 assert!(map.update(&get_key(i), (&mut get_value(i, 2)).into(), 0).is_ok());
660 }
661
662 for i in 0..5000 {
663 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 0));
664 }
665 for i in 10000..15000 {
666 assert_eq!(&map.load(&get_key(i)).unwrap()[..], &get_value(i, 2));
667 }
668 }
669
670 #[fuchsia::test]
671 fn test_hash_map_overflow() {
672 let schema = MapSchema {
673 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
674 key_size: 8,
675 value_size: u32::MAX,
676 max_entries: u32::MAX,
677 flags: MapFlags::empty(),
678 };
679 assert_eq!(Map::new(schema, "test").err(), Some(MapError::InvalidParam));
680
681 let schema = MapSchema {
682 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
683 key_size: 8,
684 value_size: 0xffff_fff0,
685 max_entries: 0xffff_fff8,
686 flags: MapFlags::empty(),
687 };
688 assert_eq!(Map::new(schema, "test").err(), Some(MapError::InvalidParam));
689
690 let schema = MapSchema {
691 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
692 key_size: 8,
693 value_size: 0x1_0000,
694 max_entries: 0x8000_0000,
695 flags: MapFlags::empty(),
696 };
697 assert_eq!(Map::new(schema, "test").err(), Some(MapError::NoMemory));
698 }
699
700 #[fuchsia::test]
701 fn test_lpm_trie_overflow() {
702 let schema = MapSchema {
703 map_type: bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
704 key_size: 8,
705 value_size: u32::MAX,
706 max_entries: u32::MAX,
707 flags: MapFlags::NoPrealloc,
708 };
709 assert_eq!(Map::new(schema, "test").err(), Some(MapError::InvalidParam));
710
711 let schema = MapSchema {
712 map_type: bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
713 key_size: 5,
714 value_size: 0xffff_ffd0,
715 max_entries: 0xffff_fff8,
716 flags: MapFlags::NoPrealloc,
717 };
718 assert_eq!(Map::new(schema, "test").err(), Some(MapError::InvalidParam));
719
720 let schema = MapSchema {
721 map_type: bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
722 key_size: 8,
723 value_size: 0x1_0000,
724 max_entries: 0x8000_0000,
725 flags: MapFlags::NoPrealloc,
726 };
727 assert_eq!(Map::new(schema, "test").err(), Some(MapError::NoMemory));
728 }
729
730 #[fuchsia::test]
731 fn test_lpm_trie_invalid_key_size() {
732 let make_schema = |key_size| MapSchema {
733 map_type: bpf_map_type_BPF_MAP_TYPE_LPM_TRIE,
734 key_size,
735 value_size: 4,
736 max_entries: 10,
737 flags: MapFlags::NoPrealloc,
738 };
739
740 assert_eq!(Map::new(make_schema(4), "test").err(), Some(MapError::InvalidParam));
742 assert!(Map::new(make_schema(5), "test").is_ok());
743
744 assert!(Map::new(make_schema(260), "test").is_ok());
746 assert_eq!(Map::new(make_schema(261), "test").err(), Some(MapError::InvalidParam));
747 }
748
749 #[fuchsia::test]
750 fn test_hash_map_update_direct() {
751 let schema = MapSchema {
752 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
753 key_size: 5,
754 value_size: 11,
755 max_entries: 10,
756 flags: MapFlags::empty(),
757 };
758
759 let map = Map::new(schema, "test").unwrap();
760 let key = MapKey::from_vec("12345".to_string().into_bytes());
761 let mut value = (0..11).collect::<Vec<u8>>();
762 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
763
764 let value_ref = map.lookup(&key).unwrap();
766 value_ref.ptr().slice(0..4).unwrap().store(&[0xae, 0xad, 0xac, 0xab]);
767
768 assert_eq!(&map.load(&key).unwrap()[..], &[0xae, 0xad, 0xac, 0xab, 4, 5, 6, 7, 8, 9, 10]);
769 }
770
771 #[fuchsia::test]
772 fn test_hash_map_ref_counting() {
773 let schema = MapSchema {
774 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
775 key_size: 5,
776 value_size: 11,
777 max_entries: 2,
778 flags: MapFlags::empty(),
779 };
780
781 let map = Map::new(schema, "test").unwrap();
782 let key = MapKey::from_vec("12345".to_string().into_bytes());
783 let key2 = MapKey::from_vec("24122".to_string().into_bytes());
784 let mut value = (0..11).collect::<Vec<u8>>();
785 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
786 assert!(map.update(&key2.clone(), (&mut value).into(), 0).is_ok());
787
788 let value_ref = map.lookup(&key).unwrap();
789
790 assert!(map.delete(&key).is_ok());
793 assert_eq!(map.update(&key.clone(), (&mut value).into(), 0), Err(MapError::SizeLimit));
794 drop(value_ref);
795 assert!(map.update(&key.clone(), (&mut value).into(), 0).is_ok());
796 }
797
798 #[fuchsia::test]
799 fn test_ringbug_sharing() {
800 let schema = MapSchema {
801 map_type: bpf_map_type_BPF_MAP_TYPE_RINGBUF,
802 key_size: 0,
803 value_size: 0,
804 max_entries: 4096 * 2,
805 flags: MapFlags::empty(),
806 };
807
808 let map = Map::new(schema, "test").unwrap();
809 map.ringbuf_reserve(8000, 0).expect("ringbuf_reserve failed");
810
811 let map2 = Map::new_shared(map.share().unwrap()).unwrap();
812
813 map2.ringbuf_reserve(2000, 0).expect_err("ringbuf_reserve expected to fail");
815 }
816
817 #[fuchsia::test]
819 fn test_all_maps_shareable() {
820 for map_type in 1..linux_uapi::bpf_map_type___MAX_BPF_MAP_TYPE {
821 let (key_size, value_size, max_entries, flags) = match map_type {
822 bpf_map_type_BPF_MAP_TYPE_RINGBUF => (0, 0, 4096, MapFlags::empty()),
823 bpf_map_type_BPF_MAP_TYPE_LPM_TRIE => (8, 4, 4096, MapFlags::NoPrealloc),
824 bpf_map_type_BPF_MAP_TYPE_SK_STORAGE => (4, 4, 0, MapFlags::NoPrealloc),
825 _ => (4, 4, 1, MapFlags::empty()),
826 };
827 let schema = MapSchema { map_type, key_size, value_size, max_entries, flags };
828
829 let map = match Map::new(schema, "test") {
830 Ok(map) => map,
831 Err(MapError::MapTypeNotSupported) => {
832 continue;
833 }
834 Err(e) => {
835 panic!("Failed to create map of type {:?}: {:?}", map_type, e);
836 }
837 };
838
839 let map_fidl = map.share().expect("Failed to share map");
840 let _: PinnedMap = Map::new_shared(map_fidl).expect("Failed to initialize shared map");
841 }
842 }
843
844 #[fuchsia::test]
845 fn test_hash_map_get_next_key() {
846 let schema = MapSchema {
847 map_type: bpf_map_type_BPF_MAP_TYPE_HASH,
848 key_size: 4,
849 value_size: 4,
850 max_entries: 4,
851 flags: MapFlags::empty(),
852 };
853
854 let map = Map::new(schema, "test").unwrap();
855 let missing_key = u32::MAX.to_ne_bytes();
856
857 assert_eq!(map.get_next_key(None), Err(MapError::InvalidKey));
859 assert_eq!(map.get_next_key(Some(&missing_key)), Err(MapError::InvalidKey));
860
861 let k1 = 1u32.to_ne_bytes();
862 let k2 = 2u32.to_ne_bytes();
863 let mut val = [1, 2, 3, 4];
864 map.update(&k1, (&mut val).into(), 0).unwrap();
865 map.update(&k2, (&mut val).into(), 0).unwrap();
866
867 let first = map.get_next_key(None).unwrap();
868 assert!(first.as_slice() == k1 || first.as_slice() == k2);
869
870 assert_eq!(map.get_next_key(Some(&missing_key)), Ok(first.clone()));
872
873 let second = map.get_next_key(Some(&first)).unwrap();
874 assert!(second.as_slice() == k1 || second.as_slice() == k2);
875 assert_ne!(first, second);
876
877 assert_eq!(map.get_next_key(Some(&second)), Err(MapError::InvalidKey));
879 }
880}