Писать eBPF на чистом C с libbpf больно. Нужно разбираться в ограничениях верификатора, вручную управлять tail calls, настраивать загрузчики, собирать отдельно userspace и kernel-код. А если добавить kfunc, то ещё и писать модуль ядра с BTF-регистрацией. Для одной задачи приходится жонглировать тремя парадигмами программирования одновременно.
KernelScript — это DSL для eBPF, который позволяет описывать userspace, eBPF и kernelspace код в одном файле. Компилятор сам разберётся, что куда отправить. На выходе — готовый проект с C-кодом, Makefile и загрузчиком. Проект написан на OCaml, распространяется под Apache 2.0.
Что именно он делает
Вы пишете
.ks файл. Компилятор по атрибутам: @xdp, @tc, @kfunc, @helper; определяет, какая функция относится к какому домену, и генерирует соответствующий C-код. XDP-функции уходят в eBPF-бинарник, обычные функции становятся userspace-кодом, @kfunc превращается в модуль ядра.Вот минимальный пример XDP-программы на KernelScript:
@xdp fn packet_filter(ctx: *xdp_md) -> xdp_action {
var packet_size = ctx->data_end - ctx->data
if (packet_size > 1500) {
return XDP_DROP
}
return XDP_PASS
}Для сравнения — та же логика на чистом C заняла бы раза в три больше строк, включая boilerplate для секций, лицензии и привязки к хукам.
Что решает
Главная проблема eBPF-разработки — это количество ручной работы. KernelScript автоматизирует несколько вещей, которые обычно приходится делать руками.
Tail calls. Вместо ручной настройки program arrays и вызовов
bpf_tail_call() достаточно написать return other_xdp_func(ctx). Компилятор сам сгенерирует нужный код.Разделяемые map-ы. Объявляете map один раз, и он доступен во всех программах как обычная переменная:
pin var shared_counter : hash<u32, u32>(1024)
@xdp fn counter(ctx: *xdp_md) -> xdp_action {
shared_counter[1] = shared_counter[1] + 1
return XDP_PASS
}
Lifecycle программ. Тип программы проверяется на этапе компиляции — нельзя вызвать
attach() до load().Генерация kfunc-модулей. Достаточно пометить функцию
@kfunc, и компилятор создаст .mod.c файл с модулем ядра и регистрацией BTF-символов.Как запустить
Зависимости для Debian/Ubuntu:
sudo apt install libbpf-dev libelf-dev zlib1g-dev opam bpftool
Установка:
git clone https://github.com/multikernel/kernelscript.git
cd kernelscript
opam init
opam install . --deps-only --with-test
eval $(opam env) && dune build && dune install
Создание проекта:
kernelscript init xdp my_filter
Это сгенерирует каталог
my_filter/ с файлом my_filter.ks и README.После редактирования
.ks файла — компиляция и сборка:kernelscript compile my_filter/my_filter.ks
cd my_filter/
make
sudo ./my_filter
На выходе получится структура:
my_filter/
├── my_filter.ks # исходник KernelScript
├── my_filter.c # userspace-программа
├── my_filter.ebpf.c # eBPF C-код
├── my_filter.mod.c # модуль ядра (если есть @kfunc)
├── Makefile
└── README.md
Поддерживаемые типы программ
KernelScript поддерживает основные типы eBPF-программ:
@xdp для обработки пакетов, @tc для traffic control, @probe для трассировки функций ядра, @tracepoint для точек трассировки. Отдельно поддерживается struct_ops для TCP congestion control.Система типов включает type aliases, структуры, enum-ы, фиксированные массивы (
u8[64]) и указатели на функции. Всё это спроектировано с учётом ограничений верификатора eBPF, то есть нет сложных дженериков, которые могут сломать верификацию.Итого
KernelScript не заменяет знание eBPF. Вам всё ещё нужно понимать, что такое XDP, как работают map-ы и зачем нужен bpf_probe_read. Но он убирает рутину и позволяет сосредоточиться на логике, а не на boilerplate. Проект пока в бете, API может меняться. Но если вы работаете с eBPF и устали от связки из трёх языков и пяти make-файлов, попробовать стоит.
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека devops'a
#арсенал_инженера