eBPF for Linux Admins: Part V
Table of Contents
eBPF - This article is part of a series.
In the previous article, we wrote an eBPF program to block all packets via XDP.
This article is a continuation of previous article where we will block a TCP port of an interface instead of all packets.
But before we go forward, let’s expand the diagram we have see in the first article.
The BPF or pseudo VM has been enhanced by Alexei Starovoitov along with Daniel Borkmann and called it eBPF or extended BPF.
- Registers were increased from 3 to 10 (+1 sp).
- Changed register to 64bit.
- Added a stack of 512 bytes
- Added unlimited arbitrary key/value structure called maps
- Added helper function support
- Added a Verifier to verify code
- Added a JIT compiler to generate native assembly code from object code
A simple firewall using eBPF#
This eBPF program will will block traffic to port 80 of an interface.
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#define BLOCK_PORT 80
#define TOTSZ (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr))
SEC("xdp")
int xdp_drop_port(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
// Set IP and TCP header structs
struct iphdr *ip = data + sizeof(struct ethhdr);
struct tcphdr *tcph = data + sizeof(struct ethhdr) + sizeof(struct iphdr);
//Below check is made mandatoy by eBPF verifier
if (data + TOTSZ > data_end) {
return XDP_PASS;
}
// If its a TCP packet and destined to port 80, then drop iit
if (ip->protocol == IPPROTO_TCP && tcph->dest == htons(BLOCK_PORT)) {
// Write debug info to /sys/kernel/debug/tracing/trace_pipe
bpf_printk("Blocking packet from %pI4 to port %u\n",&ip->saddr, ntohs(tcph->dest));
return XDP_DROP;
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
We are reading the start and end of data using xdp_md
.
We have to make sure the headers + data is not greater than the end of the data. If we read anything outside of the end of data by accident, then that may crash the kernel and that is why the verifier enforces this check.
Other details are self explanatory from the program itself.
Let’s compile the program and then load it.
clang -O2 -g -Wall -target bpf -c xdp_drop_port.c -o xdp_drop_port.o
sudo xdp-loader load -m skb -s xdp enp0s8 xdp_drop_port.o
Then you can watch the trace logs with below command
cat /sys/kernel/debug/tracing/trace_pipe
If you try to use curl command to the IP assigned to the interface enp0s8
then you will notice and output like below;
<idle>-0 [003] ..s21 89616.096958: bpf_trace_printk: Blocking packet from 192.168.57.1 to port 80
<idle>-0 [003] ..s21 89617.123247: bpf_trace_printk: Blocking packet from 192.168.57.1 to port 80
<idle>-0 [003] ..s21 89619.139545: bpf_trace_printk: Blocking packet from 192.168.57.1 to port 80
Congratulations for making yourself to this stage!!.🎉
Give a pat on your shoulder 😄
So far we have used eBPF in XDP. In upcoming one, we will use eBPF on kprobes. Brace yourself for the next eBPF chapters.