使用ebpf禁止ICMP报文

本文介绍如何使用c语言与bpftools工具编写一个拒绝icmp协议报文的ebpf程序,本文需要你对c语言与ebpf具有一定基础

前言

ebpf入门网站,请自行阅读官网入门,本文不再赘述:

环境

IP: 192.168.31.200
内核: 6.8.0-45-generic

编写内核程序

完整代码如下:

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/icmp.h>
#include "bpf/bpf_helpers.h"

SEC("xdp")
int block_ping(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *ip;
    struct icmphdr *icmp;

    // 检查以太网头部是否在包的范围内
    if ((void *)(eth + 1) > data_end) {
        return XDP_PASS;
    }

    // 检查是否为 IPv4 数据包
    if (eth->h_proto != __constant_htons(ETH_P_IP)) {
        return XDP_PASS;
    }

    // 获取 IP 头
    ip = (struct iphdr *)(eth + 1);

    // 检查 IP 头部是否在包的范围内
    if ((void *)(ip + 1) > data_end) {
        return XDP_PASS;
    }

    // 检查是否为 ICMP 协议 (ping 使用的协议)
    if (ip->protocol != IPPROTO_ICMP) {
        return XDP_PASS;
    }

    // 获取 ICMP 头
    icmp = (struct icmphdr *)(ip + 1);

    // 检查 ICMP 头部是否在包的范围内
    if ((void *)(icmp + 1) > data_end) {
        return XDP_PASS;
    }

    // 检查是否为 ICMP 回显请求 (ping)
    if (icmp->type == ICMP_ECHO) {
        // 打印调试信息
        bpf_printk("Dropping ICMP ping packet\n");

        // 丢弃 ping 数据包
        return XDP_DROP;
    }

    return XDP_PASS;
}

char LICENSE[] SEC("license") = "GPL";

编译为ebpf字节码

为了方便使用Makefile调用clang将c源代码编译为ebpf字节码,Makefile文件内容如下

# Compiler and flags
CLANG := clang
CFLAGS := -target bpf -g -O2 

# Include directory for headers
INCLUDES := -I /usr/include/x86_64-linux-gnu

# Source file and output file passed as external variables
src ?= test.c
OBJ := $(patsubst %.c,%.o,$(src))

# Default rule
all: $(OBJ)

# Rule to compile .c file to .o
%.o: %.c
	$(CLANG) $(CFLAGS) $(INCLUDES) -c $< -o $@

# Clean rule to remove generated object files
clean:
	rm -f $(OBJ)

.PHONY: all clean

使用如下命令开始编译

make src=test.c

编译成功后,会在当前目录发现test.o文件,这个文件就是编译过后的ebpf字节码文件

bpftools加载

使用bpftools加载ebpf程序到网卡上

# load
bpftools prog load test.o /sys/fs/bpf/test
# 查看id
bpftools prog 
# 加载到网卡 假设id为66
bpftools attach net xdp dev ens33 id 66

查看日志打印

在ebpf程序中使用bpf_printk函数,会把内容输出到如下文件

/sys/kernel/debug/tracing/trace_pipe

image-1729164668466

挂载后测试ping

已经无法ping通主机
image

查看网卡加载情况

id为66的xdp程序已成功挂载到ens33网卡
image-1729164188421

取消挂载

同样使用bpftools程序取消挂载

bpftool net  detach xdp  dev ens33

取消挂载后测试ping

image-1729164603871