6

libbpfgo 使用示例:搭建开发环境以及编写第一个 ebpf 程序

 2 years ago
source link: https://mozillazg.com/2022/05/ebpf-libbpfgo-develop-env-and-hello-world.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言

本文记录搭建 libbpfgo 开发环境以及使用 libbpfgo 编写一个简单的 ebpf 示例程序。

搭建开发环境

为了简单起见,我这里使用 vagrant 搭建虚拟机开发环境:

  1. 安装 vagrant-env 插件:
$ vagrant plugin install vagrant-env
  1. clone 示例代码仓库:
$ mkdir -p $GOPATH/src/github.com/mozillazg && \
  cd $GOPATH/src/github.com/mozillazg && \
  git clone https://github.com/mozillazg/hello-libbpfgo.git && \
  cd hello-libbpfgo && \
  git submodule update --init --recursive
  1. 配置 .env 文件(用于将本机的 GOPATH 挂载到虚拟机中):
# 修改 .env 文件,将 GOPATH 的值修改为本机对应的路径
$ cp .env.example .env
  1. 启动虚拟机:
$ vagrant up
  1. 等虚拟机就位后,进入虚拟机:
$ vagrant ssh

编写示例程序

这里使用 hello-libbpfgo 中 01-hello-world 目录下的示例程序为例简单介绍一下示例程序。

main.ebpf.c:

#include "vmlinux.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
                char file_name[256];
                bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));

                char fmt[] = "open file %s\n";
                bpf_trace_printk(fmt, sizeof(fmt), &file_name);

                return 0;
}

main.ebpf.c 这个程序比较简单,就是hook do_sys_openat2 这个内核函数的调用, 将函数调用参数中的 filename 信息给记录下来。

主要看一下,怎么在 go 程序中使用 libbpfgo 调用这个 ebpf 程序:

main.go:

package main

import (
        "fmt"
        "time"

        bpf "github.com/aquasecurity/libbpfgo"
)

func main() {
        bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
        if err != nil {
                panic(err)
        }
        defer bpfModule.Close()
        if err := bpfModule.BPFLoadObject(); err != nil {
                panic(err)
        }
        prog, err := bpfModule.GetProgram("kprobe__do_sys_openat2")
        if err != nil {
                panic(err)
        }
        if _, err := prog.AttachKprobe("do_sys_openat2"); err != nil {
                panic(err)
        }

        for {
                fmt.Println("Waiting...")
                time.Sleep(10 * time.Second)
        }
}

从上面的 go 程序中可以看到,使用 libbpfgo 调用 ebpf 程序主要有四个步骤:

  1. 通过 bpf.NewModuleFromFile 方法读取编译好的 .o 文件
  2. 使用 bpfModule.BPFLoadObject() 加载读取的 .o 文件中对象信息
  3. 使用 bpfModule.GetProgram 获取 ebpf 程序中对应的 hook 函数
  4. 如果这个函数是个 kprobe hook 函数,那么就调用 prog.AttachKprobe 把它 attach 到对应的内核函数hook中。

下面编译程序然后看一下对应的效果:

$ vagrant ssh
$ cd $GOPATH/src/github.com/mozillazg/hello-libbpfgo/01-hello-world && \
    make
$ sudo ./main-static

另开一个终端,查看 ebpf 中打印的信息:

$ vagrant ssh
$ sudo cat /sys/kernel/debug/tracing/trace_pipe

runc:[2:INIT]-100616  [000] d...  5527.233315: bpf_trace_printk: open file /proc/self/fd

runc:[2:INIT]-100616  [000] d...  5527.233641: bpf_trace_printk: open file /proc/self/status

runc:[2:INIT]-100616  [000] d...  5527.233802: bpf_trace_printk: open file /etc/passwd

runc:[2:INIT]-100616  [000] d...  5527.233829: bpf_trace_printk: open file /etc/group

更多信息,详见 https://github.com/mozillazg/hello-libbpfgo/tree/master/01-hello-world


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK