There are user land libraries for eBPF that allow you to write eBPF applications in C++, Rust, Go, Python and even Lua. But there are none for Java, which is a pity. So... I decided to write my own, which allows you to write eBPF programs directly in Java.
This is still in the early stages, but you can already use it for developing small tools and more coming in the future.
Based on the overview from ebpf.io, duke image from OpenJDK.
Let's discover eBPF together. Join me on the journey and learn a lot about eBPF and Java along the way.
Consider for a brief moment that you want to test how your server application behaves when every third incoming network packet is dropped. We can write a simple eBPF program to do this:
@BPF(license = "GPL")
public abstract class XDPDropEveryThirdPacket extends BPFProgram implements XDPHook {
final GlobalVariable<@Unsigned Integer> count = new GlobalVariable<>(0);
@BPFFunction
public boolean shouldDrop() {
return count.get() % 3 == 1;
}
@Override // runs directly in the kernel on every incoming packet
public xdp_action xdpHandlePacket(Ptr<xdp_md> ctx) {
// this code is actually compiled to the C code that is executed in the kernel
count.set(count.get() + 1);
return shouldDrop() ? xdp_action.XDP_DROP : xdp_action.XDP_PASS;
}
// runs in user land
public static void main(String[] args) throws InterruptedException {
try (XDPDropEveryThirdPacket program = BPFProgram.load(XDPDropEveryThirdPacket.class)) {
// attach the xdpHandlePacket method to the network interface
program.xdpAttach(XDPUtil.getNetworkInterfaceIndex());
// print the current packet count in a loop
while (true) {
System.out.println("Packet count " + program.count.get());
Thread.sleep(1000);
}
}
}
}
You can find this example as XDPDropEveryThirdPacket.java.
Provide a library (and documentation) for Java developers to explore eBPF and write their own eBPF programs, like firewalls, directly in Java, using the libbpf under the hood.
The goal is neither to replace existing eBPF libraries nor to provide a higher abstractions.
These might change in the future, but for now, you need the following:
Either a Linux machine with the following:
-
Linux 64-bit (or a VM)
-
Java 22 or later
-
libbpf and bpf-tool
- e.g.
apt install libbpf-dev linux-tools-common linux-tools-$(uname -r)
on Ubuntu
- e.g.
-
root privileges (for executing the eBPF programs)
-
On Mac OS, you can use the Lima VM (or use the
hello-ebpf.yaml
file as a guide to install the prerequisites):
limactl start hello-ebpf.yaml --mount-writable
limactl shell hello-ebpf sudo bin/install.sh
limactl shell hello-ebpf
# You'll need to be root for most of the examples
sudo -s PATH=$PATH
The scheduler examples require a patched 6.11 kernel with the scheduler extensions, you can get it from here. You might also be able to run CachyOS and install a patched kernel from there.
Posts covering the development of this project:
- Dec 01, 2023: Finding all used Classes, Methods, and Functions of a Python Module
- Dec 11, 2023: From C to Java Code using Panama
- Jan 01, 2024: Hello eBPF: Developing eBPF Apps in Java (1)
- Jan 12, 2024: Hello eBPF: Recording data in basic eBPF maps (2)
- Jan 29, 2024: Hello eBPF: Recording data in perf event buffers (3)
- Feb 12, 2024: Hello eBPF: Tail calls and your first eBPF application (4)
- Feb 26, 2024: Hello eBPF: First steps with libbpf (5)
- Mar 12, 2024: Hello eBPF: Ring buffers in libbpf (6)
- Mar 22, 2024: Hello eBPF: Auto Layouting Structs (7)
- Apr 09, 2024: Hello eBPF: Generating C Code (8)
- Apr 22, 2024: Hello eBPF: XDP-based Packet Filter (9)
- May 21, 2024: Hello eBPF: Global Variables (10)
- Jul 02, 2024: Hello eBPF: BPF Type Format and 13 Thousand Generated Java Classes (11)
- Jul 30, 2024: Hello eBPF: Write your eBPF application in Pure Java (12)
- Aug 13, 2024: Hello eBPF: A Packet Logger in Pure Java using TC and XDP Hooks (13)
I wrote a few samples that showcase the usage of the library in the bpf-samples module, you can use them as a starting point for your own eBPF programs.
Inspiration | Name and Java Class | Description |
---|---|---|
HelloWorld | A simple hello world example | |
LogOpenAt2Call | Logs all openat2 calls | |
Ansil H | RingSample | Record openat2 calls in a ring buffer |
HashMapSample | Record openat2 calls in a hash map | |
XDPDropEveryThirdPacket | Use XDP to block every third incoming packet | |
sematext | XDPPacketFilter | Use XDP to block incoming packages from specific URLs in Java |
sematext | XDPPacketFilter2 | The previous example but with the eBPF program as C code |
TCDropEveryThirdOutgoingPacket | Implement a Traffic Control to block every third outgoing packet at random | |
PacketLogger | TC and XDP based packet logger, capturing incoming and outgoing packets | |
nfil.dev | CGroupBlockHTTPEgress | Block all outgoing HTTP packets using cgroups |
demo.ForbiddenFile | Block access to a specific file via openat2 | |
Firewall | A simple firewall that blocks all incoming packets | |
FirewallSpring | A spring boot based web front-end for the Firewall |
Be sure to run the following in a shell with root privileges that uses JDK 22:
# in the project directory
./run.sh EXAMPLE_NAME
# list all examples
./run.sh
This allows you to easily run the example from above:
> ./build.sh
> ./run.sh XDPDropEveryThirdPacket
Packet count 0
Packet count 2
Packet count 3
Packet count 5
Packet count 6
Packet count 8
Packet count 9
Packet count 11
You can use the debug.sh
to run an example with a debugger port open at port 5005.
To build the project, make sure you have all prerequisites installed, then just run:
./build.sh
The library is available as a maven package:
<dependency>
<groupId>me.bechberger</groupId>
<artifactId>bpf</artifactId>
<version>0.1.1-scx-enabled-SNAPSHOT</version>
</dependency>
You might have to add the https://s01.oss.sonatype.org/content/repositories/releases/ repo:
<repositories>
<repository>
<id>snapshots</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
A look ahead into the future, so you know what to expect:
- Implement more features related to libbpf and eBPF
- cgroups support
- More documentation
These plans might change, but I'll try to keep this up to date. I'm open to suggestions, contributions, and ideas.
Tests are run using JUnit 5 and ./mvnw test
.
You can either run
./mvnw test -Dmaven.test.skip=false
or you can run the tests in a container using testutil/bin/java
:
./mvnw test -Djvm=testutil/bin/java -Dmaven.test.skip=false
This requires virtme (apt install virtme
), python 3, and docker to be installed.
You can run custom commands in the container using testutil/run-in-container.sh
.
Read more in the testutil/README.md.
I'm unable to get it running in the CI, so I'm currently running the tests locally.
Contributions are welcome; just open an issue or a pull request. Discussions take place in the discussions section of the GitHub repository.
I'm happy to include more example programs, API documentation, or helper methods, as well as links to repositories and projects that use this library.
Apache 2.0, Copyright 2023 SAP SE or an SAP affiliate company, Johannes Bechberger and contributors
This is a side project. The amount of time I can invest might vary over time.