.\" -*- 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 "15 июня 2024 г." "Справочные страницы Linux 6.9.1" .SH НАИМЕНОВАНИЕ bpf \- выполнение команды с расширенной картой или программой BPF .SH ОБЗОР .nf \fB#include \fP .P \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. Дополнительный BPF (или eBPF) аналогичен оригинальному ("классическому") BPF, используемому для фильтрации сетевых пакетов. Как для программ cBPF, так и для eBPF ядро статически анализирует программы перед их загрузкой, чтобы убедиться, что они не могут нанести вред работающей системе. .P .\" See 'enum bpf_func_id' in include/uapi/linux/bpf.h .\" eBPF дополняет cBPF несколькими способами, включая возможность вызова фиксированного набора вспомогательных функций в ядре (с помощью дополнительного кода операции \fBBPF_CALL\fP, предоставляемого eBPF) и доступа к общим структурам данных, таким как карты eBPF. .SS "Дополнительная конструкция/архитектура BPF" Карты eBPF \- это универсальная структура данных для хранения различных типов данных. Типы данных обычно обрабатываются как большие двоичные, поэтому пользователь просто указывает размер ключа и размер значения во время создания карты. Другими словами, ключ/значение для данной карты могут иметь произвольную структуру. .P Пользовательский процесс может создавать несколько карт (с парами ключ/значение, представляющими собой непрозрачные байты данных) и получать к ним доступ через файловые дескрипторы. Различные программы eBPF могут параллельно получать доступ к одним и тем же картам. .P .\" Defined by the kernel constant MAX_TAIL_CALL_CNT in include/linux/bpf.h Существует один особый тип карт, называемый программным массивом. Этот тип карты хранит файловые дескрипторы, относящиеся к другим программам eBPF. Когда выполняется поиск по карте, поток выполнения программы перенаправляется на месте к началу другой программы eBPF и не возвращается обратно к вызывающей программе. Уровень вложенности имеет фиксированное ограничение в 32, так что бесконечные циклы не могут быть созданы. Во время выполнения дескрипторы программных файлов, хранящиеся на карте, могут быть изменены, поэтому функциональность программы может быть изменена в зависимости от конкретных требований. Все программы, упомянутые в карте программных массивов, должны быть предварительно загружены в ядро с помощью функции \fBbpf\fP(). Если поиск по карте завершается неудачей, текущая программа продолжает свое выполнение. Смотрите ниже \fBBPF_MAP_TYPE_PROG_ARRAY\fP для получения более подробной информации. .P В основном программы eBPF загружаются пользовательским процессом и автоматически выгружаются при завершении процесса. В некоторых случаях, например, \fBtc\-bpf\fP(8), программа будет продолжать работать внутри ядра даже после завершения процесса, который загрузил программу. В этом случае подсистема tc хранит ссылку на программу eBPF после того, как файловый дескриптор был закрыт программой пользовательского пространства. Таким образом, будет ли конкретная программа продолжать работать в ядре, зависит от того, как она будет подключена к данной подсистеме ядра после того, как она была загружена через \fBbpf\fP(). .P Каждая программа eBPF представляет собой набор инструкций, которые безопасны для выполнения до ее завершения. Встроенный в ядро верификатор статически определяет, что программа eBPF остановлена и безопасна для выполнения. Во время проверки ядро увеличивает количество ссылок для каждой из карт, используемых программой eBPF, так что прикрепленные карты не могут быть удалены до тех пор, пока программа не будет выгружена. .P Программы eBPF могут быть привязаны к различным событиям. Этими событиями могут быть прибытие сетевых пакетов, события трассировки, события классификации по правилам сетевого массового обслуживания (для программ eBPF, подключенных к классификатору \fBtc\fP(8)), и другие типы, которые могут быть добавлены в будущем. Новое событие запускает выполнение программы eBPF, которая может сохранять информацию об этом событии в картах eBPF. Помимо хранения данных, программы eBPF могут вызывать фиксированный набор встроенных в ядро вспомогательных функций. .P Одна и та же программа eBPF может быть привязана к нескольким событиям и разные программы eBPF могут получить доступ к одной и той же карте: .P .in +4n .EX трассировка трассировка трассировка пакет пакет пакет событие A событие B событие C на eth0 на eth1 на eth2 | | | | | \[ha] | | | | v | \-\-> трассировка <\-\- трассировка сокет вход tc вход tc программа_1 программа_2 программа_3 классификатор действие | | | | программа_4 программа_5 |\-\-\- \-\-\-\-\-| |\-\-\-\-\-\-| карта_3 | | карта_1 карта_2 \-\-| карта_4 |\-\- .EE .in .\" .SS Аргументы Операция, которая должна быть выполнена системным вызовом \fBbpf\fP(), определяется аргументом \fIcmd\fP. Каждая операция принимает сопутствующий аргумент, предоставляемый через \fIattr\fP, который является указателем на объединение типа \fIbpf_attr\fP (см. ниже). Неиспользуемые поля и отступы должны быть обнулены перед вызовом. Аргумент \fIsize\fP \- это размер объединения, на которое указывает \fIattr\fP. .P Значение, указанное в \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(): .P .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 maps" Карты представляют собой универсальную структуру данных для хранения различных типов данных. Они позволяют обмениваться данными между программами ядра eBPF, а также между ядром и пользовательскими приложениями. .P Каждый тип карты имеет следующие атрибуты: .IP \[bu] 3 тип .IP \[bu] максимальное количество элементов .IP \[bu] размер ключа в байтах .IP \[bu] размер значения в байтах .P Следующие функции\-оболочки демонстрируют, как можно использовать различные команды \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 и чтобы проверить, что программа не обращается к элементу карты c \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 Аналогично, когда создается man с a\fIvalue_size\fP равным 1 и программа bpf содержит .IP .in +4n .EX value = bpf_map_lookup_elem(...); *(u32 *) value = 1; .EE .in .IP программа будет отклонена, поскольку она обращается к указателю \fIvalue\fP за пределами указанного предела в 1 байт \fIvalue_size\fP. .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. .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, \fBDEFAULT\fP, \fBEPERM\fP и \fBEINVAL\fP. Этот метод можно использовать для перебора всех элементов на карте. .TP \fBclose(map_fd)\fP .\" Удаляет карту, на которую ссылается файловый дескриптор \fImap_fd\fP. При завершении работы программы пользовательского пространства, создавшей карту, все карты будут удалены автоматически (однако, смотрите ПРИМЕЧАНИЯ). .SS "eBPF map types" Поддерживаются следующие типы карт: .TP \fBBPF_MAP_TYPE_HASH\fP .\" commit 0f8e4bd8a1fc8c4185f1630061d0a1f2d197a475 Карты хэш\-таблиц, которые обладают следующими характеристиками: .RS .IP \[bu] 3 Карты создаются и уничтожаются программами пользовательского пространства. Как программы пользовательского пространства, так и программы eBPF могут выполнять операции поиска, обновления и удаления. .IP \[bu] Ядро заботится о выделении и освобождении пар key/value. .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 Оптимизированы для максимально быстрого поиска. В будущем компилятор verifier/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() заменяет элементы в стиле \fBnonatomic\fP; для атомарных обновлений вместо этого следует использовать карту хэш\-таблицы. Однако есть один особый случай, который также может быть использован с массивами: атомарные встроенные функции \fB__sync_fetch_и_add()\fP могут использоваться с 32\-разрядными и 64\-разрядными атомарными счетчиками. Например, они могут быть применен ко всему значению, если оно представляет собой один счетчик или, в случае структуры, содержащей несколько счетчиков, они могут быть использованы для отдельных счетчиков. Это довольно часто бывает полезно для агрегирования и учета событий. .RE .IP Среди применений для отображения массивов можно выделить следующие: .RS .IP \[bu] 3 Как "глобальные" переменные eBPF: массив из 1 элемента, ключ которого (индекс) равен 0, а значение представляет собой набор "глобальных" переменных, которые программы eBPF могут использовать для сохранения состояния между событиями. .IP \[bu] Объединение событий трассировки в фиксированный набор сегментов. .IP \[bu] Учет сетевых событий, например, количества пакетов и их размеров. .RE .TP \fBBPF_MAP_TYPE_PROG_ARRAY\fP (since 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. .P .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 .P Один из доступных типов программ \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, /* Смотрите полный список в /usr/include/linux/bpf.h. */ }; .EE .in .P Более подробную информацию о типах программ eBPF смотрите ниже. .P Остальные поля \fIbpf_attr\fP задаются следующим образом: .IP \[bu] 3 \fIins\fP \- это массив инструкций \fIstruct bpf_insn\fP. .IP \[bu] \fIinsn_cnt\fP \- это количество инструкций в программе, на которые ссылается \fIins\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 должен быть нулевым указателем, а \fIlog_size\fP должен быть равен нулю. .P Применение \fBclose\fP(2) к файловому дескриптору, возвращаемому \fBBPF_PROG_LOAD\fP, приведет к выгрузке программы eBPF (однако, смотрите ПРИМЕЧАНИЯ). .P .\" .\" Карты доступны из программ 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? Тип программы eBPF (\fIprog_type\fP) определяет подмножество вспомогательных функций ядра, которые может вызывать программа. Тип программы также определяет вводимые программой данные (содержимое) в формате \fIstruct bpf_context\fP, который представляет собой большой двоичный объект данных, передаваемый в программу eBPF в качестве первого аргумента. .P Например, программа трассировки не имеет точно такого же набора вспомогательных функций, как программа фильтрации сокетов (хотя у них могут быть некоторые общие вспомогательные функции). Аналогично, входными данными (контекстом) для программы трассировки является набор значений регистров, в то время как для фильтра сокетов это сетевой пакет. .P В будущем набор функций, доступных для программ eBPF данного типа, может быть расширен. .P Поддерживаются следующие типы программ: .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) /* look up key in a map_fd */ bpf_map_update_elem(map_fd, void *key, void *value) /* update key/value */ bpf_map_delete_elem(map_fd, void *key) /* delete key in a 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 События Как только программа загружена, ее можно привязать к событию. В различных подсистемах ядра это делается по\-разному. .P .\" commit 89aa075832b0da4402acebd698d0411dcc82d03e Начиная с Linux 3.19, следующий вызов присоединяет программу \fIprog_fd\fP к сокету \fIsockfd\fP, который был создан предыдущим вызовом \fBsocket\fP(2).: .P .in +4n .EX setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)); .EE .in .P .\" commit 2541517c32be2531e0da59dfd7efc1ce844644f5 Начиная с Linux 4.1, следующий вызов может использоваться для присоединения программы eBPF, на которую ссылается файловый дескриптор \fIprog_fd\fP, к файловому дескриптору события perf, \fIevent_fd\fP, который был создан предыдущим вызовом \fBperf_event_open\fP(2): .P .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 Все остальные команды Ноль. .P В случае ошибки возвращается \-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 Для параметра \fBBPF_PROG_LOAD\fP указывает, что необходимые ресурсы заблокированы. Это происходит, когда верификатор обнаруживает ожидающие отправки сигналы во время проверки работоспособности программы bpf. В этом случае просто вызовите \fBbpf\fP() еще раз с теми же параметрами. .TP \fBEBADF\fP \fIfd\fP не является открытым файловым дескриптором. .TP \fBEFAULT\fP Один из указателей (\fIkey\fP или \fIvalue\fP, или \fIlog_buf\fP, или \fIins\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(). До Linux 4.4 все команды \fBbpf\fP() требовали, чтобы вызывающий объект обладал возможностями \fBCAP_SYS_ADMIN\fP. Начиная с Linux 4.4, непривилегированный пользователь может создавать ограниченные программы типа \fBBPF_PROG_TYPE_SOCKET_FILTER\fP и связанные с ними карты. Однако они не могут хранить указатели ядра в картах и в настоящее время ограничены следующими вспомогательными функциями: .IP \[bu] 3 get_random .PD 0 .IP \[bu] get_smp_processor_id .IP \[bu] tail_call .IP \[bu] ktime_get_ns .PD .P Непривилегированный доступ может быть заблокирован путем записи значения 1 в файл \fI/proc/sys/kernel/unprivileged_bpf_disabled\fP. .P Объекты eBPF (карты и программы) могут совместно использоваться процессами. Например, после \fBfork\fP(2) дочерний процесс наследует файловые дескрипторы, относящиеся к тем же объектам eBPF. Кроме того, файловые дескрипторы, ссылающиеся на объекты eBPF, могут передаваться через доменные сокеты UNIX. Файловые дескрипторы, ссылающиеся на объекты eBPF, можно дублировать обычным способом, используя \fBdup\fP(2) и аналогичные вызовы. Объект eBPF освобождается только после того, как все файловые дескрипторы, ссылающиеся на этот объект, будут закрыты. .P .\" There are also examples for the tc classifier, in the iproute2 .\" project, in examples/bpf Программы eBPF могут быть написаны на ограниченном языке С, который компилируется (с помощью компилятора \fBclang\fP) в байт\-код bpf. В этом ограниченном C отсутствуют различные функции, такие как циклы, глобальные переменные, функции с переменным числом, числа с плавающей запятой и передача структур в качестве аргументов функций. Некоторые примеры можно найти в файлах \fIsamples/bpf/*_kern.c\fP в дереве исходных текстов ядра. .P Ядро содержит JIT\-компилятор, который преобразует байт\-код bpf в машинный код для повышения производительности. До Linux 4.15 JIT\-компилятор по умолчанию отключен, но его работой можно управлять, записав одно из следующих целых чисел в файл \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, представленной в дереве исходных текстов ядра. .P .\" commit 290af86629b25ffd1ed6232c4e9107da031705cb Начиная с Linux 4.15, ядро может быть настроено с помощью параметра \fBCONFIG_BPF_JIT_ALWAYS_ON\fP. В этом случае JIT\-компилятор всегда включен, а параметр \fIbpf_jit_enable\fP инициализируется значением 1 и является неизменяемым. Этот параметр конфигурации ядра был предоставлен в качестве средства защиты от одной из атак Spectre на интерпретатор BPF. .P .\" 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\-компилятор для bpf в настоящее время доступен для следующих архитектур: .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 ПРИМЕРЫ .\" 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]\[rs]n", 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\[rs]n", tcp_cnt, udp_cnt); sleep(1); } \& return 0; } .EE .\" SRC END .P Другой подробный рабочий код можно найти в каталоге \fIsamples/bpf\fP в дереве исходных текстов ядра. .SH "СМОТРИТЕ ТАКЖЕ" \fBseccomp\fP(2), \fBbpf\-helpers\fP(7), \fBsocket\fP(7), \fBtc\fP(8), \fBtc\-bpf\fP(8) .P Как классический, так и расширенный BPF описаны в исходном файле ядра \fIDocumentation/networking/filter.txt\fP. .PP .SH ПЕРЕВОД Русский перевод этой страницы руководства разработал(и) Aleksandr Felda и Kirill Rekhov . .PP Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, .UR https://www.gnu.org/licenses/gpl-3.0.html .UE версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. .PP Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу .MT списка рассылки русских переводчиков .ME .