eBPF with Nix: laptop to testbed

Yifei Sun

Inria, ENS de Lyon, Université Grenoble Alpes

Background

I started a project

Background

Problem

What worked for me

NixOS VM tests

NixOS Compose

Userspace tooling

Pull packages from pinned nixpkgs

devShells.x86_64-linux.default = pkgs.mkShell {
inputsFrom = [ <locally defined derivations> ];
packages = with pkgs.llvmPackages; [ clang-unwrapped libllvm ];
};

Get a kernel

One machine

pkgs.testers.runNixOSTest {
name = "one-machine-test";

nodes.machine1 = {
imports = [ nixosModules.kernel ];
services.scx.enable = true;
};

testScript = ''
machine1.wait_for_unit("default.target")
machine1.succeed("")
machine1.fail("")
'';
}

More machines?

pkgs.testers.runNixOSTest {
name = "lots-of-machine-test";

nodes.machine1.imports = [ nixosModules.grafana ];
nodes.machine2.imports = with nixosModules; [
kernel exporter ebpf benchmark
];

testScript = ''
start_all()
machine1...
machine2...
'';
}

What’s in there?

Mock syscall

Say we want to troll ourselves:

SEC("ksyscall/statx")
int BPF_KSYSCALL(fsd_statx_entry, ... statx(2) args) {
// generate a map entry to collect start ts
// check path, if not match return
// else override with a static statx content
struct statx stx = { ... };
bpf_probe_write_user(statxbuf, &stx, sizeof(stx));
return bpf_override_return(ctx, 0);
}

And count how many times we can footgun ourselves

With a counter and a histogram

Declarative userspace program

Local testing

For simplicity

Complication is fast

Debugging is easy

Demo

$ ./result/bin/nixos-test-driver
start vlan
running vlan (pid 3859017; ctl /run/user/1000/vde1.ctl)
SSH backdoor enabled, the machines can be accessed like this:
collector: ssh -o User=root vsock/3
exporter: ssh -o User=root vsock/4

Straight to prod

Bit-perfect reproducibility (*: for some store paths)

Everything is in closure

Demo

Effort