Blog
Thoughts on engineering, design, and building great products.
LSM BPF: Cưỡng Chế An Ninh Ngay Trong Nhân
Tới giờ eBPF của ta chỉ quan sát. LSM BPF thì cưỡng chế: gắn vào các điểm kiểm soát an ninh của nhân (Linux Security Modules) mà SELinux, AppArmor dùng, và một chương trình trả về 0 cho qua hay -EPERM để chặn. Bài này viết một chương trình LSM chặn mở file, và gặp một bài học thật: lần đầu nó nạp và gắn được nhưng không chặn gì — vì bpf chưa nằm trong danh sách LSM hoạt động. Bật bpf bằng tham số boot rồi reboot, cùng chương trình đó chặn thật — cat và python đều nhận Operation not permitted.
Verifier: Vì Sao eBPF Không Sập Nhân
Bài 1 nói thiết kế máy ảo eBPF cho phép verifier chứng minh an toàn. Bài này thấy nó làm thật: ta biên dịch một chương trình XDP đọc byte đầu của gói tin mà quên kiểm giới hạn, nạp vào nhân — verifier từ chối với log chỉ đích danh thanh ghi và lý do. Thêm đúng một câu kiểm data_end, verifier cho qua. Verifier là bộ chứng minh an toàn chạy lúc nạp, theo dõi trạng thái từng thanh ghi trên mọi nhánh — thứ khiến eBPF nạp được mã lạ vào nhân mà không như kernel module.
eBPF Từ Số Không: Chạy Chương Trình Trong Nhân Linux
Ngay lúc này, trên một worker của cụm Kubernetes ta dựng ở series trước, có 140 chương trình eBPF đang chạy bên trong nhân Linux — định tuyến từng gói tin, kiểm soát quyền truy cập thiết bị, gom metric. eBPF cho phép nạp mã vào nhân và chạy an toàn tại các điểm móc, không sửa mã nguồn nhân, không nạp module. Bài mở đầu series giải thích eBPF là gì, vì sao nó đổi cách mở rộng nhân, và một chương trình đi từ code tới mã máy chạy trong nhân ra sao.
BTF và CO-RE: Một Lần Biên Dịch, Chạy Mọi Kernel
Cấu trúc dữ liệu bên trong nhân như task_struct có layout khác nhau giữa các phiên bản kernel — field nằm ở offset nào tùy bản. Vậy làm sao một chương trình eBPF biên dịch sẵn đọc đúng field trên mọi kernel? Câu trả lời là BTF và CO-RE. Bài cuối Part I sinh vmlinux.h từ BTF của nhân, viết một chương trình đọc ppid bằng cách lần qua task->real_parent->tgid, biên dịch một lần và chạy — libbpf tự tìm offset đúng theo BTF của kernel đang chạy. Đây cũng là nền cho Part III tự viết công cụ thật.
Program Type và Hook: Gắn Vào Đâu, Thấy Được Gì
Một chương trình eBPF không chạy lơ lửng — nó gắn vào một hook trong nhân, và loại hook đó quyết định ba thứ: chương trình chạy lúc nào, nhận context gì, và được gọi helper nào. Bài này liệt kê các program type nhân hỗ trợ, rồi gắn một tracepoint vào syscall openat để thấy nó chạy thật trên mỗi lần mở file — tương phản với XDP nhận gói tin ở bài trước, để thấy vì sao cùng là eBPF mà mỗi loại thấy một thế giới khác nhau.
Maps: Bộ Nhớ và Cầu Nối Với Userspace
Một chương trình eBPF chạy theo từng sự kiện rồi tắt, không giữ biến giữa các lần. Maps là cách nó nhớ trạng thái và nói chuyện với userspace. Bài này viết một chương trình đếm mỗi lần có tiến trình exec vào một map, nạp vào nhân, chạy vài lệnh, rồi đọc map từ userspace bằng bpftool — thấy con số tăng thật. Kèm soi một map thật của Cilium đang giữ metric per-CPU, và phân biệt array thường với per-CPU.
Máy Ảo eBPF: Thanh Ghi, Tập Lệnh và Bytecode
Bài trước thấy một chương trình eBPF có 'xlated 512B' (bytecode đã verify) và 'jited 333B' (mã máy). Bài này đi vào trong cái bytecode đó: eBPF là một máy ảo kiểu RISC với 11 thanh ghi 64-bit, một tập lệnh nhỏ, được thiết kế để vừa dịch nhanh sang mã máy gốc vừa kiểm chứng được an toàn. Ta đọc thẳng bytecode của một chương trình Cilium đang chạy, soi từng lệnh ánh xạ ra thanh ghi và lớp lệnh nào, rồi xem vì sao đúng thiết kế máy ảo này mới cho phép verifier chứng minh an toàn.