From cc5f720406aa939a8da0ef02bbaa6f1cb0d090fd Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Sat, 13 Sep 2025 19:58:01 +0530 Subject: [PATCH] Support simple XDP --- demo/pybpf0.py | 23 ++++++++++++++++ demo/pybpf1.py | 41 ++++++++++++++++++++++++++++ demo/{pybpf.py => pybpf2.py} | 6 ++--- demo/pybpf3.py | 52 ++++++++++++++++++++++++++++++++++++ examples/c-form/Makefile | 2 +- examples/c-form/ex2.bpf.c | 16 +++-------- examples/check.sh | 12 +++++++++ pythonbpf/functions_pass.py | 11 +++++++- pythonbpf/helpers.py | 3 +++ 9 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 demo/pybpf0.py create mode 100644 demo/pybpf1.py rename demo/{pybpf.py => pybpf2.py} (91%) create mode 100644 demo/pybpf3.py diff --git a/demo/pybpf0.py b/demo/pybpf0.py new file mode 100644 index 0000000..b292ced --- /dev/null +++ b/demo/pybpf0.py @@ -0,0 +1,23 @@ +from pythonbpf import bpf, section, bpfglobal, compile + +from ctypes import c_void_p, c_int64 + +# Instructions to how to run this program +# 1. Install PythonBPF: pip install pythonbpf +# 2. Run the program: python demo/pybpf0.py +# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf0.o +# 4. Start up any program and watch the output + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + return c_int64(0) + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + +compile() diff --git a/demo/pybpf1.py b/demo/pybpf1.py new file mode 100644 index 0000000..435fc10 --- /dev/null +++ b/demo/pybpf1.py @@ -0,0 +1,41 @@ +from pythonbpf import bpf, map, section, bpfglobal, compile +from pythonbpf.helpers import XDP_PASS +from pythonbpf.maps import HashMap + +from ctypes import c_void_p, c_int64 + +# Instructions to how to run this program +# 1. Install PythonBPF: pip install pythonbpf +# 2. Run the program: python demo/pybpf1.py +# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf1.o +# 4. Attach object file to any network device with something like ./check.sh xdp ../demo/pybpf1.o tailscale0 +# 5. send traffic through the device and observe effects + +@bpf +@map +def count() -> HashMap: + return HashMap(key_type=c_int64, value_type=c_int64, max_entries=1) + + +@bpf +@section("xdp") +def hello_world(ctx: c_void_p) -> c_int64: + key = 0 + one = 1 + prev = count().lookup(key) + if prev: + prevval = prev + 1 + print(f"count: {prevval}") + count().update(key, prevval) + return XDP_PASS + else: + count().update(key, one) + + return XDP_PASS + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + +compile() diff --git a/demo/pybpf.py b/demo/pybpf2.py similarity index 91% rename from demo/pybpf.py rename to demo/pybpf2.py index 329c3fa..51b91e6 100644 --- a/demo/pybpf.py +++ b/demo/pybpf2.py @@ -1,13 +1,13 @@ from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helpers import ktime, deref +from pythonbpf.helpers import ktime from pythonbpf.maps import HashMap from ctypes import c_void_p, c_int64, c_uint64 # Instructions to how to run this program # 1. Install PythonBPF: pip install pythonbpf -# 2. Run the program: python demo/pybpf.py -# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf.o +# 2. Run the program: python demo/pybpf2.py +# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf2.o # 4. Start a Python repl and `import os` and then keep entering `os.sync()` to see reponses. @bpf diff --git a/demo/pybpf3.py b/demo/pybpf3.py new file mode 100644 index 0000000..427df54 --- /dev/null +++ b/demo/pybpf3.py @@ -0,0 +1,52 @@ +from pythonbpf import bpf, map, section, bpfglobal, compile +from pythonbpf.helpers import ktime +from pythonbpf.maps import HashMap + +from ctypes import c_void_p, c_int64, c_uint64 + +# Instructions to how to run this program +# 1. Install PythonBPF: pip install pythonbpf +# 2. Run the program: python demo/pybpf3.py +# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf3.o +# 4. Start up any program and watch the output + +@bpf +@map +def last() -> HashMap: + return HashMap(key_type=c_uint64, value_type=c_uint64, max_entries=3) + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def do_trace(ctx: c_void_p) -> c_int64: + key = 0 + tsp = last().lookup(key) + if tsp: + kt = ktime() + delta = (kt - tsp) + if delta < 1000000000: + time_ms = (delta // 1000000) + print(f"Execve syscall entered within last second, last {time_ms} ms ago") + last().delete(key) + else: + kt = ktime() + last().update(key, kt) + return c_int64(0) + +@bpf +@section("tracepoint/syscalls/sys_exit_execve") +def do_exit(ctx: c_void_p) -> c_int64: + va = 8 + nm = 5 ^ va + al = 6 & 3 + ru = (nm + al) + print(f"this is a variable {ru}") + return c_int64(0) + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/examples/c-form/Makefile b/examples/c-form/Makefile index 64ff900..6be0827 100644 --- a/examples/c-form/Makefile +++ b/examples/c-form/Makefile @@ -1,5 +1,5 @@ BPF_CLANG := clang -CFLAGS := -O0 -emit-llvm -target bpf -c +CFLAGS := -O2 -emit-llvm -target bpf -c SRC := $(wildcard *.bpf.c) LL := $(SRC:.bpf.c=.bpf.ll) diff --git a/examples/c-form/ex2.bpf.c b/examples/c-form/ex2.bpf.c index 90527b8..89a4428 100644 --- a/examples/c-form/ex2.bpf.c +++ b/examples/c-form/ex2.bpf.c @@ -3,20 +3,10 @@ #define u64 unsigned long long #define u32 unsigned int -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 1); - __type(key, u32); - __type(value, u64); -} last SEC(".maps"); - -SEC("tracepoint/syscalls/sys_enter_execve") -int hello(struct pt_regs *ctx) { +SEC("xdp") +int hello(struct xdp_md *ctx) { bpf_printk("Hello, World!\n"); - u64 b; - u64 a = 3 * b; - bpf_printk("%d", a); - return 0; + return XDP_PASS; } char LICENSE[] SEC("license") = "GPL"; diff --git a/examples/check.sh b/examples/check.sh index a7abb16..6b05af8 100755 --- a/examples/check.sh +++ b/examples/check.sh @@ -22,11 +22,23 @@ case "$1" in sudo rm -f "$PIN_PATH" echo "[+] Stopped" ;; + xdp) + echo "[*] Loading and running $FILE" + sudo bpftool net detach xdp dev $3 + sudo bpftool prog load "$FILE" "$PIN_PATH" type xdp + sudo bpftool net attach xdp pinned "$PIN_PATH" dev $3 + echo "[+] Program loaded. Press Ctrl+C to stop" + sudo cat /sys/kernel/debug/tracing/trace_pipe + sudo bpftool net detach xdp dev $3 + sudo rm -rf "$PIN_PATH" + echo "[+] Stopped" + ;; *) echo "Usage: $0 " echo "Examples:" echo " $0 check program.bpf.o" echo " $0 run program.bpf.o" + echo " $0 xdp program.bpf.o wlp6s0" echo " $0 stop" exit 1 ;; diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions_pass.py index 7b7c05e..1c651ed 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions_pass.py @@ -242,8 +242,17 @@ def process_stmt(func, module, builder, stmt, local_sym_tab, map_sym_tab, did_re builder.ret(ir.Constant( ret_type, stmt.value.args[0].value)) did_return = True + elif isinstance(stmt.value, ast.Name): + if stmt.value.id == "XDP_PASS": + builder.ret(ir.Constant(ret_type, 2)) + did_return = True + elif stmt.value.id == "XDP_DROP": + builder.ret(ir.Constant(ret_type, 1)) + did_return = True + else: + raise ValueError("Failed to evaluate return expression") else: - print("Unsupported return value") + raise ValueError("Unsupported return value") return did_return diff --git a/pythonbpf/helpers.py b/pythonbpf/helpers.py index aa87f4e..ae24d9f 100644 --- a/pythonbpf/helpers.py +++ b/pythonbpf/helpers.py @@ -7,3 +7,6 @@ def deref(ptr): "dereference a pointer" result = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_void_p)).contents.value return result if result is not None else 0 + +XDP_DROP = ctypes.c_int64(1) +XDP_PASS = ctypes.c_int64(2)