eBPF入门系列:工具及重点项目

工具

bpftool

bpftool安装

依赖包安装

ubuntu 20.04 linux 5.4.169

# apt install pkg-config libelf-dev zlib1g-dev

ubuntu 21.10 linux 5.13.0

# sudo apt install  linux-tools-common linux-tools-5.13.0-51-generic linux-cloud-tools-5.13.0-51-generic

Ubuntu 22.04

# sudo apt install pkgconf build-essential libelf-dev  linux-tools-common linux-tools-`uname -r` linux-tools-generic

bpftool 安装

# git clone --recurse-submodules https://github.com/libbpf/bpftool.git
# cd bpftool/src
# make && make V=1 install

# bpftool version
bpftool v7.0.0
using libbpf v1.0
features: libbpf_strict

bpftool 常见命令

其他命令

// 查看运行的bpf程序
# bpftool prog list
...
//编号   类型            程序名    
51544: sched_cls  name handle_policy  tag 8c2582bd04b4fb9c  gpl
        loaded_at 2022-06-23T19:25:49+0800  uid 0
        xlated 13936B  jited 8157B  memlock 16384B  map_ids 105,107,96,1214,106,101,102,1213,103
        btf_id 5906
...

// 支持的bpf程序类型以及对应的help函数
# bpftool feature
# bpftool feature probe

// 把内核已经加载的 prog 程序固定到文件系统
$ bpftool prog pin name hello /sys/fs/bpf/hi

// 查看link列表
$ bpftool link list

// 生成包含所有bpf程序依赖的内核数据结构header文件vmlinux.h
$ 

// 查看perf相关的程序
$ bpftool perf show

加载程序到内核

$ bpftool prog load hello.bpf.o /sys/fs/bpf/hello  #此命令成功执行没有输出

检查加载到内核的程序

$ bpftool prog list 
...
540: xdp name hello tag d35b94b4c0c10efb gpl
 loaded_at 2022-08-02T17:39:47+0000 uid 0
 xlated 96B jited 148B memlock 4096B map_ids 165,166
 btf_id 254
 
$ bpftool prog show id 540 --pretty 
{
 "id": 540,        # bpf程序ID,自动生成
 "type": "xdp",       # bpf 程序类型
 "name": "hello",   		# bpf 程序名字
 "tag": "d35b94b4c0c10efb",			# 标签,程序的另一个唯一标识 
 "gpl_compatible": true,				# 使用了 GPL兼容的许可证
 "loaded_at": 1659461987,				# 加载时间点的时间戳
 "uid": 0,											# 用户ID,0为root
 "bytes_xlated": 96,						# 编译为eBPF字节码的大小
 "jited": true,									# 是否使用JIT编译
 "bytes_jited": 148,						# JIT编译后的大小
 "bytes_memlock": 4096,					# 内存预留大小
 "map_ids": [165,166						# 使用的map id
 ],	
 "btf_id": 254									# BTF块ID
}

// 查看程序的命令
$ bpftool prog show id 540
$ bpftool prog show name hello
$ bpftool prog show tag d35b94b4c0c10efb
$ bpftool prog show pinned /sys/fs/bpf/hello

// 查看eBPF字节码内容
$ bpftool prog dump xlated name hello 
int hello(struct xdp_md * ctx):
; bpf_printk("Hello World %d", counter);
 0: (18) r6 = map[id:165][0]+0
 2: (61) r3 = *(u32 *)(r6 +0)
 3: (18) r1 = map[id:166][0]+0
 5: (b7) r2 = 15
 6: (85) call bpf_trace_printk#-78032
; counter++; 
 7: (61) r1 = *(u32 *)(r6 +0)
 8: (07) r1 += 1
 9: (63) *(u32 *)(r6 +0) = r1
; return XDP_PASS;
 10: (b7) r0 = 2
 11: (95) exit
 
 // 查看JIT编译后的机器码
 $ bpftool prog dump jited name hello

挂载到内核事件

XDP类型

$ bpftool net attach xdp id 540 dev eth0

$ bpftool net list 
xdp:
eth0(2) driver id 540
tc:
flow_dissector:

$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdp qdisc
fq_codel state UP
mode DEFAULT group default qlen 1000
 ...
 prog/xdp id 540 tag 9d0e949f89f1a82c jited

查看 trace 输出

// 方法一
$ cat /sys/kernel/debug/tracing/trace_pipe

// 方法二
$ bpftool prog tracelog

如果两种trace输出都没有内容,则需要检查 /sys/kernel/debug/tracing/tracing_on 配置是否开启,1表示开启 trace。

Map常见操作

$ bpftool map list
165: array name hello.bss flags 0x400
 key 4B value 4B max_entries 1 memlock 4096B
 btf_id 254
166: array name hello.rodata flags 0x80
 key 4B value 15B max_entries 1 memlock 4096B
 btf_id 254 frozen
 
 $ bpftool map dump name hello.bss
[{
 "value": {
 ".bss": [{
 "counter": 11127
 }
 ]
 }
 }]
 //使用name查看map内容,-p输出十六进制内容
 $ sudo bpftool map dump name config -p
[{
        "key": ["0x00","0x00","0x00","0x00"
        ],
        "value": ["0x48","0x65","0x79","0x20","0x72","0x6f","0x6f","0x74","0x20","0x31","0x21","0x00"
        ],
        "formatted": {
            "key": 0,
            "value": {
                "message": "Hey root 1!"
            }
        }
    }
]
// 更新map
$ sudo bpftool map update id 407 key 0x00 0x00 0x00 0x00 value 0x48 0x65 0x79 0x20 0x72 0x6f 0x6f 0x74 0x20 0x31 0x21 0x00

卸载程序

$ bpftool net detach xdp dev eth0

$ $ bpftool net list 
xdp:
tc:
flow_dissector:

删除内核加载的程序

$ rm /sys/fs/bpf/hello
$ bpftool prog show name hello

eBPF 项目

入门必看,这个项目下有各种场景的ebpf小工具,该项目刚开始都是使用 Python 调用 eBPF 程序实现的工具,后面又用 libbpf 重写了之前的工具。

BCC依赖安装

https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu---source

# For Jammy (22.04)
sudo apt install -y bison build-essential cmake flex git libedit-dev \
libllvm14 llvm-14-dev libclang-14-dev python3 zlib1g-dev libelf-dev libfl-dev python3-distutils

Install and compile BCC

git clone https://github.com/iovisor/bcc.git
mkdir bcc/build; cd bcc/build
cmake ..
make
sudo make install
cmake -DPYTHON_CMD=python3 .. # build python3 binding
pushd src/python/
make
sudo make install
popd

编译libbpf-tools

$ git clone https://github.com/iovisor/bcc.git
$ git submodule update --init --recursive
$ cd libbpf-tools
$ make

常见报错:

1、编译过程出现以下报错:

$ make 
...
In file included from javagc.bpf.c:6:
In file included from /home/mars/ebpf-demo/bcc/libbpf-tools/.output/bpf/usdt.bpf.h:6:
/usr/include/linux/errno.h:1:10: fatal error: 'asm/errno.h' file not found
#include <asm/errno.h>
         ^~~~~~~~~~~~~
1 error generated.
make: *** [Makefile:198: /home/mars/ebpf-demo/bcc/libbpf-tools/.output/javagc.bpf.o] Error 1

解决方式:

$ sudo ln -s /usr/include/asm-generic /usr/include/asm

通过脚本语言写eBPF程序,方便写一些简单的demo程序

安装

ubuntu 20.04 linux 5.4.169

# snap install bpftrace

ubuntu 21.10/22.04

# sudo apt-get install -y bpftrace

bpftrace常见命令


# 查询所有内核插桩和跟踪点
sudo bpftrace -l

# 使用通配符查询所有的系统调用跟踪点
sudo bpftrace -l 'tracepoint:syscalls:*'

# 使用通配符查询所有名字包含"execve"的跟踪点
sudo bpftrace -l '*execve*'

# 查询execve入口参数格式
$ sudo bpftrace -lv tracepoint:syscalls:sys_enter_execve
tracepoint:syscalls:sys_enter_execve
    int __syscall_nr
    const char * filename
    const char *const * argv
    const char *const * envp

# 查询execve返回值格式
$ sudo bpftrace -lv tracepoint:syscalls:sys_exit_execve
tracepoint:syscalls:sys_exit_execve
    int __syscall_nr
    long ret

使用bpftrace跟踪短时进程

# 使用 bpftrace 来跟踪短时进程
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve,tracepoint:syscalls:sys_enter_execveat { printf("%-6d %-8s", pid, comm); join(args->argv);}'
- pid  和  comm  是 bpftrace 内置的变量,分别表示进程 PID 和进程名称(你可以在其官方文档中找到其他的内置变量);
- join(args->argv)  表示把字符串数组格式的参数用空格拼接起来,再打印到终端中。对于跟踪点来说,你可以使用  args->参数名  的方式直接读取参数(比如这里的  args->argv  就是读取系统调用中的  argv  参数)。

# 增加执行时间和父进程id
bpftrace --include linux/sched.h -e 'tracepoint:syscalls:sys_enter_execve,tracepoint:syscalls:sys_enter_execveat { time("%H:%M:%S ");printf("%-6d %-6d %-8s ", pid,curtask->parent->pid, comm); join(args->argv)}'

eCapture

eCapture 是一款基于 eBPF 技术实现的用户态数据捕获工具,其主要功能包括:

  • 不需要 CA 证书,即可捕获 HTTPS/TLS 通信数据的明文。

  • 在 bash 审计场景,可以捕获 bash 命令。

  • 数据库审计场景,可以捕获 mysqld/mariadDB的SQL 查询。

Cilium

Cilium 是一个基于 eBPF 的高性能容器网络方案。其主要功能特性包括:

  • 安全上,支持 L3/L4/L7 安全策略,这些策略按照使用方法又可以分为:

    • 基于身份的安全策略。

    • 基于 CIDR 的安全策略。

    • 基于标签的安全策略。

  • 网络上,支持三层平面网络(flat layer 3 network),如:

    • 覆盖网络(Overlay),包括 VXLAN 和 Geneve 等。

    • Linux 路由网络,包括原生的 Linux 路由和云服务商的高级网络路由等。

  • 提供基于 BPF 的负载均衡。

  • 提供便利的监控和排错能力。

eBPF Exporter

eBPF Exporter 是一个将自定义 eBPF 跟踪数据导出到 Prometheus 的工具,它实现了 Prometheus 获取数据的 API,Prometheus 可以通过这些 API 主动拉取到自定义的 eBPF 跟踪数据。

libbpf-bootstrap

Last updated