Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions fact-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ impl From<path_prefix_t> for lpm_trie::Key<[c_char; LPM_SIZE_MAX as usize]> {
}
}

impl From<lpm_trie::Key<[c_char; LPM_SIZE_MAX as usize]>> for path_prefix_t {
fn from(value: lpm_trie::Key<[c_char; LPM_SIZE_MAX as usize]>) -> Self {
path_prefix_t {
bit_len: value.prefix_len(),
path: value.data(),
}
}
}

impl PartialEq for path_prefix_t {
fn eq(&self, other: &Self) -> bool {
self.bit_len == other.bit_len && self.path == other.path
Expand Down
99 changes: 62 additions & 37 deletions fact/src/bpf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ pub struct Bpf {

tx: mpsc::Sender<Event>,

paths: Vec<path_prefix_t>,
paths_config: watch::Receiver<Vec<PathBuf>>,
paths_lpm_map: LpmTrie<MapData, [c_char; LPM_SIZE_MAX as usize], c_char>,

paths_globset: GlobSet,

Expand All @@ -55,19 +55,19 @@ impl Bpf {

// Include the BPF object as raw bytes at compile-time and load it
// at runtime.
let obj = Bpf::load_ebpf(&checks, bpf_config)?;
let mut obj = Bpf::load_ebpf(&checks, bpf_config)?;

Bpf::validate_config(&obj, bpf_config);
let paths_lpm_map = Bpf::take_path_prefix(&mut obj);

let (tx, rx) = mpsc::channel(100);
let paths = Vec::new();
let mut bpf = Bpf {
obj,
checks,
tx,
paths,
paths_config,
paths_globset: GlobSet::empty(),
paths_lpm_map,
links: Vec::new(),
running,
metrics,
Expand Down Expand Up @@ -129,6 +129,19 @@ impl Bpf {
Ok(PerCpuArray::try_from(metrics)?)
}

fn take_path_prefix(
obj: &mut Ebpf,
) -> LpmTrie<MapData, [c_char; LPM_SIZE_MAX as usize], c_char> {
let Some(paths_lpm_prefix) = obj.take_map("path_prefix") else {
unreachable!("path_prefix map not found");
};

match paths_lpm_prefix.try_into() {
Ok(map) => map,
Err(_) => unreachable!("path_prefix map is not LpmTrie"),
}
}

fn take_ringbuffer(&mut self) -> anyhow::Result<RingBuf<MapData>> {
let ringbuf = match self.obj.take_map(RINGBUFFER_NAME) {
Some(r) => r,
Expand All @@ -137,10 +150,34 @@ impl Bpf {
Ok(RingBuf::try_from(ringbuf)?)
}

fn cleanup_lpm_map(&mut self, new_paths: &[path_prefix_t]) -> anyhow::Result<()> {
let to_be_removed = self
.paths_lpm_map
.keys()
.filter_map(|p| match p {
Ok(p) => {
let p = path_prefix_t::from(p);
if !new_paths.contains(&p) {
Some(Ok(p))
} else {
None
}
}
Err(e) => Some(Err(e)),
})
.collect::<Result<Vec<_>, _>>()?;

for p in to_be_removed {
self.paths_lpm_map.remove(&p.into())?;
}

Ok(())
}

fn load_paths(&mut self) -> anyhow::Result<()> {
if self.paths_config.borrow().is_empty() {
self.detach_progs();
self.paths.clear();
self.cleanup_lpm_map(&[])?;
self.paths_globset = GlobSet::empty();
return Ok(());
}
Expand All @@ -149,41 +186,29 @@ impl Bpf {
self.attach_progs()?;
}

let Some(path_prefix) = self.obj.map_mut("path_prefix") else {
bail!("path_prefix map not found");
};
let mut path_prefix: LpmTrie<&mut MapData, [c_char; LPM_SIZE_MAX as usize], c_char> =
LpmTrie::try_from(path_prefix)?;

// Add the new prefixes
let paths_config = self.paths_config.borrow();
let mut new_paths = Vec::with_capacity(paths_config.len());
let mut builder = GlobSetBuilder::new();
for p in paths_config.iter() {
let Some(glob_str) = p.to_str() else {
bail!("failed to convert path {} to string", p.display());
};

builder.add(
Glob::new(glob_str)
.with_context(|| format!("invalid glob {}", glob_str))
.unwrap(),
);

let prefix = path_prefix_t::try_from(p)?;
path_prefix.insert(&prefix.into(), 0, 0)?;
new_paths.push(prefix);
}
self.paths_globset = builder.build()?;

// Remove old prefixes
for p in self.paths.iter().filter(|p| !new_paths.contains(p)) {
if let Err(e) = path_prefix.remove(&(*p).into()) {
warn!("Failed to remove path prefix: {e:#?}");
let new_paths = {
let paths_config = self.paths_config.borrow();
let mut new_paths = Vec::with_capacity(paths_config.len());
let mut builder = GlobSetBuilder::new();
for p in paths_config.iter() {
let Some(glob_str) = p.to_str() else {
bail!("failed to convert path {} to string", p.display());
};

builder.add(
Glob::new(glob_str).with_context(|| format!("invalid glob {}", glob_str))?,
);

let prefix = path_prefix_t::try_from(p)?;
self.paths_lpm_map.insert(&prefix.into(), 0, 0)?;
new_paths.push(prefix);
}
}
self.paths_globset = builder.build()?;
new_paths
};

self.paths = new_paths;
self.cleanup_lpm_map(&new_paths)?;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion fact/src/config/reloader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl Reloader {
let path = PathBuf::from(file);
if path.exists() {
let mtime = match path.metadata() {
Ok(m) => m.mtime(),
Ok(m) => m.mtime_nsec(),
Err(e) => {
warn!("Failed to stat {file}: {e}");
warn!("Configuration reloading may not work");
Expand Down
Loading
Loading