Как стать автором
Обновить
51
0
Антон Протопопов @aspsk

Пользователь

Отправить сообщение

Остаётся загадкой, как всякие Cilium блокируют Egress

Во-первых, при помощи tc, а не XDP. XDP используется только на железках, на veth (и прочих виртуальных устройствах) его использовать смысла нет, так как skb все равно создается. (Кроме этого, xdpgeneric кривой, а нативный xdp на veth еще более кривой.)

Во-вторых, tc программа сажается на внешний кусок veth, находящийся в рутовом namespace, так как внутренний может быть доступен приложению внутри контейнера, которое может поменять логику BPF программы. (Это поменяется, когда мы станем использовать новый виртуальный девайс netkit, см. https://lpc.events/event/17/contributions/1581/ и сможем сажать программу прямо внутрь.)

А насчет сабжа у меня когда-то был dhcpclient на XDP (https://github.com/aspsk/xdp-dhcp), который сажался на tap и отвечал виртуалочке.

Как вы ставили bpftrace? Посмотрите на список ядерных зависимостей, попробуйте собрать bpftrace локально, повесьте багу на bpftrace, если все выглядит ok, но uptr так и не цепляется

No probes to attach обычно означает, что такой функции в бинарнике нет. Посмотреть на доступные пробы можно так: bpftrace -l 'ur:/usr/bin/bash:*'

Если программа без uptr загружается (а она должна), то посмотрите на нее при помощи -dd:


bpftrace -dd -e 'ur:/usr/bin/bash:readline { printf("%s\n", str(retval)); }'

Кроме всего прочего, она выведет что-то вроде


AST after semantic analysis
-------------------
Program
 uretprobe:/usr/bin/bash:readline
  call: printf :: type[none, ctx: 0]
   string: %s\n :: type[string[3], ctx: 0]
   call: str :: type[string[64], ctx: 0, AS(user)]
    builtin: retval :: type[unsigned int64, ctx: 0, AS(user)]

здесь она увидела, что retval — это пользовательский указатель. Дальше в промежуточных кодах LLVM будет какой-то probe, у меня это


...
%probe_read_user_str = call i64 inttoptr (i64 114 to i64 ([64 x i8]*, i32, i64)*)([64 x i8]* %str, i32 %11, i64 %retval)
...

(в моем случае все правильно)

Упс, сорри, я скопировал неправильную функцию! uptr должен конвертироваться в probe_read_user_str, конечно, а kptr — в probe_read_kernel_str. (Раньше была только probe_read_str, которая угадывала принадлежность указателя.)


Загружается ли у вас программа bpftrace -e 'ur:/usr/bin/bash:readline { printf("%s\n", str(retval)); }'?

Статью я писал с такой же версией bpftrace. Какое у вас ядро? str(uptr(...)) конвертируется в вызов функции-помощника bpf_probe_read_kernel_str, так что если ядро ее не поддерживает, то bpftrace наверняка ступит.


Проверить, поддерживается ли эта функция, можно, запустив sudo bpftrace --info (см. в секуии Kernel Helpers). Если ваше ядро 5.5 или больше (или даже 5.4), то она должна поддерживаться. Если она поддерживается, то смело вешайте багу на bpftrace.

Нет, в таком общем виде нельзя.


Для ограниченного набора функций (помеченных в исходном коде ядра ALLOW_ERROR_INJECTION) можно делать error injection. А именно, если ваше ядро собрано с CONFIG_BPF_KPROBE_OVERRIDE, то при помощи хелпера bpf_override_return, доступного для программ типа BPF_PROG_TYPE_KPROBE, вы можете патчить возвращаемые значения функций. Но это не позволяет переписать функцию — она даже не будет запущена.


Для переписывания функциональности ядра есть тип программ BPF_PROG_TYPE_STRUCT_OPS, который позволяет на лету переписывать куски ядра, но тоже не в таком общем виде. См. https://lwn.net/Articles/811631/


А какой use case для вашего примера? Почему нельзя это сделать в пространстве пользователя?

Спасибо! Я пытаюсь писать понятно, но чувствую, что можно сильно лучше. Может придется делать второе издание через несколько лет :)
Было-б неплохо описать чем может помочь и как применять eBPF в админской практике


В более общем виде ответ такой: помочь может тем, что мы можем легко видеть то, чего раньше не видели и крутить ручки, до которых раньше было не дотянуться. На практике, если не писать код самому, то варианты такие: bpftrace (я напишу о нем следующую статью), bcc tools (см. книжки БГ выше, одна из них описывает больше ста боевых утилит на BPF), ply (для встроенных систем и, кажется, уже особо не поддерживается). Для систем, основанных на BPF возможностей больше. Например, в cilium (CNI для K8S), кроме прочей отладочной информации, встроен фронтенд hubble, который позволяет рисовать картинки в динамике о том, как поды создают TCP соединения и смотреть на статистику. Посмотрите также на sysdig, и т.п.
Версия от Cilium имеет те же особенности, что версия от Dropbox. Но она стоит упоминания хотя бы потому, что делается ребятами из проекта Cilium, а значит, обречена на успех.
libbpf-go — это совместная работа Cloudfare и cilium (вот как ее разработка начиналась год назад
A pure Go BPF library), и основная часть кода, скорее, разработана Cloudfare. Но когда они договаривались о сотрудничестве по разработке/поддержке этой библиотеки, cilium настояли, чтобы репозиторий был расположен в их github. И этот маркетинг, похоже, работает :)
Да, он только на днях и появился. Этот сайт поддерживается компанией Isovalent, в основе бизнеса которой лежит Kubernetes CNI cilium, основанный на BPF. У них есть еще хороший slack канал #ebpf, в котором можно задавать вопросы про BPF. Товарищи опытные — Daniel Borkman (один из создателей BPF), два кофаундера в сетевом сообществе Linux с начала двухтысячных, и плюс еще довольно много очень хороших инженеров.
Да, в нормальном случае последний clang не нужен (но для того, чтобы нормально работало BPF CO-RE точно нужен clang 10, лучше — 11).

Можно и древнее, но тогда нужно быть аккуратным, например, с определением мапов, чтобы они не генерировали BTF (т.е. не использовать макросы __type, etc.), как, например, это сделано в xdp-tutorial:
struct bpf_map_def SEC("maps") xdp_stats_map = {
    .type        = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size    = sizeof(__u32),
    .value_size  = sizeof(struct datarec),
    .max_entries = XDP_ACTION_MAX,
};
Вы не зря упоминули bpftrace. Сам bpftrace немного про другое, а для вашего случая больше подходит XDP (это одна из разновидностей BPF программ, специально предназначенная для оптимизаций такого рода).

Для того, чтобы написать простой, но максимально быстрый UDP ping (может с включением меток времени), подойдет базовая функциональность XDP. Например, см. мой пример ICMP echo сервера тут.

Если требуется проводить более сложную обработку пакета (т.е. в userspace), то можно смотреть в сторону AF_XDP (вот пример, опять же ICMP echo, из xdp-tutorial).

Для максимальной производительности драйвер карточки должен поддерживать native XDP, но и generic XDP тоже сработает быстрее, чем любое «обычное» решение на Linux.
Можно сделать чуть эффективнее:

void *(*volatile memset_s)(void *, int, size_t) = memset;

А вообще в C11 функция memset_s предусмотрена стандартом, но, кажется, ни один компилятор ее так и не реализовал.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность