Servant combines uBPF and AF_XDP. Using it you can write eBPF packet processing programs that run in usersapce.
Install the dependencies:
- Tested on: Linux kernel 5.13
- uBPF (custom version)
- libbpf (commit: f9f6e92458899fee5d3d6c62c645755c25dd502d)
- llvm 13
You can look at /setup.sh
Compile:
use make at /src/ to compile the Servant.
You can install Servant's header with make install.
Enable the Huge page size support.
Servant Arguments:
| Arg | Description |
|---|---|
| ifname | name of the ethernet interface to attach |
| qid | queue index for attaching AF_XDP |
| ebpf | path to the ebpf program binary to load |
| [map] | name of the in kernel map which eBPF program want to access you can also assign the map an index using "name:index". This index could be used with lookup_fast helper function. |
| [core] | pin the process to the given CPU core |
| [xdp-prog] | path to the XDP program. this program should have a map of type BPF_MAP_TYPE_XSKMAP named xsks_map |
| [busypoll] | run AF_XDP in busy-polling mode |
| [...] | there are other options (to be complete) |
There are some example programs at /examples.
# map incomming ipv4/udp traffic with destination port of 8080 to queue 2
sudo ethtool -U <iface> flow-type udp4 dst-port 8080 action 2
cd ./examples/dump_packet
make
# Run the eBPF program with an AF_XDP socket connected on queue 2
sudo ../../src/servant <ifce> 2 ./ubpf.oGenerate traffic toward the uBPF program. The packets should be displayed on the terminal.
On the other machine start a netcat and send UDP traffic.
nc -u <ip address of first machine> 8080
helloOut put of uBPF program should be like below
...
Packet size: 60
Src MAC: 3c:fd:fe:56:1:a2
Dest MAC: 3c:fd:fe:55:ff:42
Ether Type: 800
Src IP: c0a80102
Dst IP: c0a80101
Transport: UDP
Src PORT: 57300
Dst PORT: 8080
...
The structure of the program looks like below.
#include <servant/servant_engine.h>
int bpf_prog(struct pktctx *ctx)
{
return DROP;
}struct pktctx:
Each eBPF program will receive packets in the following form.
(look at /src/include/packet_context.h)
struct pktctx {
void *data; // in: start of the packet
void *data_end; // in: end of the packet
uint32_t pkt_len; // inout: final length of packet
int32_t trim_head; // out: skip n bytes from the head before sending
};return codes:
The eBPF program should return one of the following values.
PASS: Pass the packet to the network stack.DROP: Drop the packet.SEND: Send the packet on the attached ethernet interface.
Currently destination of packets injected to the kernel (
PASS) are hard-coded but this can be solved and is not a limitation. Look at/src/udp_sock.c
For list of helper functions defined look at /src/include/servant_engine.h
| Helper | Description |
|---|---|
lookup |
lookup an in kernel map using name of the map |
lookup_fast |
lookup an in kernel map using index of the map (You can define index of each map when starting Servant) |
userspace_lookup |
lookup a userspace map |
userspace_update |
update a usersapce map |
ubpf_get_time_ns |
read clock MONOTONIC value |
ubpf_print |
print messages |