Остаётся загадкой, как всякие 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 так и не цепляется
Упс, сорри, я скопировал неправильную функцию! 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:
Вы не зря упоминули bpftrace. Сам bpftrace немного про другое, а для вашего случая больше подходит XDP (это одна из разновидностей BPF программ, специально предназначенная для оптимизаций такого рода).
Для того, чтобы написать простой, но максимально быстрый UDP ping (может с включением меток времени), подойдет базовая функциональность XDP. Например, см. мой пример ICMP echo сервера тут.
Если требуется проводить более сложную обработку пакета (т.е. в userspace), то можно смотреть в сторону AF_XDP (вот пример, опять же ICMP echo, из xdp-tutorial).
Для максимальной производительности драйвер карточки должен поддерживать native XDP, но и generic XDP тоже сработает быстрее, чем любое «обычное» решение на Linux.
Во-первых, при помощи 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
:Кроме всего прочего, она выведет что-то вроде
здесь она увидела, что
retval
— это пользовательский указатель. Дальше в промежуточных кодах LLVM будет какой-тоprobe
, у меня это(в моем случае все правильно)
Упс, сорри, я скопировал неправильную функцию!
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 для вашего примера? Почему нельзя это сделать в пространстве пользователя?
В более общем виде ответ такой: помочь может тем, что мы можем легко видеть то, чего раньше не видели и крутить ручки, до которых раньше было не дотянуться. На практике, если не писать код самому, то варианты такие: bpftrace (я напишу о нем следующую статью), bcc tools (см. книжки БГ выше, одна из них описывает больше ста боевых утилит на BPF), ply (для встроенных систем и, кажется, уже особо не поддерживается). Для систем, основанных на BPF возможностей больше. Например, в cilium (CNI для K8S), кроме прочей отладочной информации, встроен фронтенд hubble, который позволяет рисовать картинки в динамике о том, как поды создают TCP соединения и смотреть на статистику. Посмотрите также на sysdig, и т.п.
A pure Go BPF library), и основная часть кода, скорее, разработана Cloudfare. Но когда они договаривались о сотрудничестве по разработке/поддержке этой библиотеки, cilium настояли, чтобы репозиторий был расположен в их github. И этот маркетинг, похоже, работает :)
Можно и древнее, но тогда нужно быть аккуратным, например, с определением мапов, чтобы они не генерировали BTF (т.е. не использовать макросы __type, etc.), как, например, это сделано в xdp-tutorial:
Для того, чтобы написать простой, но максимально быстрый UDP ping (может с включением меток времени), подойдет базовая функциональность XDP. Например, см. мой пример ICMP echo сервера тут.
Если требуется проводить более сложную обработку пакета (т.е. в userspace), то можно смотреть в сторону AF_XDP (вот пример, опять же ICMP echo, из xdp-tutorial).
Для максимальной производительности драйвер карточки должен поддерживать native XDP, но и generic XDP тоже сработает быстрее, чем любое «обычное» решение на Linux.
А вообще в C11 функция memset_s предусмотрена стандартом, но, кажется, ни один компилятор ее так и не реализовал.