文章目录
- 1、eBPF-Go依赖
- 1.1 Ubuntu安装ssh server
- 1.2 安装go
- 1.3 安装llvm和clang
- 1.4 安装libbpf和Linux kernel headers
- 2 编写eBPF C程序
- 3 使用bpf2go编译eBPF C程序
- 4 编写Go程序
- 5 编译运行Go应用程序
eBPF-Go是一个使用eBPF的Go库。它不依赖于C、libbpf或除标准库之外的任何其他Go库,这让它成为编写运行在各种体系结构上独立的、可移植工具的绝佳选择。
官网: https://ebpf-go.dev/
GitHub: https://github.com/cilium/ebpf
1、eBPF-Go依赖
- Linux kernel version 5.7 or later, for bpf_link support
- LLVM 11 or later 1 (clang and llvm-strip)
- libbpf headers 2
- Linux kernel headers 3
- Go compiler version supported by ebpf-go’s Go module
1.1 Ubuntu安装ssh server
本文例子是在虚拟机装的Ubuntu 22.04
Ubuntu安装ssh server,方便与主机文件传输
# 安装OpenSSH服务器软件包:
sudo apt install openssh-server# 安装完成后,SSH服务将自动启动。检查SSH服务器的状态:
sudo systemctl status ssh# 如果SSH服务没有启动,您可以使用以下命令启动它:
sudo systemctl start ssh#SSH服务在系统启动时自动启动:
sudo systemctl enable ssh
1.2 安装go
wget https://go.dev/dl/go1.22.6.linux-amd64.tar.gz
sudo tar -xzf go1.22.6.linux-amd64.tar.gz -C /usr/local# 配置环境变量
vim ~/.profile
# ~/.profile文件末尾增加环境变量
export GOPROXY=https://goproxy.cn
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
# 使配置生效
source ~/.profile
1.3 安装llvm和clang
sudo apt update
sudo apt install llvm-13 clang-13# 安装完后,配置环境变量
vim ~/.profile
# ~/.profile文件末尾增加环境变量
export PATH=$PATH:/usr/lib/llvm-13/bin
# 使配置生效
source ~/.profile# 使用以下命令查看版本
llvm-config --version
clang --version
1.4 安装libbpf和Linux kernel headers
# 安装libbpf-dev依赖,避免'bpf/bpf_helpers.h' file not found
sudo apt-get install libbpf-dev
# 软链接,避免'asm/types.h' file not found错误
ln -sf /usr/include/asm-generic/ /usr/include/asm
2 编写eBPF C程序
编写eBPF C程序,Go将引用C的结构。
counter.c
//go:build ignore#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>struct {__uint(type, BPF_MAP_TYPE_ARRAY); __type(key, __u32);__type(value, __u64);__uint(max_entries, 1);
} pkt_count SEC(".maps"); // count_packets atomically increases a packet counter on every invocation.
SEC("xdp")
int count_packets() {__u32 key = 0; __u64 *count = bpf_map_lookup_elem(&pkt_count, &key); if (count) { __sync_fetch_and_add(count, 1); }return XDP_PASS;
}char __license[] SEC("license") = "Dual MIT/GPL";
3 使用bpf2go编译eBPF C程序
创建counter.c源文件后,创建另一个名为gen.go的文件,其中包含一个//go:generate语句。当在项目目录中运行go generate时,将使用bpf2go编译eBPF C程序。
文件gen.go
package main
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go counter counter.c
在使用Go工具链之前,Go需要先声明一个Go模块。
go mod init ebpf-test
go mod tidy
添加bpf2go的依赖:
go get github.com/cilium/ebpf/cmd/bpf2go
运行go generate:
4 编写Go程序
main.go
package mainimport ("log""net""os""os/signal""time""github.com/cilium/ebpf/link""github.com/cilium/ebpf/rlimit"
)func main() {// Remove resource limits for kernels <5.11.if err := rlimit.RemoveMemlock(); err != nil {log.Fatal("Removing memlock:", err)}// Load the compiled eBPF ELF and load it into the kernel.var objs counterObjectsif err := loadCounterObjects(&objs, nil); err != nil {log.Fatal("Loading eBPF objects:", err)}defer objs.Close()ifname := "ens33" // Change this to an interface on your machine.iface, err := net.InterfaceByName(ifname)if err != nil {log.Fatalf("Getting interface %s: %s", ifname, err)}// Attach count_packets to the network interface.link, err := link.AttachXDP(link.XDPOptions{Program: objs.CountPackets,Interface: iface.Index,})if err != nil {log.Fatal("Attaching XDP:", err)}defer link.Close()log.Printf("统计虚拟网卡%s数据包..", ifname)// Periodically fetch the packet counter from PktCount,// exit the program when interrupted.tick := time.Tick(time.Second)stop := make(chan os.Signal, 5)signal.Notify(stop, os.Interrupt)for {select {case <-tick:var count uint64err := objs.PktCount.Lookup(uint32(0), &count)if err != nil {log.Fatal("Map lookup:", err)}log.Printf("收到 %d 数据包", count)case <-stop:log.Print("收到退出信号..")return}}
}
最终文件目录如下
5 编译运行Go应用程序
go build && sudo ./ebpf-test
运行效果
参考自官网英文入门文档:https://ebpf-go.dev/guides/getting-started/