Skip to main content

ebpf_api/maps/
mod.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![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    // Equivalent of EINVAL.
51    InvalidParam,
52
53    // No entry with the specified key,
54    InvalidKey,
55
56    // Entry already exists..
57    EntryExists,
58
59    // Map size limit has been reached.
60    SizeLimit,
61
62    // Cannot allocate memory.
63    NoMemory,
64
65    // Invalid VMO was passed for a shared map.
66    InvalidVmo,
67
68    // Specified map type is not supported.
69    MapTypeNotSupported,
70
71    // Specified map configuration is not supported.
72    NotSupported,
73
74    // An internal issue, e.g. failed to allocate VMO.
75    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    // Read-only and write-only flags are mutually exclusive.
134    if flags.contains(MapFlags::SyscallReadOnly) && flags.contains(MapFlags::SyscallWriteOnly) {
135        return Err(MapError::InvalidParam);
136    }
137
138    // `MMAPABLE` is valid only for arrays.
139    if flags.contains(MapFlags::Mmapable) && schema.map_type != bpf_map_type_BPF_MAP_TYPE_ARRAY {
140        return Err(MapError::InvalidParam);
141    }
142
143    // `CLONE` is valid only for socket storage maps.
144    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    // Returns true if `POLLIN` is signaled for the map FD. Should be
159    // overridden only for ring buffers.
160    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/// A BPF map. This is a hashtable that can be accessed both by BPF programs and userspace.
170#[derive(Debug)]
171pub struct Map {
172    pub schema: MapSchema,
173
174    // The impl because it's required for some map implementations need to be
175    // pinned, particularly ring buffers.
176    map_impl: Pin<Box<dyn MapImpl + Sync>>,
177}
178
179/// Maps are normally kept pinned in memory since linked eBPF programs store direct pointers to
180/// the maps they depend on.
181#[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
208// Avoid allocation for eBPF keys smaller than 16 bytes.
209pub type MapKey = smallvec::SmallVec<[u8; 16]>;
210
211// Avoid allocation for eBPF values smaller than 64 bytes.
212pub type MapValue = smallvec::SmallVec<[u8; 64]>;
213
214// Access rights required for a map VMO handle. Should be consistent with the
215// rights specified in FIDL. READ, WRITE and MAP rights are required to access
216// the map contents. SIGNAL and WAIT rights are used for synchronization.
217// LINT.IfChange(map_rights)
218const 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);
223// LINT.ThenChange(//sdk/fidl/fuchsia.ebpf/ebpf.fidl:map_rights)
224
225// Rights for the VMO handle when sharing a map.
226const 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        // Check VMO rights.
241        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    // The list of supported maps should be kept in sync with the enum values in
360    // `fuchsia.ebpf.MapType`.
361    match schema.map_type {
362        // LINT.IfChange(supported_maps)
363        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            // SK_STORAGE maps are implemented as hashmaps with socket cookie used as a key.
373            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        // These types are in use, but not yet implemented. Incorrectly use Array or Hash for
384        // these
385        bpf_map_type_BPF_MAP_TYPE_DEVMAP_HASH => {
386            track_stub!(TODO("https://fxbug.dev/323847465"), "BPF_MAP_TYPE_DEVMAP_HASH");
387            // `BPF_F_RDONLY_PROG` is not yet implemented, but it's always set
388            // for `DEVMAP` maps.
389            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        // LINT.ThenChange(:fidl_map_types)
406
407        // Unimplemented types
408        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
523// LINT.IfChange(fidl_map_types)
524fn 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        // Other map types are rejected in `create_map_impl()`.
537        {
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// LINT.ThenChange(:supported_maps, //sdk/fidl/fuchsia.ebpf/ebpf.fidl:map_types)
557
558#[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        // Create two array maps sharing the content.
573        let map1 = Map::new(schema, "test").unwrap();
574        let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
575
576        // Set a value in one map and check that it's updated in the other.
577        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        // Create two array maps sharing the content.
594        let map1 = Map::new(schema, "test").unwrap();
595        let map2 = Map::new_shared(map1.share().unwrap()).unwrap();
596
597        // Set a value in one map and check that it's updated in the other.
598        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        // Should fail to add another entry when the map is full.
632        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        // Update some elements.
642        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        // Delete half of the entries.
650        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        // Replace removed entries with new ones
658        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        // Key size must be at least 5 bytes
741        assert_eq!(Map::new(make_schema(4), "test").err(), Some(MapError::InvalidParam));
742        assert!(Map::new(make_schema(5), "test").is_ok());
743
744        // Key size must be at most 260 bytes
745        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        // Access a value directly the way eBPF programs do.
765        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        // Delete an element. The corresponding data entry should not be
791        // released until `value_ref` is dropped.
792        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        // Expected to fail since there is no space left.
814        map2.ringbuf_reserve(2000, 0).expect_err("ringbuf_reserve expected to fail");
815    }
816
817    // Verifies that all supported map types are shareable.
818    #[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        // Empty map returns InvalidKey for both None and a non-existent key.
858        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        // Non-existent key returns the first key.
871        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        // Last key returns InvalidKey.
878        assert_eq!(map.get_next_key(Some(&second)), Err(MapError::InvalidKey));
879    }
880}