Examples
Inter-process Communication
Attach a program to an XDP-event. Transformed C sources code into eBPF bytecode and then compiled to machine code. Load the program in the kernel and attach it to event.
hello.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
int counter = 0;
SEC("xdp")
int hello(struct xdp_md *ctx) {
bpf_printk("Hello World %d", counter);
counter++;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
hello-func.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
static __attribute((noinline)) int get_opcode(struct bpf_raw_tracepoint_args *ctx) {
return ctx->args[1];
}
SEC("raw_tp/")
int hello(struct bpf_raw_tracepoint_args *ctx) {
int opcode = get_opcode(ctx);
bpf_printk("Syscall: %d", opcode);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
-
Compilation in eBPF bytecode \ make
-
Loading the program in the kernel\ sudo bpftool prog load hello.bpf.o /sys/fs/bpf/hello
(hello is the program's name)
-
Check if the program is in the kernel \ ls /sys/fs/bpf
-
Some information of the program \ sudo bpftool prog list
-
Attach the program to an event \ sudo bpftool net attach xdp id 540 dev lo
-
Check the output \ sudo cat /sys/kernel/debug/tracing/trace_pipe
-
Detach the program \ sudo bpftool net detach xdp dev lo
-
Remove the program to the kernel \ sudo rm /sys/fs/bpf/hello
eBPF program :
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "simple.h"
char LICENSE[] SEC("license") = "GPL";
int count=0;
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 40000);
__type(key, u32);
__type(value, struct typetest);
} my_map SEC(".maps");
SEC("tp/signal/signal_deliver")
int test(void *params)
{
struct typetest t;
t.pid = bpf_get_current_pid_tgid() >> 32;
t.count = count;
t.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
if( ! bpf_map_lookup_elem(&my_map,&t.pid) ){
bpf_map_update_elem(&my_map,&t.pid,&t,BPF_ANY);
}
count ++;
return 0;
}
User space program :
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <unistd.h>
#include "simple.h"
#include "simple.skel.h"
void print_map_array(struct simple *skel){
struct typetest t;
int *cur_key= NULL;
int next_key;
int err;
if( (err = bpf_map__get_next_key(skel->maps.my_map, cur_key, &next_key,sizeof(int)))==0){
printf("-----------------------------------------------------------------------------------\n");
for (;err == 0;) {
bpf_map__lookup_elem(skel->maps.my_map, &next_key,sizeof(int), &t,sizeof(struct typetest),BPF_ANY);
printf("PID = %d, count = %d , UID = %d\n",t.pid,t.count,t.uid);
cur_key = &next_key;
err = bpf_map__get_next_key(skel->maps.my_map, cur_key, &next_key,sizeof(int));
}
printf("-----------------------------------------------------------------------------------\n");
}
}
int main(void)
{
struct simple *skel = simple__open();
simple__load(skel);
simple__attach(skel);
while(true){
sleep(1);
print_map_array(skel);
}
return 0;
}
Marche bien mais je conseille de tester en changeant d'évènement
- structure de données utilisé ( à mettre dans un .h ):
en gros, le programme affiche le contenu de la Hash-map toute les 1 seconde ( la Hash-map contient de éléments de type struct typeset)
Pour utiliser un evenement du kernel, on peut aller voir la liste des evenements disponibles dans
De la, on peut choisir l'evenement que l'on veut de la forme "type d'evenement : evenement" Ensuite, on peut modifier le code suivant pour utiliser l'evenement choisit :
test.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
struct my_syscalls_enter_sysinfo{
unsigned short common_type;
unsigned char common_flags;
unsigned char common_preempt_count;
int common_pid;
int syscall_nr;
void *info;
};
SEC("tp/syscalls/sys_enter_sysinfo")
void testmdr(struct my_syscalls_enter_sysinfo *ctx){
bpf_printk("%d\n", ctx->common_pid);
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
Nous devons remplacer SEC(tp/../..) par SEC(tp/type d'evenement/evenement) et remplacer la structure de données pour qu'elle corresponde au format de l'évenement. Pour récupérer le format de l'évenement il suffit de faire
Ainsi s'affichera le format de la structure de donnée de l'évenement.
Quand les modifications sont faites, il suffit d'ajouter ces fichiers au dossier
test.c
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <bpf/libbpf.h>
#include "test.skel.h"
int main(void)
{
struct test_bpf *skel = test_bpf__open();
test_bpf__load(skel);
test_bpf__attach(skel);
while(true){
sleep(1);
}
return 0;
}
Makefile
BIN="test"
all:
@bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
@clang -g -O3 -target bpf -D__TARGET_ARCH_x86 -c $(BIN).bpf.c -o $(BIN).bpf.o
@bpftool gen skeleton $(BIN).bpf.o > $(BIN).skel.h
@clang $(BIN).c -lbpf -lelf -o $(BIN)
.PHONY: clean
clean:
@rm -rf *.o *.skel.h vmlinux.h $(BIN)
Il n'y a plus qu' compiler avec make, ce qui loadera le programme dans le kernel et attachera celui ci à l'évenement souhaité. Il faut ensuite execcuter le programme et pour afficher les prints, il faut regarder dans le trace_pipe à l'adresse