Skip to content

Hello eBPF world! Hello Java world! Let's discover eBPF together and write Java user-land library along the way.

License

Notifications You must be signed in to change notification settings

parttimenerd/hello-ebpf

Repository files navigation

Hello eBPF

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.

Overview images

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.

Example

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.

Goals

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.

Prerequisites

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
  • 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.

Blog Posts

Posts covering the development of this project:

Examples

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

Running the Examples

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.

Build

To build the project, make sure you have all prerequisites installed, then just run:

./build.sh

Usage as a library

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>
You have to copy the .mvn/jvm.config file and add the annotation processor to your project. To properly use hello-ebpf as a library, you have to allow the maven compiler to access all the required internal APIs. You can do this by copying the `.mvn/jvm.config` file from this repository to your project.

Furthermore, you have to add the annotation processor to your project:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.0</version>
  <configuration>
    <annotationProcessors>
      <annotationProcessor>me.bechberger.ebpf.bpf.processor.Processor</annotationProcessor>
    </annotationProcessors>
    <compilerArgs>
      <arg>-Xplugin:BPFCompilerPlugin</arg>
    </compilerArgs>
  </configuration>
</plugin>

Plans

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.

Testing

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.

Contributing

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.

License

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.

About

Hello eBPF world! Hello Java world! Let's discover eBPF together and write Java user-land library along the way.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages