commit 9b16db0c087f9206046244e8fa7665393dc08a9c Author: Andreas Gruhler <andreas.gruhler@adfinis.com> Date: Fri, 12 May 2023 15:25:32 +0200 Initial commit Diffstat:
A | .gitignore | | | 4 | ++++ |
A | README.md | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | packer/config/ks.cfg | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
A | packer/libvirt-centos9.pkr.hcl | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
A | packer/scripts/ebpf-lab.sh | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | samples/hello-raw-tracepoint.py | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | samples/hello.py | | | 15 | +++++++++++++++ |
7 files changed, 293 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -0,0 +1,4 @@ +**ssh-config +**manifest.json +**.swp +packer/output diff --git a/README.md b/README.md @@ -0,0 +1,64 @@ +# eBPF Lab +This is a lab environment with example code from the book Learning eBPF by Liz +Rice: https://github.com/lizrice/learning-ebpf + +## Lab Environment +A packer configuration was created in the `packer` folder to build and +configure a Centos 8 stream VM with: +* bcc (https://github.com/iovisor/bcc) +* bpftool (https://github.com/libbpf/bpftool) +* bpftrace (https://github.com/iovisor/bpftrace) + +Unfortunately, some blockers (broken functionality, missing features) were +encountered pretty early in the eBPF learning process when using the pre-built +software from the distribution repositories. Therefore, most recent components +are built from the main branch with the idea of a reproducible lab environment. + +The Packer build allows to reproduce and develop the Lab image. To create the +qemu image with Packer: +``` +cd packer +packer build libvirt-centos9.pkr.hcl +``` + +## Setup and Connect to Lab VM + +Configure location of qemu image: +``` +qemuimg=output/centos9-20230512-132055/centos9-20230512-132055.qcow2 +``` + +Create the KVM lab VM: +``` +sudo virt-install --name ebpf-lab --description "eBPF Lab" \ + --osinfo=centos-stream9 --ram=4096 --vcpus=2 \ + --disk path=$qemuimg --boot hd --wait 0 --autostart +``` + +Create a sample ssh config: +``` +domip=$(sudo virsh -q domifaddr ebpf-lab | awk '{print $4}' | cut -d/ -f 1) + +cat << EOF > ssh-config +Host ebpf-lab + HostName $domip + User root +EOF +``` + +Connect to the VM with password `root`: +``` +ssh ebpf-lab -F ssh-config +``` + +## Usage +* Work as the root user + +## Lab cleanup +Remove the VM and associated resource: +``` +sudo virsh destroy ebpf-lab +sudo virsh undefine ebpf-lab +rm ssh-config +rm $qemuimg +``` diff --git a/packer/config/ks.cfg b/packer/config/ks.cfg @@ -0,0 +1,46 @@ +#version=RHEL9 +#ignoredisk --only-use=sda +clearpart --none --initlabel +autopart --type=lvm + +cmdline + +# Use network installation +repo --name="AppStream" --baseurl="https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/" +url --url="https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os" + +# Keyboard layouts +keyboard --vckeymap=us --xlayouts='us' +# System language +lang en_US.UTF-8 +# System timezone +timezone Europe/Zurich --utc + +# Network information +network --bootproto=dhcp --device=link --ipv6=auto --activate +network --hostname=localhost.localdomain + +# Root password +rootpw root + +# Do not configure the X Window System +skipx +# System services +services --enabled=sshd,chronyd +firewall --enabled --service=ssh + +reboot + +%packages +@core +@^minimal install +qemu-guest-agent +%end + +# Once the installation is complete, +# but before the system is rebooted for the first time +%post +# enable the password-based SSH root logins +echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf +%end + diff --git a/packer/libvirt-centos9.pkr.hcl b/packer/libvirt-centos9.pkr.hcl @@ -0,0 +1,39 @@ +variable "manifest" { + type = string + default = "manifest.json" +} + +source "qemu" "centos9" { + accelerator = "kvm" + boot_command = ["<up><tab> rd.shell ip=dhcp inst.cmdline inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter>"] + boot_wait = "5s" + cpu_model = "host" + disk_interface = "virtio" + disk_size = "14000" + memory = "4096" + cpus = "2" + net_device = "virtio-net" + format = "qcow2" + http_directory = "config" + iso_checksum = "file:https://linuxsoft.cern.ch/centos-stream/9-stream/BaseOS/x86_64/iso/CentOS-Stream-9-latest-x86_64-boot.iso.SHA256SUM" + iso_url = "https://linuxsoft.cern.ch/centos-stream/9-stream/BaseOS/x86_64/iso/CentOS-Stream-9-latest-x86_64-boot.iso" + shutdown_command = "echo 'packer' | sudo -S shutdown -P now" + ssh_timeout = "20m" + ssh_username = "root" + ssh_password = "root" + vm_name = "centos9-${formatdate("YYYYMMDD-hhmmss", timestamp())}.qcow2" + output_directory = "output/centos9-${formatdate("YYYYMMDD-hhmmss", timestamp())}" +} + +build { + sources = ["source.qemu.centos9"] + + provisioner "shell" { + script = "scripts/ebpf-lab.sh" + } + + post-processor "manifest" { + output = "${var.manifest}" + strip_path = true + } +} diff --git a/packer/scripts/ebpf-lab.sh b/packer/scripts/ebpf-lab.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +#set -o xtrace + +dnf config-manager --set-enabled crb +dnf install -y epel-release epel-next-release + +# Install BCC prerequisites +# https://github.com/iovisor/bcc/blob/master/INSTALL.md +dnf install -y bison cmake ethtool flex git iperf3 libstdc++-devel python3-netaddr \ + python3-pyroute2 python3-pip python3-docutils gcc gcc-c++ make \ + zlib-devel elfutils-libelf-devel clang-15.0.7 clang-devel-15.0.7 \ + llvm-15.0.7 llvm-devel-15.0.7 llvm-static-15.0.7 ncurses-devel zip unzip netperf + +# Lock Clang and LLVM versions +dnf install -y python3-dnf-plugin-versionlock +yum versionlock llvm* +yum versionlock clang* + +# LLVM required because of issue: +# https://github.com/llvm/llvm-project/issues/61436 + +# Install BCC from source +git clone https://github.com/iovisor/bcc.git +mkdir bcc/build; cd bcc/build + +# LLVM should always link shared library +cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LLVM_SHARED=1 +make -j10 +make install + +# Install bpftool +# https://github.com/libbpf/bpftool +cd $HOME +git clone --recurse-submodules https://github.com/libbpf/bpftool.git +cd bpftool/src; make install +cd ../docs; make install + +# Install bpftrace +# https://github.com/iovisor/bpftrace/blob/master/INSTALL.md +cd $HOME +dnf install -y bcc-devel systemtap-sdt-devel binutils-devel libbpf-devel vim-common \ + libpcap-devel gtest-devel gmock-devel cereal-devel asciidoctor dwarves +git clone https://github.com/iovisor/bpftrace --recurse-submodules +cd bpftrace +mkdir build; cd build +../build-libs.sh +cmake -DCMAKE_BUILD_TYPE=Release .. +make -j8 +make install + +# Prepare eBPF lab samples +hostnamectl hostname ebpf-lab +git clone https://github.com/in0rdr/ebpf-lab.git diff --git a/samples/hello-raw-tracepoint.py b/samples/hello-raw-tracepoint.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +from bcc import BPF +import ctypes as ct + +# https://docs.kernel.org/trace/events.html +# Tracepoints can be used without creating custom kernel modules +# to register probe functions using the event tracing infrastructure. + +# eCHO Episode 74: eBPF Tail Calls +# https://www.youtube.com/watch?v=3qLXw3E0YWg + +bpf_text = r""" +#include <linux/sched.h> + +int noop() { + return 0; +} + +static int ignore_syscall() { + return 0; +} + +// eBPF tail calls allow for calling a series of functions w/o growing the stack +// BPF_PROG_ARRAY is a map to index eBPF functions +// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#10-bpf_prog_array +BPF_PROG_ARRAY(syscall, 300); + +// searchable list of syscalls +// https://filippo.io/linux-syscall-table + +// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#7-raw-tracepoints +RAW_TRACEPOINT_PROBE(sys_enter) { + // printf() to the common trace_pipe (/sys/kernel/debug/tracing/trace_pipe) + // https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#1-bpf_trace_printk + bpf_trace_printk("syscall"); + + // the syscall opcode id is the second argument + // ctx is hidden by the macro RAW_TRACEPOINT_PROBE + // https://github.com/torvalds/linux/blob/master/include/trace/events/syscalls.h + int opcode = ctx->args[1]; + switch (opcode) { + case 64: + // eBPF stack only 512 bytes, don't repeat that too often + ignore_syscall(); + break; + } + + // perform a "tail call" to another function for each specified opcode + // the mapping of tail call to opcode is done in userspace + // func invocation on struct is not proper C, will be translated by BCC + syscall.call(ctx, opcode); + + // this line will never be evaluated when the tail call succeeds + bpf_trace_printk("Another syscall: %d", opcode); + + return 0; +} +""" + +b = BPF(text=bpf_text) + +# fetch the program map +prog_array = b.get_table("syscall") + +# map the 64 syscall to the ignore function in the program map +noop_fn = b.load_func("noop", BPF.RAW_TRACEPOINT) +prog_array[ct.c_int(64)] = ct.c_int(noop_fn.fd) + +b.trace_print() diff --git a/samples/hello.py b/samples/hello.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +from bcc import BPF + +program = r""" +int hello(void *ctx) { + bpf_trace_printk("Hello eBPF!"); + return 0; +} +""" + +b = BPF(text=program) +syscall = b.get_syscall_fnname("execve") +b.attach_kprobe(event=syscall, fn_name="hello") + +b.trace_print()