.\" -*- coding: UTF-8 -*- .\" Copyright (C) 2015 Alexei Starovoitov .\" and Copyright (C) 2015 Michael Kerrisk .\" .\" SPDX-License-Identifier: Linux-man-pages-copyleft .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH bpf 2 "28 июля 2023 г." "Linux man\-pages 6.05.01" .SH ИМЯ bpf \- выполняет команду с расширенной картой BPF или программу .SH СИНТАКСИС .nf \fB#include \fP .PP \fBint bpf(int \fP\fIcmd\fP\fB, union bpf_attr *\fP\fIattr\fP\fB, unsigned int \fP\fIsize\fP\fB);\fP .fi .SH ОПИСАНИЕ Системный вызов \fBbpf\fP() выполняет набор операций, связанных с расширенными пакетными фильтрами Беркли (Berkeley Packet Filters). Расширенные BPF (или eBPF) подобны первоначальным («классическим») BPF (cBPF), которые используются для фильтрации сетевых пакетов. Перед загрузкой программы cBPF и eBPF анализируются ядром на предмет их безвредности для работающей системы. .PP .\" See 'enum bpf_func_id' in include/uapi/linux/bpf.h .\" Набор eBPF расширяет cBPF в разных направлениях, включая способность вызова фиксированного набора вспомогательных функций ядра (через расширенный код операции \fBBPF_CALL\fP, предоставляемый eBPF) и доступа к общим структурам данных, таким как карты eBPF. .SS "Структура/архитектура расширенных BPF" Карты eBPF — это обобщённая структура данных, которая позволяет хранить данных различных типов. Типы данных, в общем случае, считаются двоичными объектами (binary blobs), поэтому пользователь просто указывает размер ключа и размер значения при создании карты. Другими словами, ключ/значение задаваемой карты могут иметь произвольную структуру. .PP Пользовательский процесс может создать несколько карт (с парами ключ/значение нераспознаваемых байт данных (opaque bytes of data)) и работать с ними через файловые дескрипторы. Несколько программ eBPF могут получать доступ к одним и тем же картам параллельно. Решение, что хранить в картах, полностью отдано пользовательскому процессу и программе eBPF. .PP .\" Defined by the kernel constant MAX_TAIL_CALL_CNT in include/linux/bpf.h Существует специальный карточный тип, называемый программным массивом (program array). В данном типе карты хранятся файловые дескрипторы, указывающие на другие программы eBPF. Когда выполняется поиск в карте программный поток в этом месте перенаправляется в начало другой программы eBPF и не возвращается в вызывающую программу. Уровень вложенности ограничен 32, поэтому бесконечные циклы невозможны. Во время выполнения программные файловые дескрипторы, хранящиеся в карте, не могут быть изменены, поэтому функциональность программы можно изменить только на основе специальных требований. Все программы, на которые есть ссылки из карты программного массива, должны заранее загружаться в ядро с помощью \fBbpf\fP(). Если поиск по карте завершился с ошибкой, то текущая программа продолжает выполняться. Подробней смотрите далее в описании \fBBPF_MAP_TYPE_PROG_ARRAY\fP. .PP Обычно, программы eBPF загружаются пользовательским процессом и выгружаются при его завершении. В некоторых случаях, например, \fBtc\-bpf\fP(8), программа продолжает работать внутри ядра даже после того, как процесс загрузивший программу, закончил работать. В этом случае ссылку на программу eBPF после того, как файловый дескриптор был закрыт программой из пользовательского пространства, содержит подсистема tc. То есть, будет ли специальная программа продолжать работать внутри ядра, зависит от того, будет ли она присоединена к указанной подсистеме ядра после загрузки через \fBbpf\fP(). .PP Программа eBPF представляет собой набор инструкций, безопасно выполняющаяся от начала и до конца. Ядерный механизм проверки статически определяет, что программа eBPF завершится и её безопасно запускать. Во время проверки ядро увеличивает счётчик ссылок для каждой карты, которая используется программой eBPF, поэтому присоединённые карты невозможно удалить пока не будет выгружена программа. .PP Программы eBPF могут быть присоединены к различным событиям. Эти события могут возникать при поступлении сетевых пакетов, это могут быть события трассировки, события распределения по сетевым очередям (для программ eBPF, присоединённых к классификатору \fBtc\fP(8)) и другие типы событий, которые могут быть добавлены в будущем. Новое событие активирует выполнение программы eBPF, которое может сохранить информацию о событии в картах eBPF. Кроме сохранения данных, программы eBPF могут вызывать фиксированный набор вспомогательных функций ядра. .PP Программа eBPF может быть присоединена к нескольким событиям, а различные программы eBPF могут иметь доступ к одной карте: .PP .in +4n .EX tracing tracing tracing packet packet packet event A event B event C on eth0 on eth1 on eth2 | | | | | \[ha] | | | | v | \-\-> tracing <\-\- tracing socket tc ingress tc egress prog_1 prog_2 prog_3 classifier action | | | | prog_4 prog_5 |\-\-\- \-\-\-\-\-| |\-\-\-\-\-\-| map_3 | | map_1 map_2 \-\-| map_4 |\-\- .EE .in .\" .SS Аргументы The operation to be performed by the \fBbpf\fP() system call is determined by the \fIcmd\fP argument. Each operation takes an accompanying argument, provided via \fIattr\fP, which is a pointer to a union of type \fIbpf_attr\fP (see below). The unused fields and padding must be zeroed out before the call. The \fIsize\fP argument is the size of the union pointed to by \fIattr\fP. .PP Значением \fIcmd\fP может быть одно из: .TP \fBBPF_MAP_CREATE\fP Создать карту и вернуть файловый дескриптор, который указывает на эту карту. Для нового файлового дескриптора флаг close\-on\-exec (смотрите \fBfcntl\fP(2)) устанавливается автоматически. .TP \fBBPF_MAP_LOOKUP_ELEM\fP Найти элемент по ключу в указанной карте и вернуть его значение. .TP \fBBPF_MAP_UPDATE_ELEM\fP Создать или обновить элемент (пару ключ/значение) в указанной карте. .TP \fBBPF_MAP_DELETE_ELEM\fP Найти и удалить элемент по ключу в указанной карте. .TP \fBBPF_MAP_GET_NEXT_KEY\fP Найти элемент по ключу в указанной карте и вернуть ключ следующего элемента. .TP \fBBPF_PROG_LOAD\fP Проверить и загрузить программу eBPF; возвращается новый файловый дескриптор, связанный с программой. Для нового файлового дескриптора флаг close\-on\-exec (смотрите \fBfcntl\fP(2)) устанавливается автоматически. .IP Объединение \fIbpf_attr\fP состоит из различных анонимных структур, которые используются в различных командах \fBbpf\fP(): .PP .in +4n .EX .\" commit 2541517c32be2531e0da59dfd7efc1ce844644f5 union bpf_attr { struct { /* Used by BPF_MAP_CREATE */ __u32 map_type; __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* maximum number of entries in a map */ }; \& struct { /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY commands */ __u32 map_fd; __aligned_u64 key; union { __aligned_u64 value; __aligned_u64 next_key; }; __u64 flags; }; \& struct { /* Used by BPF_PROG_LOAD */ __u32 prog_type; __u32 insn_cnt; __aligned_u64 insns; /* \[aq]const struct bpf_insn *\[aq] */ __aligned_u64 license; /* \[aq]const char *\[aq] */ __u32 log_level; /* verbosity level of verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied \[aq]char *\[aq] buffer */ __u32 kern_version; /* checked when prog_type=kprobe (since Linux 4.1) */ }; } __attribute__((aligned(8))); .EE .in .\" .SS "Карты eBPF" Карты представляют собой обобщённую структуру данных, которая позволяет хранить данных различных типов. Карты позволяют использовать данные нескольким ядерным программам eBPF одновременно, а также ядру и приложениям пользовательского пространства. .PP Каждый тип карты имеет следующие атрибуты: .IP \[bu] 3 тип .IP \[bu] максимальное количество элементов .IP \[bu] размер ключа в байтах .IP \[bu] размер значения в байтах .PP Следующие обёрточные функции показывают как для доступа к картам можно использовать различные команды \fBbpf\fP(). Для указания вызываемой операции служит параметр \fIcmd\fP. .TP \fBBPF_MAP_CREATE\fP Команда \fBBPF_MAP_CREATE\fP создаёт новую карту, возвращая новый файловый дескриптор, который указывает на карту. .IP .in +4n .EX int bpf_create_map(enum bpf_map_type map_type, unsigned int key_size, unsigned int value_size, unsigned int max_entries) { union bpf_attr attr = { .map_type = map_type, .key_size = key_size, .value_size = value_size, .max_entries = max_entries }; \& return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } .EE .in .IP Новая карта имеет тип, указанный в \fImap_type\fP, и атрибуты, в соответствии с \fIkey_size\fP, \fIvalue_size\fP и \fImax_entries\fP. При успешном выполнении этой операции возвращается файловый дескриптор. При ошибке возвращается \-1 и в \fIerrno\fP записывается \fBEINVAL\fP, \fBEPERM\fP или \fBENOMEM\fP. .IP Атрибуты \fIkey_size\fP и \fIvalue_size\fP будут использоваться механизмом проверки при загрузке программы для проверки того, что программа вызывает вспомогательные функции \fBbpf_map_*_elem\fP() с корректно инициализированным \fIkey\fP и что программа не обращается к элементу карты \fIvalue\fP за пределами, задаваемыми \fIvalue_size\fP. Например, когда карта создана с \fIkey_size\fP равным 8 и программа eBPF вызывает .IP .in +4n .EX bpf_map_lookup_elem(map_fd, fp \- 4) .EE .in .IP то программа не будет загружена, так как от вспомогательной ядерной функции .IP .in +4n .EX bpf_map_lookup_elem(map_fd, void *key) .EE .in .IP ожидается чтение 8 байт из места, указанного \fIkey\fP, но начальный адрес \fIfp\ \-\ 4\fP (где \fIfp\fP — вершина стека) выходит за границы стека. .IP Аналогично, когда карта создаётся с \fIvalue_size\fP равным 1 и программа eBPF содержит .IP .in +4n .EX value = bpf_map_lookup_elem(...); *(u32 *) value = 1; .EE .in .IP то программа не будет загружена, так как она обращается к указателю \fIvalue\fP вне ограничения \fIvalue_size\fP, равного 1 байту. .IP В настоящее время поддерживаются следующие значения \fImap_type\fP: .IP .in +4n .EX enum bpf_map_type { BPF_MAP_TYPE_UNSPEC, /* Reserve 0 as invalid map type */ BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_STACK, /* See /usr/include/linux/bpf.h for the full list. */ }; .EE .in .IP .\" FIXME We need an explanation of why one might choose each of .\" these map implementations Для \fImap_type\fP выбирается одна из доступных реализаций карт в ядре. Для всех типов карт программы eBPF получают доступ через одни и те же вспомогательные функции \fBbpf_map_lookup_elem\fP() и \fBbpf_map_update_elem\fP(). Подробности о различных типах карт приведены далее. .TP \fBBPF_MAP_LOOKUP_ELEM\fP Команда \fBBPF_MAP_LOOKUP_ELEM\fP ищет элемент с заданным \fIkey\fP в карте, на которую ссылается файловый дескриптор \fIfd\fP. .IP .in +4n .EX int bpf_lookup_elem(int fd, const void *key, void *value) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .value = ptr_to_u64(value), }; \& return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } .EE .in .IP Если элемент найден, то возвращается ноль и значение элемента сохраняется в \fIvalue\fP, которое должно указывать на буфер размером \fIvalue_size\fP байт. .IP Если элемент не найден, то возвращается \-1, а в \fIerrno\fP записывается \fBENOENT\fP. .TP \fBBPF_MAP_UPDATE_ELEM\fP Команда \fBBPF_MAP_UPDATE_ELEM\fP создаёт или обновляет элемент с заданными \fIkey/value\fP в карте, на которую ссылается файловый дескриптор \fIfd\fP. .IP .in +4n .EX int bpf_update_elem(int fd, const void *key, const void *value, uint64_t flags) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .value = ptr_to_u64(value), .flags = flags, }; \& return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } .EE .in .IP В аргументе \fIflags\fP должно быть указано одно из: .RS .TP \fBBPF_ANY\fP Создать новый элемент или обновить существующий. .TP \fBBPF_NOEXIST\fP Создать новый элемент, только если он не существует. .TP \fBBPF_EXIST\fP Обновить существующий элемент. .RE .IP При успешном выполнении операции возвращается ноль. При ошибке возвращается \-1, а в \fIerrno\fP записывается \fBEINVAL\fP, \fBEPERM\fP, \fBENOMEM\fP или \fBE2BIG\fP. Значение \fBE2BIG\fP показывает, что количество элементов в карте достигло ограничения \fImax_entries\fP, заданного при создании карты. Значение \fBEEXIST\fP устанавливается, если в \fIflags\fP указано \fBBPF_NOEXIST\fP и элемент с \fIkey\fP уже существует в карте. Значение \fBENOENT\fP устанавливается, если в \fIflags\fP указано \fBBPF_EXIST\fP и элемент с \fIkey\fP не существует в карте. .TP \fBBPF_MAP_DELETE_ELEM\fP Команда \fBBPF_MAP_DELETE_ELEM\fP удаляет элемент с ключом \fIkey\fP из карты, на которую ссылается файловый дескриптор \fIfd\fP. .IP .in +4n .EX int bpf_delete_elem(int fd, const void *key) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), }; \& return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); } .EE .in .IP При успешном выполнении возвращается ноль. Если элемент не найден, то возвращается \-1, а \fIerrno\fP присваивается значение \fBENOENT\fP. .TP \fBBPF_MAP_GET_NEXT_KEY\fP Команда \fBBPF_MAP_GET_NEXT_KEY\fP ищет элемент по ключу \fIkey\fP в карте, на которую указывает файловый дескриптор \fIfd\fP, и присваивает указателю \fInext_key\fP ключ следующего элемента. .IP .in +4n .EX int bpf_get_next_key(int fd, const void *key, void *next_key) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .next_key = ptr_to_u64(next_key), }; \& return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); } .EE .in .IP Если \fIkey\fP найден, то операция возвращает ноль и устанавливает указатель \fInext_key\fP равным ключу следующего элемента. Если \fIkey\fP не найден, то операция возвращает ноль и устанавливает указатель \fInext_key\fP равным ключу первого элемента. Если \fIkey\fP — последний элемент, то возвращается \-1 и \fIerrno\fP присваивается значение \fBENOENT\fP. Другие возможные значения \fIerrno\fP: \fBENOMEM\fP, \fBEFAULT\fP, \fBEPERM\fP и \fBEINVAL\fP. Данный метод можно использовать для обхода всех элементов в карте. .TP \fBclose(map_fd)\fP .\" Данный вызов удаляет карту, на которую ссылается файловый дескриптор \fImap_fd\fP. Когда программа пользовательского пространства, создавшая карту, завершает работу, все карты удаляются автоматически (но смотрите ЗАМЕЧАНИЯ). .SS "Типы карт eBPF" Поддерживаются следующие типы карт: .TP \fBBPF_MAP_TYPE_HASH\fP .\" commit 0f8e4bd8a1fc8c4185f1630061d0a1f2d197a475 Карты в виде хэш\-таблицы имеют следующие характеристики: .RS .IP \[bu] 3 Карты создаются и уничтожаются программами пользовательского пространства. Из пользовательских и eBPF программ можно выполнять операции поиска, обновления и удаления. .IP \[bu] За выделение/освобождения места для пар ключ/значение отвечает ядро. .IP \[bu] Вспомогательная функция \fBmap_update_elem\fP() завершится с ошибкой при вставке нового элемента при достижении ограничения \fImax_entries\fP (то есть, программы eBPF не смогут занять всю память). .IP \[bu] Функция \fBmap_update_elem\fP() выполняет атомарную замену существующего элемента. .RE .IP Карты в виде хэш\-таблицы оптимизированы под скоростной поиск. .TP \fBBPF_MAP_TYPE_ARRAY\fP .\" commit 28fbcfa08d8ed7c5a50d41a0433aad222835e8e3 Карты в виде массива имеют следующие характеристики: .RS .IP \[bu] 3 Оптимизированы под самый быстрый поиск. В будущем механизм проверки/компилятор JIT смогут распознавать операции lookup(), которые выдают ключ\-константу и оптимизировать его в указатель\-константу. Также возможно оптимизировать ключ не константу в явный указатель арифметически, так как указатели и \fIvalue_size\fP являются константами на всём протяжении жизни программы eBPF. Другими словами, \fBarray_map_lookup_elem\fP() может быть «встроена» механизмом проверки/компилятором JIT, одновременно сохраняя доступ к этой карте из пространства пользователя. .IP \[bu] Место под все элементы массива выделяется заранее и заполняется нулями при инициализации. .IP \[bu] Ключом является индекс массива, и он всегда занимает четыре байта. .IP \[bu] Функция \fBmap_delete_elem\fP() завершается с ошибкой \fBEINVAL\fP, так как элементы нельзя удалять. .IP \[bu] Функция \fBmap_update_elem\fP() заменяет элементы \fBне атомарным\fP образом; для атомарных обновлений нужно использовать карту в виде хэш\-таблицы. Однако есть особый случай при работе и с массивами: можно использовать встроенную атомарную функцию \fB__sync_fetch_and_add()\fP для работы с 32 и 64\-битными атомарными указателями. Например, это можно использовать, если само значение целиком представляет собой одиночный счётчик, или если есть структура с несколькими счётчиками, то функцию можно применять для отдельных счётчиков. Это довольно часто полезно для агрегирования событий и учёта. .RE .IP Возможные варианты использования карт в виде массивов: .RS .IP \[bu] 3 «Глобальные» переменные eBPF: массив из одного элемента 1, чей ключ (индекс) равен 0, а значение — набор «глобальных» переменных, в которых программы eBPF могут хранить состояние между событиями. .IP \[bu] Агрегация событий трассировки в постоянный набор блоков (buckets). .IP \[bu] Учёт сетевых событий, например, количество и размер пакетов. .RE .TP \fBBPF_MAP_TYPE_PROG_ARRAY\fP (начиная с Linux 4.2) Карта в виде программного массива — специальный вариант карты в виде массива, в которой значения содержат только файловые дескрипторы, указывающие на другие программы eBPF. То есть \fIkey_size\fP и \fIvalue_size\fP должны занимать по четыре байта. Данная карта используется вместе с функцией \fBbpf_tail_call\fP(). .IP Это означает, что программу eBPF с картой в виде программного массива можно присоединить из ядра с помощью вызова .IP .in +4n .EX void bpf_tail_call(void *context, void *prog_map, unsigned int index); .EE .in .IP и, таким образом, заменить свой программный поток потоком из элемента массива заданной программы, если он есть. Это можно рассматривать как подобие таблицы переходов к другой программе eBPF. Вызываемая программа использует тот же стек. При выполнении перехода в новую программу возврат к старой более невозможен. .IP .\" MAX_TAIL_CALL_CNT Если программа eBPF с указанным индексом не найдена в программном массиве (так как элемент карты не содержит корректного программного файлового дескриптора, поиск индекса/ключа вне границ диапазона или превышено ограничение на количество вложенных вызовов (32)), то продолжается выполнение текущей программы eBPF. Это можно использовать для обработки значений по умолчанию. .IP .\" Например, карта в виде программного массива полезна, если при трассировке или работе с сетью нужно передать обработку отдельных системных вызовов или протоколов их собственным подпрограммам и использовать их идентификаторы как отдельные индексы карт. Такой подход позволяет достичь преимуществ в производительности, а также преодолеть возможное ограничение на количество инструкций одной программы eBPF. В динамичных окружениях пользовательский демон может атомарно заменить отдельные подпрограммы во время выполнения на новые версии, чтобы полностью изменить работу программы, например, при глобальном изменении политик. .SS "Программы eBPF" Команда \fBBPF_PROG_LOAD\fP используется для загрузки программы eBPF в ядро. Возвращаемым значением является новый файловый дескриптор, связанный с этой программой eBPF. .PP .in +4n .EX char bpf_log_buf[LOG_BUF_SIZE]; \& int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, int insn_cnt, const char *license) { union bpf_attr attr = { .prog_type = type, .insns = ptr_to_u64(insns), .insn_cnt = insn_cnt, .license = ptr_to_u64(license), .log_buf = ptr_to_u64(bpf_log_buf), .log_size = LOG_BUF_SIZE, .log_level = 1, }; \& return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } .EE .in .PP Значением \fIprog_type\fP может быть один из типов программ: .IP .in +4n .EX enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, /* Reserve 0 as invalid program type */ BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, /* See /usr/include/linux/bpf.h for the full list. */ }; .EE .in .PP Дополнительную информацию о типах программ eBPF смотрите далее. .PP Остальные поля \fIbpf_attr\fP заполняются следующим образом: .IP \[bu] 3 Поле \fIinsns\fP — массив инструкций \fIstruct bpf_insn\fP. .IP \[bu] Поле \fIinsn_cnt\fP — количество инструкций в программе, на которую ссылается \fIinsns\fP. .IP \[bu] Поле \fIlicense\fP — строка лицензии, которая должна быть совместима с GPL, чтобы можно было вызывать вспомогательные функции, помеченные как \fIgpl_only\fP (условия лицензии такие же как и для модулей ядра, то есть также можно использовать двойные лицензии, например «Dual BSD/GPL»). .IP \[bu] Поле \fIlog_buf\fP — указатель на буфер, выделенный вызывающим, в котором ядерный механизм проверки может хранить журнал проверки. Данный журнал представляет собой многострочный текст, из которого автор программы может понять как механизм проверки сделал вывод, что программа eBPF небезопасна. Формат вывода может поменяться в любое время, поскольку механизм проверки ещё дорабатывается. .IP \[bu] Поле \fIlog_size\fP — размер буфера, на который указывает \fIlog_buf\fP. Если размер буфера недостаточен для хранения всех сообщений от механизма проверки, то возвращается \-1, а \fIerrno\fP присваивается \fBENOSPC\fP. .IP \[bu] Поле \fIlog_level\fP — степень подробности отчёта механизма проверки. Значение ноль означает, что механизм проверки не будет вести журнал; в этом случае значение \fIlog_buf\fP должно быть равно указателю NULL, а \fIlog_size\fP равно нулю. .PP При применении \fBclose\fP(2) к файловому дескриптору, полученному от \fBBPF_PROG_LOAD\fP, происходит выгрузка программы eBPF (но смотрите ЗАМЕЧАНИЯ). .PP .\" .\" Карты доступны из программ eBPF и используются для обмена данными между программами eBPF, а также между программами eBPF и приложениями пользовательского пространства. Например, программы eBPF могут обрабатывать различные события (kprobe, пакеты) и сохранять свои данные в карте, а затем программы пользовательского пространства могут выбирать данные из карты. И наоборот, программы пользовательского пространства могут использовать карту в качестве механизма настройки, заполняя карту значениями, читаемыми программой eBPF, которая затем, согласно этим значениям, изменяет своё поведение на лету. .SS "Типы программ eBPF" .\" .\" FIXME .\" Somewhere in this page we need a general introduction to the .\" bpf_context. For example, how does a BPF program access the .\" context? The eBPF program type (\fIprog_type\fP) determines the subset of kernel helper functions that the program may call. The program type also determines the program input (context)\[em]the format of \fIstruct bpf_context\fP (which is the data blob passed into the eBPF program as the first argument). .PP Например, программа трассировки не имеет доступа к тому же поднабору вспомогательных функций, как у программы фильтрации сокетов (хотя они могут обращаться к некоторым одинаковым функциям). Также, входные данные (контекст) программы трассировки — это набор значений регистров, а у фильтра сокетов — сетевой пакет. .PP Набор функций, доступных программам eBPF определённого типа, может увеличиться в будущем. .PP Поддерживаются следующие типы программ: .TP \fBBPF_PROG_TYPE_SOCKET_FILTER\fP (начиная с Linux 3.19) В настоящее время, набор функций для \fBBPF_PROG_TYPE_SOCKET_FILTER\fP такой: .IP .in +4n .EX bpf_map_lookup_elem(map_fd, void *key) /* поиск ключа в map_fd */ bpf_map_update_elem(map_fd, void *key, void *value) /* обновление ключа/значения */ bpf_map_delete_elem(map_fd, void *key) /* удаление ключа из map_fd */ .EE .in .IP .\" FIXME: We need some text here to explain how the program .\" accesses __sk_buff. .\" See 'struct __sk_buff' and commit 9bac3d6d548e5 .\" .\" Alexei commented: .\" Actually now in case of SOCKET_FILTER, SCHED_CLS, SCHED_ACT .\" the program can now access skb fields. .\" Аргумент \fIbpf_context\fP представляет собой указатель на \fIstruct __sk_buff\fP. .TP \fBBPF_PROG_TYPE_KPROBE\fP (начиная с Linux 4.1) .\" commit 2541517c32be2531e0da59dfd7efc1ce844644f5 .\" FIXME Document this program type .\" Describe allowed helper functions for this program type .\" Describe bpf_context for this program type .\" .\" FIXME We need text here to describe 'kern_version' [Будет описано] .TP \fBBPF_PROG_TYPE_SCHED_CLS\fP (начиная с Linux 4.1) .\" commit 96be4325f443dbbfeb37d2a157675ac0736531a1 .\" commit e2e9b6541dd4b31848079da80fe2253daaafb549 .\" FIXME Document this program type .\" Describe allowed helper functions for this program type .\" Describe bpf_context for this program type [Будет описано] .TP \fBBPF_PROG_TYPE_SCHED_ACT\fP (начиная с Linux 4.1) .\" commit 94caee8c312d96522bcdae88791aaa9ebcd5f22c .\" commit a8cb5f556b567974d75ea29c15181c445c541b1f .\" FIXME Document this program type .\" Describe allowed helper functions for this program type .\" Describe bpf_context for this program type [Будет описано] .SS События После того как программа загружена, к ней можно присоединить событие. Различные подсистемы ядра делают это по\-разному. .PP .\" commit 89aa075832b0da4402acebd698d0411dcc82d03e Начиная с Linux 3.19, следующий вызов присоединяет программу \fIprog_fd\fP к сокету \fIsockfd\fP, который был создан вызовом \fBsocket\fP(2) ранее: .PP .in +4n .EX setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)); .EE .in .PP .\" commit 2541517c32be2531e0da59dfd7efc1ce844644f5 Начиная с Linux 4.1, следующий вызов можно использовать для присоединения программы eBPF, на которую ссылается файловый дескриптор \fIprog_fd\fP, к файловому дескриптору событий perf \fIevent_fd\fP, созданному вызовом \fBperf_event_open\fP(2) ранее: .PP .in +4n .EX ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); .EE .in .\" .\" .SH "ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ" При успешном выполнении возвращаемое значение зависит от используемой команды: .TP \fBBPF_MAP_CREATE\fP Новый файловый дескриптор, связанный с картой eBPF. .TP \fBBPF_PROG_LOAD\fP Новый файловый дескриптор, связанный с программой eBPF. .TP Все остальные команды Ноль. .PP В случае ошибки возвращается \-1, а \fIerrno\fP устанавливается в значение ошибки. .SH ОШИБКИ .TP \fBE2BIG\fP Программа eBPF слишком большая или достигнуто ограничение \fImax_entries\fP (максимальное количество элементов) в карте. .TP \fBEACCES\fP Для \fBBPF_PROG_LOAD\fP: хотя все инструкции программы корректны, программа считается ошибочной, так как признана небезопасной. Это может возникать из\-за доступа к запрещённой области или неинициализированного стека/регистра или функциональные ограничения не совпадают с типами или выполняется невыровненный доступ к памяти. В этом случае рекомендуется вызвать \fBbpf\fP() ещё раз с \fIlog_level = 1\fP и посмотреть \fIlog_buf\fP на предмет причины, указанной механизмом проверки. .TP \fBEAGAIN\fP For \fBBPF_PROG_LOAD\fP, indicates that needed resources are blocked. This happens when the verifier detects pending signals while it is checking the validity of the bpf program. In this case, just call \fBbpf\fP() again with the same parameters. .TP \fBEBADF\fP \fIfd\fP не является открытым файловым дескриптором. .TP \fBEFAULT\fP Значение одного из указателей (\fIkey\fP, \fIvalue\fP, \fIlog_buf\fP или \fIinsns\fP) находится вне доступного адресного пространства. .TP \fBEINVAL\fP Значение \fIcmd\fP не распознано ядром. .TP \fBEINVAL\fP Для \fBBPF_MAP_CREATE\fP: некорректное значение атрибутов или \fImap_type\fP. .TP \fBEINVAL\fP Для команд \fBBPF_MAP_*_ELEM\fP: некоторые поля \fIunion bpf_attr\fP, не используемые этой командой, не обнулены. .TP \fBEINVAL\fP Для \fBBPF_PROG_LOAD\fP: попытка загрузить некорректную программу. Программы eBPF могут быть признаны некорректными из\-за нераспознанных инструкций, использования зарезервированных полей, переходов за пределы диапазона, бесконечных циклов или вызовов неизвестных функций. .TP \fBENOENT\fP Для \fBBPF_MAP_LOOKUP_ELEM\fP и \fBBPF_MAP_DELETE_ELEM\fP: элемент с заданным \fIkey\fP не найден. .TP \fBENOMEM\fP Невозможно выделить достаточно памяти. .TP \fBEPERM\fP Вызов запущен без необходимых прав (без мандата \fBCAP_SYS_ADMIN\fP). .SH СТАНДАРТЫ Linux. .SH ИСТОРИЯ Linux 3.18. .SH ЗАМЕЧАНИЯ .\" commit 1be7f75d1668d6296b80bf35dcf6762393530afc .\" [Linux 5.6] mtk: The list of available functions is, I think, governed .\" by the check in net/core/filter.c::bpf_base_func_proto(). Prior to Linux 4.4, all \fBbpf\fP() commands require the caller to have the \fBCAP_SYS_ADMIN\fP capability. From Linux 4.4 onwards, an unprivileged user may create limited programs of type \fBBPF_PROG_TYPE_SOCKET_FILTER\fP and associated maps. However they may not store kernel pointers within the maps and are presently limited to the following helper functions: .IP \[bu] 3 get_random .PD 0 .IP \[bu] get_smp_processor_id .IP \[bu] tail_call .IP \[bu] ktime_get_ns .PD .PP Unprivileged access may be blocked by writing the value 1 to the file \fI/proc/sys/kernel/unprivileged_bpf_disabled\fP. .PP Объекты eBPF (карты и программы) могут использоваться несколькими процессами одновременно. Например, после \fBfork\fP(2) потомок наследует файловые дескрипторы, ссылающиеся на одинаковые объекты eBPF. Также, файловые дескрипторы, ссылающиеся на объекты eBPF, можно передавать через доменные сокеты UNIX. Файловые дескрипторы, ссылающиеся на объекты eBPF, можно дублировать обычным образом с помощью \fBdup\fP(2) и подобных вызовов. Объекты eBPF уничтожаются только после закрытия всех файловых дескрипторов, ссылающихся на объект. .PP .\" There are also examples for the tc classifier, in the iproute2 .\" project, in examples/bpf Программы eBPF можно писать на специализированной версии языка C, которая компилируется (с помощью компилятора \fBclang\fP) в байт\-код eBPF. В этой версии C отсутствуют различные свойства, например, глобальные переменные, функции с переменным числом аргументов, числа с плавающей запятой и нельзя передавать структуры в качестве аргументов. Примеры можно найти в файлах \fIsamples/bpf/*_kern.c\fP из дерева исходного кода ядра. .PP The kernel contains a just\-in\-time (JIT) compiler that translates eBPF bytecode into native machine code for better performance. Before Linux 4.15, the JIT compiler is disabled by default, but its operation can be controlled by writing one of the following integer strings to the file \fI/proc/sys/net/core/bpf_jit_enable\fP: .TP \fB0\fP Выключить компиляцию JIT (по умолчанию). .TP \fB1\fP Обычная компиляция. .TP \fB2\fP Режим отладки. Генерируемый код операций сбрасывается в виде шестнадцатеричных чисел в журнал ядра. Затем его можно дизассемблировать с помощью программы \fItools/net/bpf_jit_disasm.c\fP, которая находится в дереве исходного кода ядра. .PP .\" commit 290af86629b25ffd1ed6232c4e9107da031705cb Начиная с Linux 4.15, ядро можно настраивать через параметр \fBCONFIG_BPF_JIT_ALWAYS_ON\fP. В этом случае компилятор JIT всегда включён и \fIbpf_jit_enable\fP устанавливается в 1 и это нельзя изменить (данный параметр ядра был добавлен для предотвращения одной из атак «Спектр», направленной на интерпретатор BPF). .PP .\" Last reviewed in Linux 4.18-rc by grepping for BPF_ALU64 in arch/ .\" and by checking the documentation for bpf_jit_enable in .\" Documentation/sysctl/net.txt В настоящее время компилятор JIT для eBPF доступен на следующих архитектурах: .IP \[bu] 3 .\" commit 0a14842f5a3c0e88a1e59fac5c3025db39721f74 x86\-64 (начиная с Linux 3.18; cBPF начиная с Linux 3.0); .PD 0 .IP \[bu] .\" commit ddecdfcea0ae891f782ae853771c867ab51024c2 ARM32 (начиная с Linux 3.18; cBPF начиная с Linux 3.4); .IP \[bu] .\" commit 2809a2087cc44b55e4377d7b9be3f7f5d2569091 SPARC 32 (начиная с Linux 3.18; cBPF начиная с Linux 3.5); .IP \[bu] .\" commit e54bcde3d69d40023ae77727213d14f920eb264a ARM\-64 (начиная с Linux 3.18); .IP \[bu] .\" commit c10302efe569bfd646b4c22df29577a4595b4580 s390 (начиная с Linux 4.1; cBPF начиная с Linux 3.7); .IP \[bu] .\" commit 0ca87f05ba8bdc6791c14878464efc901ad71e99 .\" commit 156d0e290e969caba25f1851c52417c14d141b24 PowerPC 64 (начиная с Linux 4.8; cBPF начиная с Linux 3.1); .IP \[bu] .\" commit 7a12b5031c6b947cc13918237ae652b536243b76 SPARC 64 (начиная с Linux 4.12); .IP \[bu] .\" commit 03f5781be2c7b7e728d724ac70ba10799cc710d7 x86\-32 (начиная с Linux 4.18); .IP \[bu] .\" commit c6610de353da5ca6eee5b8960e838a87a90ead0c .\" commit f381bf6d82f032b7410185b35d000ea370ac706b MIPS 64 (начиная с Linux 4.18; cBPF начиная с Linux 3.16); .IP \[bu] .\" commit 2353ecc6f91fd15b893fa01bf85a1c7a823ee4f2 riscv (начиная с Linux 5.1). .PD .SH ПРИМЕРЫ .\" [[FIXME]] SRC BEGIN (bpf.c) .EX .\" == atomic64_add /* bpf+sockets example: * 1. create array map of 256 elements * 2. load program that counts number of packets received * r0 = skb\->data[ETH_HLEN + offsetof(struct iphdr, protocol)] * map[r0]++ * 3. attach prog_fd to raw socket via setsockopt() * 4. print number of received TCP/UDP packets every second */ int main(int argc, char *argv[]) { int sock, map_fd, prog_fd, key; long long value = 0, tcp_cnt, udp_cnt; \& map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 256); if (map_fd < 0) { printf("failed to create map \[aq]%s\[aq]\en", strerror(errno)); /* likely not run as root */ return 1; } \& struct bpf_insn prog[] = { BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* r6 = r1 */ BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol)), /* r0 = ip\->proto */ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, \-4), /* *(u32 *)(fp \- 4) = r0 */ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, \-4), /* r2 = r2 \- 4 */ BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* r1 = map_fd */ BPF_CALL_FUNC(BPF_FUNC_map_lookup_elem), /* r0 = map_lookup(r1, r2) */ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), /* if (r0 == 0) goto pc+2 */ BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */ BPF_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* lock *(u64 *) r0 += r1 */ BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */ BPF_EXIT_INSN(), /* return r0 */ }; \& prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog) / sizeof(prog[0]), "GPL"); \& sock = open_raw_sock("lo"); \& assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)) == 0); \& for (;;) { key = IPPROTO_TCP; assert(bpf_lookup_elem(map_fd, &key, &tcp_cnt) == 0); key = IPPROTO_UDP; assert(bpf_lookup_elem(map_fd, &key, &udp_cnt) == 0); printf("TCP %lld UDP %lld packets\en", tcp_cnt, udp_cnt); sleep(1); } \& return 0; } .EE .\" SRC END .PP Другой рабочий код можно найти в каталоге \fIsamples/bpf\fP дерева исходного кода ядра. .SH "СМ. ТАКЖЕ" \fBseccomp\fP(2), \fBbpf\-helpers\fP(7), \fBsocket\fP(7), \fBtc\fP(8), \fBtc\-bpf\fP(8) .PP Классический и расширенный BPF описаны в файле исходного кода ядра \fIDocumentation/networking/filter.txt\fP. .PP .SH ПЕРЕВОД Русский перевод этой страницы руководства был сделан Artyom Kunyov , Azamat Hackimov , Dmitriy Ovchinnikov , Dmitry Bolkhovskikh , ITriskTI , Yuri Kozlov и Иван Павлов . .PP Этот перевод является бесплатной документацией; прочитайте .UR https://www.gnu.org/licenses/gpl-3.0.html Стандартную общественную лицензию GNU версии 3 .UE или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ. .PP Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на .MT man-pages-ru-talks@lists.sourceforge.net .ME .