Skip to main content

aya_friday/maps/hash_map/
hash_map.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    marker::PhantomData,
4};
5
6use crate::{
7    Pod,
8    maps::{IterableMap, MapData, MapError, MapIter, MapKeys, check_kv_size, hash_map},
9};
10
11/// A hash map that can be shared between eBPF programs and user space.
12///
13/// # Minimum kernel version
14///
15/// The minimum kernel version required to use this feature is 3.19.
16///
17/// # Examples
18///
19/// ```no_run
20/// # let mut bpf = aya::Ebpf::load(&[])?;
21/// use aya::maps::HashMap;
22///
23/// let mut redirect_ports = HashMap::try_from(bpf.map_mut("REDIRECT_PORTS").unwrap())?;
24///
25/// // redirect port 80 to 8080
26/// redirect_ports.insert(80, 8080, 0);
27/// // redirect port 443 to 8443
28/// redirect_ports.insert(443, 8443, 0);
29/// # Ok::<(), aya::EbpfError>(())
30/// ```
31#[doc(alias = "BPF_MAP_TYPE_HASH")]
32#[doc(alias = "BPF_MAP_TYPE_LRU_HASH")]
33#[derive(Debug)]
34pub struct HashMap<T, K, V> {
35    pub(crate) inner: T,
36    _kv: PhantomData<(K, V)>,
37}
38
39impl<T: Borrow<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
40    pub(crate) fn new(map: T) -> Result<Self, MapError> {
41        let data = map.borrow();
42        check_kv_size::<K, V>(data)?;
43
44        Ok(Self {
45            inner: map,
46            _kv: PhantomData,
47        })
48    }
49
50    /// Returns a copy of the value associated with the key.
51    pub fn get(&self, key: &K, flags: u64) -> Result<V, MapError> {
52        hash_map::get(self.inner.borrow(), key, flags)
53    }
54
55    /// An iterator visiting all key-value pairs in arbitrary order. The
56    /// iterator item type is `Result<(K, V), MapError>`.
57    pub fn iter(&self) -> MapIter<'_, K, V, Self> {
58        MapIter::new(self)
59    }
60
61    /// An iterator visiting all keys in arbitrary order. The iterator element
62    /// type is `Result<K, MapError>`.
63    pub fn keys(&self) -> MapKeys<'_, K> {
64        MapKeys::new(self.inner.borrow())
65    }
66}
67
68impl<'a, T: Borrow<MapData>, K: Pod, V: Pod> IntoIterator for &'a HashMap<T, K, V> {
69    type Item = Result<(K, V), MapError>;
70    type IntoIter = MapIter<'a, K, V, HashMap<T, K, V>>;
71
72    fn into_iter(self) -> Self::IntoIter {
73        self.iter()
74    }
75}
76
77impl<T: BorrowMut<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
78    /// Inserts a key-value pair into the map.
79    pub fn insert(
80        &mut self,
81        key: impl Borrow<K>,
82        value: impl Borrow<V>,
83        flags: u64,
84    ) -> Result<(), MapError> {
85        hash_map::insert(self.inner.borrow_mut(), key.borrow(), value.borrow(), flags)
86    }
87
88    /// Removes a key from the map.
89    pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
90        hash_map::remove(self.inner.borrow_mut(), key)
91    }
92}
93
94impl<T: Borrow<MapData>, K: Pod, V: Pod> IterableMap<K, V> for HashMap<T, K, V> {
95    fn map(&self) -> &MapData {
96        self.inner.borrow()
97    }
98
99    fn get(&self, key: &K) -> Result<V, MapError> {
100        Self::get(self, key, 0)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use std::io;
107
108    use assert_matches::assert_matches;
109    use aya_obj::generated::{bpf_attr, bpf_cmd, bpf_map_type};
110    use libc::{EFAULT, ENOENT};
111
112    use super::*;
113    use crate::{
114        maps::{
115            Map,
116            test_utils::{self, new_map},
117        },
118        sys::{SysResult, Syscall, SyscallError, override_syscall},
119    };
120
121    fn new_obj_map() -> aya_obj::Map {
122        test_utils::new_obj_map::<u32>(bpf_map_type::BPF_MAP_TYPE_HASH)
123    }
124
125    fn sys_error(value: i32) -> SysResult {
126        Err((-1, io::Error::from_raw_os_error(value)))
127    }
128
129    #[test]
130    fn test_wrong_key_size() {
131        let map = new_map(new_obj_map());
132        assert_matches!(
133            HashMap::<_, u8, u32>::new(&map),
134            Err(MapError::InvalidKeySize {
135                size: 1,
136                expected: 4
137            })
138        );
139    }
140
141    #[test]
142    fn test_wrong_value_size() {
143        let map = new_map(new_obj_map());
144        assert_matches!(
145            HashMap::<_, u32, u16>::new(&map),
146            Err(MapError::InvalidValueSize {
147                size: 2,
148                expected: 4
149            })
150        );
151    }
152
153    #[test]
154    fn test_try_from_wrong_map() {
155        let map = new_map(new_obj_map());
156        let map = Map::Array(map);
157        assert_matches!(
158            HashMap::<_, u8, u32>::try_from(&map),
159            Err(MapError::InvalidMapType { .. })
160        );
161    }
162
163    #[test]
164    fn test_try_from_wrong_map_values() {
165        let map = new_map(new_obj_map());
166        let map = Map::HashMap(map);
167        assert_matches!(
168            HashMap::<_, u32, u16>::try_from(&map),
169            Err(MapError::InvalidValueSize {
170                size: 2,
171                expected: 4
172            })
173        );
174    }
175
176    #[test]
177    fn test_new_ok() {
178        let map = new_map(new_obj_map());
179        let _: HashMap<_, u32, u32> = HashMap::new(&map).unwrap();
180    }
181
182    #[test]
183    fn test_try_from_ok() {
184        let map = new_map(new_obj_map());
185        let map = Map::HashMap(map);
186        let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
187    }
188
189    #[test]
190    fn test_try_from_ok_lru() {
191        let map_data = || {
192            new_map(test_utils::new_obj_map::<u32>(
193                bpf_map_type::BPF_MAP_TYPE_LRU_HASH,
194            ))
195        };
196        let map = Map::HashMap(map_data());
197        let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
198        let map = Map::LruHashMap(map_data());
199        let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
200    }
201
202    #[test]
203    fn test_insert_syscall_error() {
204        let mut map = new_map(new_obj_map());
205        let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
206
207        override_syscall(|_| sys_error(EFAULT));
208
209        assert_matches!(
210            hm.insert(1, 42, 0),
211            Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
212        );
213    }
214
215    #[test]
216    fn test_insert_ok() {
217        let mut map = new_map(new_obj_map());
218        let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
219
220        override_syscall(|call| match call {
221            Syscall::Ebpf {
222                cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
223                ..
224            } => Ok(0),
225            _ => sys_error(EFAULT),
226        });
227
228        assert_matches!(hm.insert(1, 42, 0), Ok(()));
229    }
230
231    #[test]
232    fn test_insert_boxed_ok() {
233        let mut map = new_map(new_obj_map());
234        let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
235
236        override_syscall(|call| match call {
237            Syscall::Ebpf {
238                cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
239                ..
240            } => Ok(0),
241            _ => sys_error(EFAULT),
242        });
243
244        assert_matches!(hm.insert(Box::new(1), Box::new(42), 0), Ok(()));
245    }
246
247    #[test]
248    fn test_remove_syscall_error() {
249        let mut map = new_map(new_obj_map());
250        let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
251
252        override_syscall(|_| sys_error(EFAULT));
253
254        assert_matches!(
255            hm.remove(&1),
256            Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
257        );
258    }
259
260    #[test]
261    fn test_remove_ok() {
262        let mut map = new_map(new_obj_map());
263        let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
264
265        override_syscall(|call| match call {
266            Syscall::Ebpf {
267                cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
268                ..
269            } => Ok(0),
270            _ => sys_error(EFAULT),
271        });
272
273        assert_matches!(hm.remove(&1), Ok(()));
274    }
275
276    #[test]
277    fn test_get_syscall_error() {
278        let map = new_map(new_obj_map());
279        override_syscall(|_| sys_error(EFAULT));
280        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
281
282        assert_matches!(
283            hm.get(&1, 0),
284            Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
285        );
286    }
287
288    #[test]
289    fn test_get_not_found() {
290        let map = new_map(new_obj_map());
291        override_syscall(|call| match call {
292            Syscall::Ebpf {
293                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
294                ..
295            } => sys_error(ENOENT),
296            _ => sys_error(EFAULT),
297        });
298        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
299
300        assert_matches!(hm.get(&1, 0), Err(MapError::KeyNotFound));
301    }
302
303    fn bpf_key<T: Copy>(attr: &bpf_attr) -> Option<T> {
304        match unsafe { attr.__bindgen_anon_2.key } as *const T {
305            p if p.is_null() => None,
306            p => Some(unsafe { *p }),
307        }
308    }
309
310    #[expect(
311        clippy::unnecessary_wraps,
312        reason = "mock syscall handlers use SysResult"
313    )]
314    fn set_next_key<T: Copy>(attr: &bpf_attr, next: T) -> SysResult {
315        let key =
316            (unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.next_key } as *const T).cast_mut();
317        unsafe { *key = next }
318        Ok(0)
319    }
320
321    #[expect(
322        clippy::unnecessary_wraps,
323        reason = "mock syscall handlers use SysResult"
324    )]
325    fn set_ret<T: Copy>(attr: &bpf_attr, ret: T) -> SysResult {
326        let value =
327            (unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.value } as *const T).cast_mut();
328        unsafe { *value = ret }
329        Ok(0)
330    }
331
332    #[test]
333    fn test_keys_empty() {
334        let map = new_map(new_obj_map());
335        override_syscall(|call| match call {
336            Syscall::Ebpf {
337                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
338                ..
339            } => sys_error(ENOENT),
340            _ => sys_error(EFAULT),
341        });
342        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
343        let keys = hm.keys().collect::<Result<Vec<_>, _>>();
344        assert_matches!(keys, Ok(ks) if ks.is_empty())
345    }
346
347    fn get_next_key(attr: &bpf_attr) -> SysResult {
348        match bpf_key(attr) {
349            None => set_next_key(attr, 10),
350            Some(10) => set_next_key(attr, 20),
351            Some(20) => set_next_key(attr, 30),
352            Some(30) => sys_error(ENOENT),
353            Some(_) => sys_error(EFAULT),
354        }
355    }
356
357    fn lookup_elem(attr: &bpf_attr) -> SysResult {
358        match bpf_key(attr) {
359            Some(10) => set_ret(attr, 100),
360            Some(20) => set_ret(attr, 200),
361            Some(30) => set_ret(attr, 300),
362            Some(_) => sys_error(ENOENT),
363            None => sys_error(EFAULT),
364        }
365    }
366
367    #[test]
368    fn test_keys() {
369        let map = new_map(new_obj_map());
370
371        override_syscall(|call| match call {
372            Syscall::Ebpf {
373                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
374                attr,
375            } => get_next_key(attr),
376            _ => sys_error(EFAULT),
377        });
378
379        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
380
381        let keys = hm.keys().collect::<Result<Vec<_>, _>>().unwrap();
382        assert_eq!(&keys, &[10, 20, 30])
383    }
384
385    #[test]
386    fn test_keys_error() {
387        let map = new_map(new_obj_map());
388        override_syscall(|call| match call {
389            Syscall::Ebpf {
390                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
391                attr,
392            } => match bpf_key(attr) {
393                None => set_next_key(attr, 10),
394                Some(10) => set_next_key(attr, 20),
395                Some(_) => sys_error(EFAULT),
396            },
397            _ => sys_error(EFAULT),
398        });
399        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
400
401        let mut keys = hm.keys();
402        assert_matches!(keys.next(), Some(Ok(10)));
403        assert_matches!(keys.next(), Some(Ok(20)));
404        assert_matches!(
405            keys.next(),
406            Some(Err(MapError::SyscallError(SyscallError {
407                call: "bpf_map_get_next_key",
408                io_error: _
409            })))
410        );
411        assert_matches!(keys.next(), None);
412    }
413
414    #[test]
415    fn test_iter() {
416        let map = new_map(new_obj_map());
417        override_syscall(|call| match call {
418            Syscall::Ebpf {
419                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
420                attr,
421            } => get_next_key(attr),
422            Syscall::Ebpf {
423                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
424                attr,
425            } => lookup_elem(attr),
426            _ => sys_error(EFAULT),
427        });
428        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
429        let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
430        assert_eq!(&items, &[(10, 100), (20, 200), (30, 300)])
431    }
432
433    #[test]
434    fn test_iter_key_deleted() {
435        let map = new_map(new_obj_map());
436        override_syscall(|call| match call {
437            Syscall::Ebpf {
438                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
439                attr,
440            } => get_next_key(attr),
441            Syscall::Ebpf {
442                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
443                attr,
444            } => match bpf_key(attr) {
445                Some(10) => set_ret(attr, 100),
446                Some(20) => sys_error(ENOENT),
447                Some(30) => set_ret(attr, 300),
448                Some(_) => sys_error(ENOENT),
449                None => sys_error(EFAULT),
450            },
451            _ => sys_error(EFAULT),
452        });
453        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
454
455        let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
456        assert_eq!(&items, &[(10, 100), (30, 300)])
457    }
458
459    #[test]
460    fn test_iter_key_error() {
461        let map = new_map(new_obj_map());
462        override_syscall(|call| match call {
463            Syscall::Ebpf {
464                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
465                attr,
466            } => match bpf_key(attr) {
467                None => set_next_key(attr, 10),
468                Some(10) => set_next_key(attr, 20),
469                Some(20) => sys_error(EFAULT),
470                Some(30) => sys_error(ENOENT),
471                Some(i) => panic!("invalid key {i}"),
472            },
473            Syscall::Ebpf {
474                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
475                attr,
476            } => lookup_elem(attr),
477            _ => sys_error(EFAULT),
478        });
479        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
480
481        let mut iter = hm.iter();
482        assert_matches!(iter.next(), Some(Ok((10, 100))));
483        assert_matches!(iter.next(), Some(Ok((20, 200))));
484        assert_matches!(
485            iter.next(),
486            Some(Err(MapError::SyscallError(SyscallError {
487                call: "bpf_map_get_next_key",
488                io_error: _
489            })))
490        );
491        assert_matches!(iter.next(), None);
492    }
493
494    #[test]
495    fn test_iter_value_error() {
496        let map = new_map(new_obj_map());
497        override_syscall(|call| match call {
498            Syscall::Ebpf {
499                cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
500                attr,
501            } => get_next_key(attr),
502            Syscall::Ebpf {
503                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
504                attr,
505            } => match bpf_key(attr) {
506                Some(10) => set_ret(attr, 100),
507                Some(20) => sys_error(EFAULT),
508                Some(30) => set_ret(attr, 300),
509                Some(_) => sys_error(ENOENT),
510                None => sys_error(EFAULT),
511            },
512            _ => sys_error(EFAULT),
513        });
514        let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
515
516        let mut iter = hm.iter();
517        assert_matches!(iter.next(), Some(Ok((10, 100))));
518        assert_matches!(
519            iter.next(),
520            Some(Err(MapError::SyscallError(SyscallError {
521                call: "bpf_map_lookup_elem",
522                io_error: _
523            })))
524        );
525        assert_matches!(iter.next(), Some(Ok((30, 300))));
526        assert_matches!(iter.next(), None);
527    }
528}