Post

ebpf开发笔记

ebpf开发笔记

内核函数 Hook

Kprobe

Kprobe 是一种动态追踪技术,允许您在几乎所有内核函数的入口和返回点插入探针。这使得它成为 eBPF 程序中最常用的 Hook 点之一。

验证函数是否可以 Hook

在开发 eBPF 程序之前,需要确认目标内核函数是否存在且可以被 Hook:

1
cat /proc/kallsyms | grep nf_nat_ipv4_manip_pkt

查看 Tracepoint 格式

在使用 tracepoint 时,需要了解其数据格式:

1
cat /sys/kernel/debug/tracing/events/sock/inet_sock_set_state/format

Ref:

  • https://mozillazg.com/2022/05/ebpf-libbpf-raw-tracepoint-common-questions.html#hidsec

系统信息获取

进程和线程信息

在 eBPF 程序中,经常需要获取当前进程和线程的信息:

1
2
3
4
5
// 获取当前线程ID
__u32 tid = bpf_get_current_pid_tgid();

// 获取当前进程ID(通过右移32位获取)
__u32 pid = bpf_get_current_pid_tgid() >> 32;

说明:

  • bpf_get_current_pid_tgid() 返回一个64位的值
  • 低32位是线程ID (tid)
  • 高32位是进程ID (pid)
  • 在Linux中,线程也被视为轻量级进程,tid实际上是线程的进程ID

CPU信息

获取当前CPU的ID:

1
2
// 获取当前CPU的ID
__u32 cid = bpf_get_smp_processor_id();

说明:

  • bpf_get_smp_processor_id() 返回当前正在执行eBPF程序的CPU编号
  • 这个值在SMP(对称多处理器)系统中特别有用
  • 可以用于分析负载分布或实现per-CPU的数据结构

调试技巧

直接打印调试信息

在 eBPF 程序开发过程中,相比使用复杂的 map 和 perf 输出机制,可以使用 bpf_printk 快速打印调试信息:

1
2
3
4
#include <bpf/bpf_helpers.h>

// 在 Hook 函数中使用
bpf_printk("hook here...");

查看调试输出:

1
sudo cat /sys/kernel/debug/tracing/trace_pipe

注意:这种方式输出的信息不会打印到标准输出,而是通过管道输出到用户空间。

Ref:

  • https://docs.ebpf.io/ebpf-library/libbpf/ebpf/bpf_printk/

  • https://docs.ebpf.io/linux/helper-function/bpf_trace_printk/

数据处理

字节序转换

内核空间

内核中的网络相关数据通常使用大端序(Big Endian)存储,数据类型通常标记为 be16be32 等。

1
2
// 端口号转换(大端序到主机字节序)
event.R_port = bpf_ntohs(event.R_port);

用户空间

在用户空间进行字节序转换需要使用 arpa/inet.h 提供的函数:

1
2
3
4
5
6
7
8
#include <arpa/inet.h>

// 端口号转换
uint16_t port = ntohs(_event->R_port);

// IP 地址转换
unsigned int ip = 3232252161;
ip = htonl(ip);

IP 地址转换

将 IP 地址转换为字符串格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <arpa/inet.h>

// IPv4 地址转换
char local_ip[INET_ADDRSTRLEN];
char external_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &_event->L_ip_v4, local_ip, sizeof(local_ip));
inet_ntop(AF_INET, &_event->R_ip_v4, external_ip, sizeof(external_ip));

// IPv6 地址转换
char local_ip[INET6_ADDRSTRLEN];
char external_ip[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &_event->L_ip_v6, local_ip, sizeof(local_ip));
inet_ntop(AF_INET6, &_event->R_ip_v6, external_ip, sizeof(external_ip));
This post is licensed under CC BY 4.0 by the author.