ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【踩坑】使用libbpfgo构建你的第一个eBPF项目

2021-06-13 14:30:07  阅读:755  来源: 互联网

标签:sudo libbpf bpf eBPF libbpfgo 编译 构建 go


文章目录


前言

本文参考:How to Build eBPF Programs with libbpfgo

但实际运行时出现了许多情况,因此记录分享。

写在最前:使用libbpf请将系统更新到最新版本,本文环境为Ubuntu21.04。
Ubuntu最新版本下载


一、为什么使用libbpf?

libbpf是可以在用户空间和 bpf 程序中导入的库。它为开发人员提供了一个用于加载 bpf 程序并与之交互的 API。

在使用libbpf前,先使用bcc对eBPF相关知识进行学习运行,学习曲线将更平滑。相对于bcc,libbpf与BPF CO-RE的实际编译部署的难度增大了。

然而性能优化大师 Brendan Gregg 在用 libbpf + BPF CO-RE 转换一个 BCC 工具后给出了性能对比数据:

As my colleague Jason pointed out, the memory footprint of opensnoopas CO-RE is much lower than opensnoop.py. 9 Mbytes for CO-RE vs 80 Mbytes for Python.

我们可以看到在运行时相比 BCC 版本,libbpf + BPF CO-RE 版本节约了近 9 倍的内存开销,这对于物理内存资源已经紧张的服务器来说会更友好。

二、环境搭建

大概是最困难的部分,clang相关的环境需要你的Linux系统升级至较新的版本,而go相关的环境缘于一些原因较难安装上。

1.Libbpf相关环境搭建

实际上要装很多的环境,参考了网上的文章。

sudo apt install build-essential git make libelf-dev strace tar bpfcc-tools libbpf-dev linux-headers-$(uname -r) gcc-

安装clang和llvm,当前最新版本为12。

sudo apt install clang llvm

如果存在因为某些版本问题无法安装的软件时,建议:

  1. 升级到最新版系统
  2. 使用sudo aptitude install代替sudo apt install

安装完成后,于命令行输入

clang -v

见到类似下图的输出则安装成功:
在这里插入图片描述

2.GO环境搭建

sudo apt-get golang

由于未来将使用go get相关命令来获取包,此处建议先换源,否则会出现connection refused的情况。

使用GOPROXY,在命令行输入:

export GOPROXY=https://goproxy.io
export GO111MODULE=on

三、使用libbpfgo编译运行eBPF程序

构建基于libbpf的BPF应用需要使用BPF CO-RE包含的几个步骤:

  • 生成带所有内核类型的头文件vmlinux.h;
  • 使用Clang(版本10或更新版本)将BPF程序的源代码编译为.o对象文件;
  • 使用 libbpfgo 将其编译为二进制文件,加载到内核中并监听输出。

步骤1:生成头文件

首先新建一个目录作为本次eBPF程序的目录,并将目录权限设置为可以新建或删除文件。

sudo mkdir libbpf-hello
sudo chmod 777 libbpf-hello

进入该目录下,生成带所有内核类型的头文件vmlinux.h

cd libbpfgo-hello/
sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

看到目录下出现了vmlinux.h文件且里面有内容则成功。

步骤2:Clang将BPF程序的源代码编译为.o对象文件

首先,创建文件simple.h,内容如下:

typedef struct process_info {
    int pid;
    char comm[100];
} proc_info;

simple.h 包含我们想要包含在 bpf 和用户空间代码中的结构定义。

然后,创建文件simple.bpf.c,这是本次BPF程序原代码。

#include "vmlinux.h"
#include <bpf/bpf_helpers.h> 
#include "simple.h"

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 1 << 24);
} events SEC(".maps");

long ringbuffer_flags = 0;

SEC("kprobe/sys_execve")
int kprobe__sys_execve(struct pt_regs *ctx)
{
    __u64 id = bpf_get_current_pid_tgid();
    __u32 tgid = id >> 32;
    proc_info *process;

    // Reserve space on the ringbuffer for the sample
    process = bpf_ringbuf_reserve(&events, sizeof(proc_info), ringbuffer_flags);
    if (!process) {
        return 0;
    }
    process->pid = tgid;
    bpf_get_current_comm(&process->comm, 100);
    bpf_ringbuf_submit(process, ringbuffer_flags);
    return 0;
}

最后,使用Clang进行编译。

clang -g -O2 -c -target bpf -o simple.bpf.o simple.bpf.c

成功编译后,目录结构应该如下:
在这里插入图片描述

步骤3:使用GO编译为二进制文件并运行

(注:本阶段的一些命令需要不使用sudo才能运行,否则连不上go的网络,我也不知道为什么…)

创建文件main.go,内容如下:

package main

import "C"

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	bpf "github.com/aquasecurity/tracee/libbpfgo"
)

func main() {

	bpfModule, err := bpf.NewModuleFromFile("simple.bpf.o")
	if err != nil {
		os.Exit(-1)
	}
	defer bpfModule.Close()

	bpfModule.BPFLoadObject()
	prog, err := bpfModule.GetProgram("kprobe__sys_execve")
	if err != nil {
		os.Exit(-1)
	}

	_, err = prog.AttachKprobe("__x64_sys_execve")
	if err != nil {
		os.Exit(-1)
	}

	eventsChannel := make(chan []byte)
	rb, err := bpfModule.InitRingBuf("events", eventsChannel)
	if err != nil {
		os.Exit(-1)
	}

	rb.Start()

	for {
		event := <-eventsChannel
		pid := int(binary.LittleEndian.Uint32(event[0:4])) // Treat first 4 bytes as LittleEndian Uint32
		comm := string(bytes.TrimRight(event[4:], "\x00")) // Remove excess 0's from comm, treat as string
		fmt.Printf("%d %v\n", pid, comm)
	}

	rb.Stop()
	rb.Close()
}

利用go的module工具,在目录下输入:

go mod init libbpfgo-hello

但是此时生成的go.mod是空的,为了加载所需module,需要输入:

go mod tidy

如果显示connection refused的话,只能使用科学上网手段了,如:

proxychains go mod tidy

加载成功后,目录下将会多出go.mod与go.sum两个文件。并会有如下显示:

在这里插入图片描述
之后,使用go进行编译生成二进制文件。在命令行输入:

CC=gcc CGO_CFLAGS="-I /usr/include/bpf" CGO_LDFLAGS="/usr/lib/x86_64-linux-gnu/libbpf.a" go build -o libbpfgo-prog

注:文件libbpf.a的地址可能不一样,建议使用find命令找一下本机的libbpf.a文件。

编译完成后得到二进制文件 libbpfgo-prog ,文件目录结构如下:
在这里插入图片描述
于命令行输入以下命令运行二进制文件

sudo ./libbpfgo-prog

随着系统运行,能够以下列格式捕捉到系统的exec信息
在这里插入图片描述
至此,你的第一个通过libbpfgo构建的eBPF项目成功运行!


后记

关于本文代码中的详细解释,可见How to Build eBPF Programs with libbpfgo

标签:sudo,libbpf,bpf,eBPF,libbpfgo,编译,构建,go
来源: https://blog.csdn.net/weixin_43144516/article/details/117871024

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有