xref: /DragonOS/kernel/src/bpf/map/mod.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 mod array_map;
2 mod hash_map;
3 mod lru;
4 mod queue;
5 mod util;
6 
7 use super::Result;
8 use crate::bpf::map::array_map::{ArrayMap, PerCpuArrayMap, PerfEventArrayMap};
9 use crate::bpf::map::hash_map::PerCpuHashMap;
10 use crate::bpf::map::util::{BpfMapGetNextKeyArg, BpfMapMeta, BpfMapUpdateArg};
11 use crate::filesystem::vfs::file::{File, FileMode};
12 use crate::filesystem::vfs::syscall::ModeType;
13 use crate::filesystem::vfs::{FilePrivateData, FileSystem, FileType, IndexNode, Metadata};
14 use crate::include::bindings::linux_bpf::{bpf_attr, bpf_map_type};
15 use crate::libs::casting::DowncastArc;
16 use crate::libs::spinlock::{SpinLock, SpinLockGuard};
17 use crate::process::ProcessManager;
18 use crate::syscall::user_access::{UserBufferReader, UserBufferWriter};
19 use alloc::boxed::Box;
20 use alloc::string::String;
21 use alloc::sync::Arc;
22 use alloc::vec::Vec;
23 use core::any::Any;
24 use core::fmt::Debug;
25 use intertrait::CastFromSync;
26 use log::{error, info};
27 use system_error::SystemError;
28 
29 #[derive(Debug)]
30 pub struct BpfMap {
31     inner_map: SpinLock<Box<dyn BpfMapCommonOps>>,
32     meta: BpfMapMeta,
33 }
34 
35 pub type BpfCallBackFn = fn(key: &[u8], value: &[u8], ctx: *const u8) -> i32;
36 
37 pub trait BpfMapCommonOps: Send + Sync + Debug + CastFromSync {
38     /// Lookup an element in the map.
39     ///
40     /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_lookup_elem/
41     fn lookup_elem(&mut self, _key: &[u8]) -> Result<Option<&[u8]>> {
42         Err(SystemError::ENOSYS)
43     }
44     /// Update an element in the map.
45     ///
46     /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_update_elem/
47     fn update_elem(&mut self, _key: &[u8], _value: &[u8], _flags: u64) -> Result<()> {
48         Err(SystemError::ENOSYS)
49     }
50     /// Delete an element from the map.
51     ///
52     /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_delete_elem/
53     fn delete_elem(&mut self, _key: &[u8]) -> Result<()> {
54         Err(SystemError::ENOSYS)
55     }
56     /// For each element in map, call callback_fn function with map,
57     /// callback_ctx and other map-specific parameters.
58     ///
59     /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_for_each_map_elem/
60     fn for_each_elem(&mut self, _cb: BpfCallBackFn, _ctx: *const u8, _flags: u64) -> Result<u32> {
61         Err(SystemError::ENOSYS)
62     }
63     /// Look up an element with the given key in the map referred to by the file descriptor fd,
64     /// and if found, delete the element.
65     fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> {
66         Err(SystemError::ENOSYS)
67     }
68 
69     /// erform a lookup in percpu map for an entry associated to key on cpu.
70     fn lookup_percpu_elem(&mut self, _key: &[u8], _cpu: u32) -> Result<Option<&[u8]>> {
71         Err(SystemError::ENOSYS)
72     }
73     /// Get the next key in the map. If key is None, get the first key.
74     ///
75     /// Called from syscall
76     fn get_next_key(&self, _key: Option<&[u8]>, _next_key: &mut [u8]) -> Result<()> {
77         Err(SystemError::ENOSYS)
78     }
79 
80     /// Push an element value in map.
81     fn push_elem(&mut self, _value: &[u8], _flags: u64) -> Result<()> {
82         Err(SystemError::ENOSYS)
83     }
84 
85     /// Pop an element value from map.
86     fn pop_elem(&mut self, _value: &mut [u8]) -> Result<()> {
87         Err(SystemError::ENOSYS)
88     }
89 
90     /// Peek an element value from map.
91     fn peek_elem(&self, _value: &mut [u8]) -> Result<()> {
92         Err(SystemError::ENOSYS)
93     }
94 
95     /// Freeze the map.
96     ///
97     /// It's useful for .rodata maps.
98     fn freeze(&self) -> Result<()> {
99         Err(SystemError::ENOSYS)
100     }
101 
102     /// Get the first value pointer.
103     fn first_value_ptr(&self) -> Result<*const u8> {
104         Err(SystemError::ENOSYS)
105     }
106 }
107 impl DowncastArc for dyn BpfMapCommonOps {
108     fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
109         self
110     }
111 }
112 impl BpfMap {
113     pub fn new(map: Box<dyn BpfMapCommonOps>, meta: BpfMapMeta) -> Self {
114         assert_ne!(meta.key_size, 0);
115         BpfMap {
116             inner_map: SpinLock::new(map),
117             meta,
118         }
119     }
120 
121     pub fn inner_map(&self) -> &SpinLock<Box<dyn BpfMapCommonOps>> {
122         &self.inner_map
123     }
124 
125     pub fn key_size(&self) -> usize {
126         self.meta.key_size as usize
127     }
128 
129     pub fn value_size(&self) -> usize {
130         self.meta.value_size as usize
131     }
132 }
133 
134 impl IndexNode for BpfMap {
135     fn open(&self, _data: SpinLockGuard<FilePrivateData>, _mode: &FileMode) -> Result<()> {
136         Ok(())
137     }
138     fn close(&self, _data: SpinLockGuard<FilePrivateData>) -> Result<()> {
139         Ok(())
140     }
141     fn read_at(
142         &self,
143         _offset: usize,
144         _len: usize,
145         _buf: &mut [u8],
146         _data: SpinLockGuard<FilePrivateData>,
147     ) -> Result<usize> {
148         Err(SystemError::ENOSYS)
149     }
150 
151     fn write_at(
152         &self,
153         _offset: usize,
154         _len: usize,
155         _buf: &[u8],
156         _data: SpinLockGuard<FilePrivateData>,
157     ) -> Result<usize> {
158         Err(SystemError::ENOSYS)
159     }
160 
161     fn metadata(&self) -> Result<Metadata> {
162         let meta = Metadata {
163             mode: ModeType::from_bits_truncate(0o755),
164             file_type: FileType::File,
165             ..Default::default()
166         };
167         Ok(meta)
168     }
169 
170     fn resize(&self, _len: usize) -> Result<()> {
171         Ok(())
172     }
173 
174     fn fs(&self) -> Arc<dyn FileSystem> {
175         todo!("BpfMap does not have a filesystem")
176     }
177 
178     fn as_any_ref(&self) -> &dyn Any {
179         self
180     }
181 
182     fn list(&self) -> Result<Vec<String>> {
183         Err(SystemError::ENOSYS)
184     }
185 }
186 
187 /// Create a map and return a file descriptor that refers to
188 /// the map.  The close-on-exec file descriptor flag
189 /// is automatically enabled for the new file descriptor.
190 ///
191 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_CREATE/
192 pub fn bpf_map_create(attr: &bpf_attr) -> Result<usize> {
193     let map_meta = BpfMapMeta::try_from(attr)?;
194     info!("The map attr is {:#?}", map_meta);
195     let map: Box<dyn BpfMapCommonOps> = match map_meta.map_type {
196         bpf_map_type::BPF_MAP_TYPE_ARRAY => {
197             let array_map = ArrayMap::new(&map_meta)?;
198             Box::new(array_map)
199         }
200         bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY => {
201             let per_cpu_array_map = PerCpuArrayMap::new(&map_meta)?;
202             Box::new(per_cpu_array_map)
203         }
204         bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY => {
205             let perf_event_array_map = PerfEventArrayMap::new(&map_meta)?;
206             Box::new(perf_event_array_map)
207         }
208 
209         bpf_map_type::BPF_MAP_TYPE_CPUMAP
210         | bpf_map_type::BPF_MAP_TYPE_DEVMAP
211         | bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH => {
212             error!("bpf map type {:?} not implemented", map_meta.map_type);
213             Err(SystemError::EINVAL)?
214         }
215         bpf_map_type::BPF_MAP_TYPE_HASH => {
216             let hash_map = hash_map::BpfHashMap::new(&map_meta)?;
217             Box::new(hash_map)
218         }
219         bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH => {
220             let per_cpu_hash_map = PerCpuHashMap::new(&map_meta)?;
221             Box::new(per_cpu_hash_map)
222         }
223         bpf_map_type::BPF_MAP_TYPE_QUEUE => {
224             let queue_map = queue::QueueMap::new(&map_meta)?;
225             Box::new(queue_map)
226         }
227         bpf_map_type::BPF_MAP_TYPE_STACK => {
228             let stack_map = queue::StackMap::new(&map_meta)?;
229             Box::new(stack_map)
230         }
231         bpf_map_type::BPF_MAP_TYPE_LRU_HASH => {
232             let lru_hash_map = lru::LruMap::new(&map_meta)?;
233             Box::new(lru_hash_map)
234         }
235         bpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH => {
236             let lru_per_cpu_hash_map = lru::PerCpuLruMap::new(&map_meta)?;
237             Box::new(lru_per_cpu_hash_map)
238         }
239         _ => {
240             unimplemented!("bpf map type {:?} not implemented", map_meta.map_type)
241         }
242     };
243     let bpf_map = BpfMap::new(map, map_meta);
244     let fd_table = ProcessManager::current_pcb().fd_table();
245     let file = File::new(Arc::new(bpf_map), FileMode::O_RDWR | FileMode::O_CLOEXEC)?;
246     let fd = fd_table.write().alloc_fd(file, None).map(|x| x as usize)?;
247     info!("create map with fd: [{}]", fd);
248     Ok(fd)
249 }
250 
251 /// Create or update an element (key/value pair) in a specified map.
252 ///
253 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_UPDATE_ELEM/
254 pub fn bpf_map_update_elem(attr: &bpf_attr) -> Result<usize> {
255     let arg = BpfMapUpdateArg::from(attr);
256     info!("<bpf_map_update_elem>: {:#x?}", arg);
257     let map = get_map_file(arg.map_fd as i32)?;
258     let meta = &map.meta;
259     let key_size = meta.key_size as usize;
260     let value_size = meta.value_size as usize;
261 
262     let key_buf = UserBufferReader::new(arg.key as *const u8, key_size, true)?;
263     let value_buf = UserBufferReader::new(arg.value as *const u8, value_size, true)?;
264 
265     let key = key_buf.read_from_user(0)?;
266     let value = value_buf.read_from_user(0)?;
267     map.inner_map.lock().update_elem(key, value, arg.flags)?;
268     info!("bpf_map_update_elem ok");
269     Ok(0)
270 }
271 
272 pub fn bpf_map_freeze(attr: &bpf_attr) -> Result<usize> {
273     let arg = BpfMapUpdateArg::from(attr);
274     let map_fd = arg.map_fd;
275     info!("<bpf_map_freeze>: map_fd: {:}", map_fd);
276     let map = get_map_file(map_fd as i32)?;
277     map.inner_map.lock().freeze()?;
278     Ok(0)
279 }
280 
281 ///  Look up an element by key in a specified map and return its value.
282 ///
283 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_LOOKUP_ELEM/
284 pub fn bpf_lookup_elem(attr: &bpf_attr) -> Result<usize> {
285     let arg = BpfMapUpdateArg::from(attr);
286     // info!("<bpf_lookup_elem>: {:#x?}", arg);
287     let map = get_map_file(arg.map_fd as _)?;
288     let meta = &map.meta;
289     let key_size = meta.key_size as usize;
290     let value_size = meta.value_size as usize;
291 
292     let key_buf = UserBufferReader::new(arg.key as *const u8, key_size, true)?;
293     let mut value_buf = UserBufferWriter::new(arg.value as *mut u8, value_size, true)?;
294 
295     let key = key_buf.read_from_user(0)?;
296 
297     let mut inner = map.inner_map.lock();
298     let r_value = inner.lookup_elem(key)?;
299     if let Some(r_value) = r_value {
300         value_buf.copy_to_user(r_value, 0)?;
301         Ok(0)
302     } else {
303         Err(SystemError::ENOENT)
304     }
305 }
306 /// Look up an element by key in a specified map and return the key of the next element.
307 ///
308 /// - If key is `None`, the operation returns zero and sets the next_key pointer to the key of the first element.
309 /// - If key is `Some(T)`, the operation returns zero and sets the next_key pointer to the key of the next element.
310 /// - If key is the last element, returns -1 and errno is set to ENOENT.
311 ///
312 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_GET_NEXT_KEY/
313 pub fn bpf_map_get_next_key(attr: &bpf_attr) -> Result<usize> {
314     let arg = BpfMapGetNextKeyArg::from(attr);
315     // info!("<bpf_map_get_next_key>: {:#x?}", arg);
316     let map = get_map_file(arg.map_fd as i32)?;
317     let meta = &map.meta;
318     let key_size = meta.key_size as usize;
319 
320     let key = if let Some(key_ptr) = arg.key {
321         let key_buf = UserBufferReader::new(key_ptr as *const u8, key_size, true)?;
322         let key = key_buf.read_from_user(0)?.to_vec();
323         Some(key)
324     } else {
325         None
326     };
327     let key = key.as_deref();
328     let mut next_key_buf = UserBufferWriter::new(arg.next_key as *mut u8, key_size, true)?;
329     let inner = map.inner_map.lock();
330     let next_key = next_key_buf.buffer(0)?;
331     inner.get_next_key(key, next_key)?;
332     // info!("next_key: {:?}", next_key);
333     Ok(0)
334 }
335 
336 /// Look up and delete an element by key in a specified map.
337 ///
338 /// # WARN
339 ///
340 /// Not all map types (particularly array maps) support this operation,
341 /// instead a zero value can be written to the map value. Check the map types page to check for support.
342 ///
343 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_DELETE_ELEM/
344 pub fn bpf_map_delete_elem(attr: &bpf_attr) -> Result<usize> {
345     let arg = BpfMapUpdateArg::from(attr);
346     // info!("<bpf_map_delete_elem>: {:#x?}", arg);
347     let map = get_map_file(arg.map_fd as i32)?;
348     let meta = &map.meta;
349     let key_size = meta.key_size as usize;
350 
351     let key_buf = UserBufferReader::new(arg.key as *const u8, key_size, true)?;
352     let key = key_buf.read_from_user(0)?;
353     map.inner_map.lock().delete_elem(key)?;
354     Ok(0)
355 }
356 
357 /// Iterate and fetch multiple elements in a map.
358 ///
359 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_LOOKUP_BATCH/
360 pub fn bpf_map_lookup_batch(_attr: &bpf_attr) -> Result<usize> {
361     todo!()
362 }
363 
364 /// Look up an element with the given key in the map referred to by the file descriptor fd,
365 /// and if found, delete the element.
366 ///
367 /// For BPF_MAP_TYPE_QUEUE and BPF_MAP_TYPE_STACK map types, the flags argument needs to be set to 0,
368 /// but for other map types, it may be specified as:
369 /// - BPF_F_LOCK : If this flag is set, the command will acquire the spin-lock of the map value we are looking up.
370 ///
371 /// If the map contains no spin-lock in its value, -EINVAL will be returned by the command.
372 ///
373 /// The BPF_MAP_TYPE_QUEUE and BPF_MAP_TYPE_STACK map types implement this command as a “pop” operation,
374 /// deleting the top element rather than one corresponding to key.
375 /// The key and key_len parameters should be zeroed when issuing this operation for these map types.
376 ///
377 /// This command is only valid for the following map types:
378 /// - BPF_MAP_TYPE_QUEUE
379 /// - BPF_MAP_TYPE_STACK
380 /// - BPF_MAP_TYPE_HASH
381 /// - BPF_MAP_TYPE_PERCPU_HASH
382 /// - BPF_MAP_TYPE_LRU_HASH
383 /// - BPF_MAP_TYPE_LRU_PERCPU_HASH
384 ///
385 ///
386 /// See https://ebpf-docs.dylanreimerink.nl/linux/syscall/BPF_MAP_LOOKUP_AND_DELETE_ELEM/
387 pub fn bpf_map_lookup_and_delete_elem(attr: &bpf_attr) -> Result<usize> {
388     let arg = BpfMapUpdateArg::from(attr);
389     // info!("<bpf_map_lookup_and_delete_elem>: {:#x?}", arg);
390     let map = get_map_file(arg.map_fd as i32)?;
391     let meta = &map.meta;
392     let key_size = meta.key_size as usize;
393     let value_size = meta.value_size as usize;
394 
395     let key_buf = UserBufferReader::new(arg.key as *const u8, key_size, true)?;
396     let mut value_buf = UserBufferWriter::new(arg.value as *mut u8, value_size, true)?;
397 
398     let value = value_buf.buffer(0)?;
399     let key = key_buf.read_from_user(0)?;
400     let mut inner = map.inner_map.lock();
401     inner.lookup_and_delete_elem(key, value)?;
402     Ok(0)
403 }
404 
405 fn get_map_file(fd: i32) -> Result<Arc<BpfMap>> {
406     let fd_table = ProcessManager::current_pcb().fd_table();
407     let map = fd_table
408         .read()
409         .get_file_by_fd(fd)
410         .ok_or(SystemError::EBADF)?;
411     let map = map
412         .inode()
413         .downcast_arc::<BpfMap>()
414         .ok_or(SystemError::EINVAL)?;
415     Ok(map)
416 }
417