Skip to main content
  1. Posts/

eBPF for Linux Admins: Part III

·4 mins· loading · loading ·
Ansil H
ebpf
Author
Ansil H
DevOps Guy
eBPF - This article is part of a series.
Part 3: This Article

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.

The netfilter hooks are a framework inside the Linux kernel that allows kernel modules to register callback functions at different locations of the Linux network stack. The registered callback function is then called back for every packet that traverses the respective hook within the Linux network stack.

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.

eBPF - This article is part of a series.
Part 3: This Article

Related

eBPF for Linux Admins: Part II
·2 mins· loading · loading
Ansil H
ebpf
eBPF for Linux Admins: Part I
·4 mins· loading · loading
Ansil H
ebpf