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