Skip to content

Introduction

eBPF is a technology which enables users to extend the functionality of operating systems in a fast and secure way.

eBPF programs can be attached at different points in the kernel and will be called like a function. Programs get called with a context which is a struct with information the kernel is making easily available to the program. Typical examples are a socket buffer or CPU registers.

Program like functions also have return values, the meaning of which is again determined by the return type.

eBPF program are typically written in C and compiled with LLVM.

eBPF programs are loaded into the kernel using BPF syscall, the userspace program that does this is referred to as a loader. When the loader loads a program the kernel will verify that the program is safe.

Helper Functions

The programs can interact with rest of kernel module using helper functions. These helper functions take upto 5 arguments and return a single return value.

KFuncs

Instead of creating a special "eBPF-only" function, the kernel developers simply "annotate" existing kernel functions to make them visible to eBPF. This allows eBPF to access a much wider range of kernel features without the overhead of maintaining a separate API.

Objects

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>

SEC("xdp")
int dns_mitigation(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    // Everything starts with a pointer to the Ethernet header
    struct ethhdr *eth = data;
    // Before reading the Ethernet header, we MUST prove it fits in the packet
    if ((void *)(eth + 1) > data_end)
                return XDP_PASS; 

Step-by-Step Packet Diving

  1. Check for IPv4: Ensure eth->h_proto == htons(ETH_P_IP).

  2. Jump to IP Header: struct iphdr *iph = data + sizeof(struct ethhdr);.

  3. Check for UDP: Ensure iph->protocol == IPPROTO_UDP.

  4. Jump to UDP Header: struct udphdr *udp = (void *)iph + (iph->ihl * 4);.

  5. Check for Port 53: if (ntohs(udp->dest) != 53) return XDP_PASS;.

  6. Find DNS ID: The DNS Transaction ID is the first 2 bytes after the UDP header

What happens when a network packet reaches the NIC

When the network packet reaches the NIC, the following happens:

  • When a network packet arrives at the server’s Network Interface Card (NIC), it is first processed by the NIC’s hardware logic. This includes layer-2 framing validation, MAC address filtering, and possibly early classification (such as RSS hash or VLAN tagging). Once the packet is deemed valid and destined for the host, the NIC places the packet into a pre-allocated DMA-capable buffer in host memory location pointed by the receive descriptor ring.
  • To notify the host of new packet arrival, the NIC triggers an Interrupt Request (IRQ). These interrupts signal the CPU to invoke the appropriate kernel driver routine to begin packet processing as shown in the diagram below
  • Once the interrupt is acknowledged, the Linux kernel’s networking stack takes over. It processes packets in a multi-layered pipeline:

  • Data Link Layer: Validates Ethernet frame structure and MAC headers.

  • Network Layer: Handles IP header parsing, routing decisions, and fragmentation (if necessary).
  • Transport Layer: Manages TCP/UDP headers, sequence numbers, port mapping, and flow control.

Packets that successfully pass through the kernel stack are stored in per-socket receive queues, usually called socket buffers (skb). User-space applications retrieve this data using system calls such as recv(), read(), or poll().

  • In blocking mode, the application is put to sleep until data arrives.
  • In non-blocking mode, the call returns immediately if no data is available.

The socket buffer acts as a staging area between the asynchronous kernel processing and application-level consumption.

Pasted image 20260409003349.png

Pasted image 20260409004446.png

eBPF sits directly within the Linux kernel, acting as a programmable layer that attaches to hooks at the earliest stages of the networking stack—specifically at the network driver level (XDP) or immediately after Traffic Control. This allows packets to be dropped, redirected, or modified before traversing the full networking stack, offering high-performance networking, security, and observability. 

Key Networking Hook Points:

  • eXpress Data Path (XDP): The lowest-level hook, located directly at the NIC driver upon packet arrival, enabling maximum performance by processing packets before the kernel allocates resources (skb).
  • Traffic Control (TC) Ingress/Egress: Sits within the kernel's TC layer, allowing eBPF programs to inspect, filter, or redirect traffic as it enters or leaves the networking stack.
  • Socket Level (cgroups): Can hook into socket creation to manage networking policies for specific containerized applications.