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 ebpf::MapFlags;
23use starnix_sync::{EbpfStateLock, LockDepMutex};
24use starnix_uapi::{bpf_map_type, bpf_map_type_BPF_MAP_TYPE_SK_STORAGE};
25use std::collections::BTreeMap;
26use std::ops::Bound;
27use std::sync::Arc;
28use zerocopy::IntoBytes as _;
29
30struct WeakMapWithType {
31    map_type: bpf_map_type,
32    weak_map: WeakBpfMapHandle,
33}
34
35impl WeakMapWithType {
36    fn new(map: &BpfMapHandle) -> Self {
37        Self { map_type: map.schema.map_type, weak_map: Arc::downgrade(map) }
38    }
39}
40
41/// Stores global eBPF state.
42#[derive(Default)]
43pub struct EbpfState {
44    pub attachments: EbpfAttachments,
45
46    programs: LockDepMutex<BTreeMap<ProgramId, WeakProgramHandle>, EbpfStateLock>,
47    maps: LockDepMutex<BTreeMap<BpfMapId, WeakMapWithType>, EbpfStateLock>,
48}
49
50impl EbpfState {
51    fn register_program(&self, program: &ProgramHandle) {
52        self.programs.lock().insert(program.id(), Arc::downgrade(program));
53    }
54
55    fn unregister_program(&self, id: ProgramId) {
56        self.programs.lock().remove(&id).expect("Missing eBPF program");
57    }
58
59    fn get_next_program_id(&self, start_id: ProgramId) -> Option<ProgramId> {
60        self.programs
61            .lock()
62            .range((Bound::Excluded(start_id), Bound::Unbounded))
63            .next()
64            .map(|(k, _)| *k)
65    }
66
67    fn get_program_by_id(&self, id: ProgramId) -> Option<ProgramHandle> {
68        self.programs.lock().get(&id).map(|p| p.upgrade()).flatten()
69    }
70
71    fn register_map(&self, map: &BpfMapHandle) {
72        self.maps.lock().insert(map.id(), WeakMapWithType::new(map));
73    }
74
75    fn unregister_map(&self, id: BpfMapId) {
76        self.maps.lock().remove(&id).expect("Missing eBPF map");
77    }
78
79    fn get_next_map_id(&self, start_id: BpfMapId) -> Option<BpfMapId> {
80        self.maps
81            .lock()
82            .range((Bound::Excluded(start_id), Bound::Unbounded))
83            .next()
84            .map(|(k, _)| *k)
85    }
86
87    fn get_map_by_id(&self, id: BpfMapId) -> Option<BpfMapHandle> {
88        self.maps.lock().get(&id).map(|entry| entry.weak_map.upgrade()).flatten()
89    }
90
91    /// Removed socket with the specified `cookie` from all `sk_storage` maps.
92    // TODO(https://fxbug.dev/496639039): Move sk_storage cleanup to Netstack.
93    pub fn remove_sk_storage_entries(&self, cookie: u64) {
94        self.maps.lock().iter().for_each(|(_, entry)| {
95            if entry.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE
96                && let Some(map) = entry.weak_map.upgrade()
97            {
98                let _ = map.delete(cookie.as_bytes());
99            }
100        });
101    }
102
103    /// Clones `sk_storage` map entries with `BPF_F_CLONE` from `parent_cookie` socket to
104    /// `child_cookie` socket.
105    pub fn clone_sk_storage_entries(&self, parent_cookie: u64, child_cookie: u64) {
106        self.maps.lock().iter().for_each(|(_, entry)| {
107            if entry.map_type == bpf_map_type_BPF_MAP_TYPE_SK_STORAGE
108                && let Some(map) = entry.weak_map.upgrade()
109                && map.schema.flags.contains(MapFlags::Clone)
110            {
111                if let Some(mut val) = map.load(parent_cookie.as_bytes()) {
112                    let _ = map.update(child_cookie.as_bytes(), (&mut val[..]).into(), 0);
113                }
114            }
115        });
116    }
117}