eBPF for Linux Admins: Part III
Table of Contents
eBPF - This article is part of a series.
In this article, we will write a kernel module that drops the incoming packets if it’s destined to port 80.
Yes, we can use iptables
, but to learn the inner-workings of Linux, let’s come out of that comfort zone and write our own one this time.
Before we move forward, we have to familiarize more terms, which are Helper Functions
and Hooks
.
Helper Functions#
In the previous chapter, we wrote a simple module to print a message to the kernel ring buffer and there we used a helper function called, pr_info
.
Since the driver is in Kernel space, we couldn’t use any of the userspace libraries to make log entries. So what we did was, we took the help of a kernel helper function. Linux helper functions are facilities that can be used to interact with other parts of the kernel. We will see more helper functions going forward.
In our hello
example we used the helper function pr_info
from header file <linux/printk.h>
Hooks#
Hooks are pre-defined points in Kernel where you can register a function to it. When ever kernel reaches that hook, the registered function in that hook gets executed.
Let’s see an example of hooks in netfilter.
What is netfilter?
It’s your favorite iptables/nftable
backend to filter/translate packets.
In above diagram, you can see different hook points and we will use the hook point PRE-ROUTING
to drop a packet.
So, we will write a module that will have a function; to be precise a callback function
, to drop an IPv4 packet if the destination port is 80
.
Then that function will register to the PRE-ROUTING
hook.
When ever the kernel reaches the PRE-ROUTING
hook point, our function will get executed and packet will be dropped.
But wait!!. Didn’t we start this series for learning eBPF?
Yeah, I know, but please be patient. Things will get better going forward.
A simple firewall module to drop packets to port 80#
mkdir -p netfilter/example
cd !$
vi drop_pkt.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#define BLOCK_PORT 80
// Callback function
static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
// Access packet information using skb
struct iphdr *iph = ip_hdr(skb);
struct tcphdr *tcph = tcp_hdr(skb);
if(ntohs(tcph->dest) == BLOCK_PORT){
pr_info("Blocking packet from %pI4 to port %u\n",&iph->saddr, ntohs(tcph->dest));
return NF_DROP; // Drop packets to port 80
}
return NF_ACCEPT; // Allow the packet to continue
}
// Netfilter hook options
static struct nf_hook_ops nf_hook_ops = {
.hook = hook_func, // Our callback function
.pf = PF_INET, // IPv4 protocol family
.hooknum = NF_INET_PRE_ROUTING, // Hook point in the netfilter framework
.priority = NF_IP_PRI_FIRST, // Invocation priority
};
static int __init drop_pkt_module_init(void) {
return nf_register_net_hook(&init_net, &nf_hook_ops); //init_net indicates the root network namespace
}
static void __exit drop_pkt_module_exit(void) {
nf_unregister_net_hook(&init_net, &nf_hook_ops);
}
module_init(drop_pkt_module_init);
module_exit(drop_pkt_module_exit);
MODULE_LICENSE("GPL");
Create a Makefile with below contnents.
vi Makefile
obj-m += drop_pkt.o
PWD := $(CURDIR)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Now, you can start a simple HTTP server using python in a new terminal.
sudo python3 -m http.server 80
Now try to access the port using curl
command.
curl -sI 192.168.56.102
Output:-
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.12
Date: Mon, 15 Jan 2024 17:38:36 GMT
Content-type: text/html; charset=utf-8
Content-Length: 1218
Now, load the module.
insmod drop_pkt.ko
Repeat the curl command again and this time the command will hung.
If you check the dmesg
, you will see and output like below;
...
[98510.008817] Blocking packet from 192.168.56.1 to port 80
[98511.022603] Blocking packet from 192.168.56.1 to port 80
[98513.038433] Blocking packet from 192.168.56.1 to port 80
[98517.198138] Blocking packet from 192.168.56.1 to port 80
...
You can unload the module using below command and then curl
command will work.
sudo rmmod drop_pkt
Now we are familiar with how we can interact with different parts of the kernel using Kernel modules and some of the basics of hooks and helper functions.
In the next one, we will learn how we can interact with the packets before the kernel create socket buffer or skb.