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