Skip to main content

starnix_core/bpf/
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//! Implementation of (e)BPF.
6//!
7//! BPF stands for Berkeley Packet Filter and is an API introduced in BSD that allows filtering
8//! network packets by running little programs in the kernel. eBPF stands for extended BFP and
9//! is a Linux extension of BPF that allows hooking BPF programs into many different
10//! non-networking-related contexts.
11
12pub mod attachments;
13pub mod context;
14pub mod fs;
15pub mod map;
16pub mod program;
17pub mod syscalls;
18
19use crate::bpf::attachments::EbpfAttachments;
20use crate::bpf::map::{BpfMapHandle, BpfMapId, WeakBpfMapHandle};
21use crate::bpf::program::{ProgramHandle, ProgramId, WeakProgramHandle};
22use crate::security;
23use crate::task::CurrentTask;
24use ebpf::MapFlags;
25use starnix_sync::{EbpfStateLock, LockDepMutex};
26use starnix_uapi::auth::{CAP_SYS_ADMIN, Capabilities};
27use starnix_uapi::errors::Errno;
28use starnix_uapi::{bpf_map_type, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE};
29use std::collections::BTreeMap;
30use std::ops::Bound;
31use std::sync::Arc;
32use zerocopy::IntoBytes as _;
33
34/// Checks if `current_task` has either `capability` or [`CAP_SYS_ADMIN`], matching Linux
35/// capability auditing behavior.
36///
37/// In Linux, `capability` is checked first with auditing. If `capability` is absent, an audit
38/// denial is generated for `capability`, and [`CAP_SYS_ADMIN`] is checked as a fallback with auditing.
39/// If [`CAP_SYS_ADMIN`] is also absent, an audit denial is generated for [`CAP_SYS_ADMIN`].
40pub(crate) fn check_cap_or_sys_admin(
41    current_task: &CurrentTask,
42    capability: Capabilities,
43) -> Result<(), Errno> {
44    if security::check_task_capable(current_task, capability).is_ok() {
45        return Ok(());
46    }
47    security::check_task_capable(current_task, CAP_SYS_ADMIN)
48}
49
50struct WeakMapWithType {
51    map_type: bpf_map_type,
52    weak_map: WeakBpfMapHandle,
53}
54
55impl WeakMapWithType {
56    fn new(map: &BpfMapHandle) -> Self {
57        Self { map_type: map.schema.map_type, weak_map: Arc::downgrade(map) }
58    }
59}
60
61/// Stores global eBPF state.
62#[derive(Default)]
63pub struct EbpfState {
64    pub attachments: EbpfAttachments,
65
66    programs: LockDepMutex<BTreeMap<ProgramId, WeakProgramHandle>, EbpfStateLock>,
67    maps: LockDepMutex<BTreeMap<BpfMapId, WeakMapWithType>, EbpfStateLock>,
68}
69
70impl EbpfState {
71    fn register_program(&self, program: &ProgramHandle) {
72        self.programs.lock().insert(program.id(), Arc::downgrade(program));
73    }
74
75    fn unregister_program(&self, id: ProgramId) {
76        self.programs.lock().remove(&id).expect("Missing eBPF program");
77    }
78
79    fn get_next_program_id(&self, start_id: ProgramId) -> Option<ProgramId> {
80        self.programs
81            .lock()
82            .range((Bound::Excluded(start_id), Bound::Unbounded))
83            .next()
84            .map(|(k, _)| *k)
85    }
86
87    fn get_program_by_id(&self, id: ProgramId) -> Option<ProgramHandle> {
88        self.programs.lock().get(&id).map(|p| p.upgrade()).flatten()
89    }
90
91    fn register_map(&self, map: &BpfMapHandle) {
92        self.maps.lock().insert(map.id(), WeakMapWithType::new(map));
93    }
94
95    fn unregister_map(&self, id: BpfMapId) {
96        self.maps.lock().remove(&id).expect("Missing eBPF map");
97    }
98
99    fn get_next_map_id(&self, start_id: BpfMapId) -> Option<BpfMapId> {
100        self.maps
101            .lock()
102            .range((Bound::Excluded(start_id), Bound::Unbounded))
103            .next()
104            .map(|(k, _)| *k)
105    }
106
107    fn get_map_by_id(&self, id: BpfMapId) -> Option<BpfMapHandle> {
108        self.maps.lock().get(&id).map(|entry| entry.weak_map.upgrade()).flatten()
109    }
110
111    /// Removed socket with the specified `cookie` from all `sk_storage` maps.
112    // TODO(https://fxbug.dev/496639039): Move sk_storage cleanup to Netstack.
113    pub fn remove_sk_storage_entries(&self, cookie: u64) {
114        self.maps.lock().iter().for_each(|(_, entry)| {
115            if entry.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE
116                && let Some(map) = entry.weak_map.upgrade()
117            {
118                let _ = map.delete(cookie.as_bytes());
119            }
120        });
121    }
122
123    /// Clones `sk_storage` map entries with `BPF_F_CLONE` from `parent_cookie` socket to
124    /// `child_cookie` socket.
125    pub fn clone_sk_storage_entries(&self, parent_cookie: u64, child_cookie: u64) {
126        self.maps.lock().iter().for_each(|(_, entry)| {
127            if entry.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE
128                && let Some(map) = entry.weak_map.upgrade()
129                && map.schema.flags.contains(MapFlags::Clone)
130            {
131                if let Some(mut val) = map.load(parent_cookie.as_bytes()) {
132                    let _ = map.update(child_cookie.as_bytes(), (&mut val[..]).into(), 0);
133                }
134            }
135        });
136    }
137}