Provided by: manpages-ru-dev_4.19.0-7_all bug

ИМЯ

       bpf - выполняет команду с расширенной картой BPF или программу

СИНТАКСИС

       #include <linux/bpf.h>

       int bpf(int cmd, union bpf_attr *attr, unsigned int size);

ОПИСАНИЕ

       Системный  вызов  bpf()  выполняет  набор  операций,  связанных  с  расширенными пакетными
       фильтрами  Беркли  (Berkeley  Packet  Filters).  Расширенные  BPF   (или   eBPF)   подобны
       первоначальным  («классическим»)  BPF  (cBPF), которые используются для фильтрации сетевых
       пакетов. Перед  загрузкой  программы  cBPF  и  eBPF  анализируются  ядром  на  предмет  их
       безвредности для работающей системы.

       Набор eBPF расширяет cBPF в разных направлениях, включая способность вызова фиксированного
       набора  вспомогательных  функций  ядра   (через   расширенный   код   операции   BPF_CALL,
       предоставляемый eBPF) и доступа к общим структурам данных, таким как карты eBPF.

   Структура/архитектура расширенных BPF
       Карты  eBPF  — это обобщённая структура данных, которая позволяет хранить данных различных
       типов. Типы данных, в общем случае, считаются двоичными объектами (binary blobs),  поэтому
       пользователь  просто  указывает размер ключа и размер значения при создании карты. Другими
       словами, ключ/значение задаваемой карты могут иметь произвольную структуру.

       Пользовательский  процесс  может  создать   несколько   карт   (с   парами   ключ/значение
       нераспознаваемых  байт  данных  (opaque  bytes  of data)) и работать с ними через файловые
       дескрипторы. Несколько программ eBPF могут  получать  доступ  к  одним  и  тем  же  картам
       параллельно.  Решение, что хранить в картах, полностью отдано пользовательскому процессу и
       программе eBPF.

       Существует специальный карточный тип, называемый программным массивом (program  array).  В
       данном  типе  карты  хранятся  файловые дескрипторы, указывающие на другие программы eBPF.
       Когда выполняется поиск в карте программный поток в этом месте перенаправляется  в  начало
       другой  программы  eBPF  и  не  возвращается  в  вызывающую программу. Уровень вложенности
       ограничен 32, поэтому  бесконечные  циклы  невозможны.  Во  время  выполнения  программные
       файловые дескрипторы, хранящиеся в карте, не могут быть изменены, поэтому функциональность
       программы можно изменить только  на  основе  специальных  требований.  Все  программы,  на
       которые  есть  ссылки  из  карты программного массива, должны заранее загружаться в ядро с
       помощью bpf(). Если поиск по карте завершился с ошибкой, то текущая  программа  продолжает
       выполняться. Подробней смотрите далее в описании BPF_MAP_TYPE_PROG_ARRAY.

       Обычно,  программы  eBPF  загружаются  пользовательским  процессом  и  выгружаются при его
       завершении. В некоторых случаях, например, tc-bpf(8), программа продолжает работать внутри
       ядра  даже после того, как процесс загрузивший программу, закончил работать. В этом случае
       ссылку на программу eBPF после того, как файловый  дескриптор  был  закрыт  программой  из
       пользовательского  пространства,  содержит  подсистема  tc.  То есть, будет ли специальная
       программа продолжать работать внутри ядра, зависит от того, будет ли  она  присоединена  к
       указанной подсистеме ядра после загрузки через bpf().

       Программа eBPF представляет собой набор инструкций, безопасно выполняющаяся от начала и до
       конца. Ядерный механизм проверки статически определяет, что программа eBPF завершится и её
       безопасно  запускать.  Во время проверки ядро увеличивает счётчик ссылок для каждой карты,
       которая используется программой eBPF, поэтому присоединённые карты невозможно удалить пока
       не будет выгружена программа.

       Программы  eBPF  могут быть присоединены к различным событиям. Эти события могут возникать
       при поступлении сетевых пакетов, это могут быть события трассировки, события распределения
       по  сетевым  очередям  (для программ eBPF, присоединённых к классификатору tc(8)) и другие
       типы событий, которые могут быть добавлены в будущем. Новое событие активирует  выполнение
       программы  eBPF,  которое  может  сохранить  информацию  о  событии  в  картах eBPF. Кроме
       сохранения данных, программы  eBPF  могут  вызывать  фиксированный  набор  вспомогательных
       функций ядра.

       Программа  eBPF  может быть присоединена к нескольким событиям, а различные программы eBPF
       могут иметь доступ к одной карте:

           tracing     tracing    tracing    packet      packet     packet
           event A     event B    event C    on eth0     on eth1    on eth2
            |             |         |          |           |          ^
            |             |         |          |           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 |--

   Аргументы
       The operation to be performed by the bpf()  system call is determined by the cmd argument.
       Each  operation takes an accompanying argument, provided via attr, which is a pointer to a
       union of type bpf_attr (see below).  The unused fields and  padding  must  be  zeroed  out
       before the call.  The size argument is the size of the union pointed to by attr.

       Значением cmd может быть одно из:

       BPF_MAP_CREATE
              Создать  карту  и  вернуть файловый дескриптор, который указывает на эту карту. Для
              нового файлового дескриптора флаг close-on-exec (смотрите fcntl(2)) устанавливается
              автоматически.

       BPF_MAP_LOOKUP_ELEM
              Найти элемент по ключу в указанной карте и вернуть его значение.

       BPF_MAP_UPDATE_ELEM
              Создать или обновить элемент (пару ключ/значение) в указанной карте.

       BPF_MAP_DELETE_ELEM
              Найти и удалить элемент по ключу в указанной карте.

       BPF_MAP_GET_NEXT_KEY
              Найти элемент по ключу в указанной карте и вернуть ключ следующего элемента.

       BPF_PROG_LOAD
              Проверить  и  загрузить  программу  eBPF;  возвращается  новый файловый дескриптор,
              связанный  с  программой.  Для  нового  файлового  дескриптора  флаг  close-on-exec
              (смотрите fcntl(2)) устанавливается автоматически.

              Объединение  bpf_attr состоит из различных анонимных структур, которые используются
              в различных командах bpf():

           union bpf_attr {
               struct {    /* используется в BPF_MAP_CREATE */
                   __u32         map_type;
                   __u32         key_size;    /* размер ключа в байтах */
                   __u32         value_size;  /* размер значения в байтах */
                   __u32         max_entries; /* максимальное количество
                                                 элементов в карте */
               };

               struct {    /* используется в командах BPF_MAP_*_ELEM
                              и BPF_MAP_GET_NEXT_KEY */
                   __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;      /* 'const struct bpf_insn *' */
                   __aligned_u64 license;    /* 'const char *' */
                   __u32         log_level;  /* verbosity level of verifier */
                   __u32         log_size;   /* size of user buffer */
                   __aligned_u64 log_buf;    /* user supplied 'char *'
                                                buffer */
                   __u32         kern_version;
                                             /* checked when prog_type=kprobe
                                                (since Linux 4.1) */
               };
           } __attribute__((aligned(8)));

   Карты eBPF
       Карты представляют собой обобщённую структуру данных,  которая  позволяет  хранить  данных
       различных  типов.  Карты  позволяют использовать данные нескольким ядерным программам eBPF
       одновременно, а также ядру и приложениям пользовательского пространства.

       Каждый тип карты имеет следующие атрибуты:

       •  тип

       •  максимальное количество элементов

       •  размер ключа в байтах

       •  размер значения в байтах

       Следующие обёрточные функции показывают  как  для  доступа  к  картам  можно  использовать
       различные команды bpf(). Для указания вызываемой операции служит параметр cmd.

       BPF_MAP_CREATE
              Команда  BPF_MAP_CREATE  создаёт  новую карту, возвращая новый файловый дескриптор,
              который указывает на карту.

                  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));
                  }

              Новая карта имеет тип, указанный в map_type, и атрибуты, в соответствии с key_size,
              value_size  и  max_entries.  При  успешном  выполнении  этой  операции возвращается
              файловый дескриптор. При ошибке возвращается -1  и  в  errno  записывается  EINVAL,
              EPERM или ENOMEM.

              Атрибуты  key_size  и  value_size  будут  использоваться  механизмом  проверки  при
              загрузке программы  для  проверки  того,  что  программа  вызывает  вспомогательные
              функции  bpf_map_*_elem()  с  корректно  инициализированным  key и что программа не
              обращается к элементу карты value за пределами, задаваемыми  value_size.  Например,
              когда карта создана с key_size равным 8 и программа eBPF вызывает

                  bpf_map_lookup_elem(map_fd, fp - 4)

              то программа не будет загружена, так как от вспомогательной ядерной функции

                  bpf_map_lookup_elem(map_fd, void *key)

              ожидается чтение 8 байт из места, указанного key, но начальный адрес fp - 4 (где fp
              — вершина стека) выходит за границы стека.

              Аналогично, когда карта создаётся с value_size равным 1 и программа eBPF содержит

                  value = bpf_map_lookup_elem(...);
                  *(u32 *) value = 1;

              то программа не будет загружена, так как  она  обращается  к  указателю  value  вне
              ограничения value_size, равного 1 байту.

              В настоящее время поддерживаются следующие значения map_type:

                  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. */
                  };

              Для  map_type  выбирается  одна из доступных реализаций карт в ядре. Для всех типов
              карт программы eBPF получают доступ через одни  и  те  же  вспомогательные  функции
              bpf_map_lookup_elem()  и  bpf_map_update_elem(). Подробности о различных типах карт
              приведены далее.

       BPF_MAP_LOOKUP_ELEM
              Команда BPF_MAP_LOOKUP_ELEM ищет  элемент  с  заданным  key  в  карте,  на  которую
              ссылается файловый дескриптор fd.

                  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));
                  }

              Если  элемент найден, то возвращается ноль и значение элемента сохраняется в value,
              которое должно указывать на буфер размером value_size байт.

              Если элемент не найден, то возвращается -1, а в errno записывается ENOENT.

       BPF_MAP_UPDATE_ELEM
              Команда BPF_MAP_UPDATE_ELEM создаёт или обновляет элемент с заданными  key/value  в
              карте, на которую ссылается файловый дескриптор fd.

                  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));
                  }

              В аргументе flags должно быть указано одно из:

              BPF_ANY
                     Создать новый элемент или обновить существующий.

              BPF_NOEXIST
                     Создать новый элемент, только если он не существует.

              BPF_EXIST
                     Обновить существующий элемент.

              При успешном выполнении операции возвращается ноль. При ошибке возвращается -1, а в
              errno записывается EINVAL, EPERM, ENOMEM или E2BIG. Значение E2BIG показывает,  что
              количество  элементов  в  карте  достигло  ограничения  max_entries,  заданного при
              создании карты. Значение EEXIST устанавливается, если в flags указано BPF_NOEXIST и
              элемент с key уже существует в карте. Значение ENOENT устанавливается, если в flags
              указано BPF_EXIST и элемент с key не существует в карте.

       BPF_MAP_DELETE_ELEM
              Команда BPF_MAP_DELETE_ELEM удаляет элемент с  ключом  key  из  карты,  на  которую
              ссылается файловый дескриптор fd.

                  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));
                  }

              При  успешном выполнении возвращается ноль. Если элемент не найден, то возвращается
              -1, а errno присваивается значение ENOENT.

       BPF_MAP_GET_NEXT_KEY
              Команда BPF_MAP_GET_NEXT_KEY  ищет  элемент  по  ключу  key  в  карте,  на  которую
              указывает файловый дескриптор fd, и присваивает указателю next_key  ключ следующего
              элемента.

                  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));
                  }

              Если key найден, то операция возвращает ноль  и  устанавливает  указатель  next_key
              равным ключу следующего элемента. Если key не найден, то операция возвращает ноль и
              устанавливает  указатель  next_key  равным  ключу  первого  элемента.  Если  key  —
              последний элемент, то возвращается -1 и errno присваивается значение ENOENT. Другие
              возможные значения errno: ENOMEM,  EFAULT,  EPERM  и  EINVAL.  Данный  метод  можно
              использовать для обхода всех элементов в карте.

       close(map_fd)
              Данный  вызов удаляет карту, на которую ссылается файловый дескриптор map_fd. Когда
              программа пользовательского пространства, создавшая карту,  завершает  работу,  все
              карты удаляются автоматически (но смотрите ЗАМЕЧАНИЯ).

   Типы карт eBPF
       Поддерживаются следующие типы карт:

       BPF_MAP_TYPE_HASH
              Карты в виде хэш-таблицы имеют следующие характеристики:

              •  Карты  создаются  и  уничтожаются программами пользовательского пространства. Из
                 пользовательских и eBPF программ можно выполнять операции поиска,  обновления  и
                 удаления.

              •  За выделение/освобождения места для пар ключ/значение отвечает ядро.

              •  Вспомогательная  функция  map_update_elem()  завершится  с  ошибкой  при вставке
                 нового элемента при достижении ограничения max_entries (то есть, программы  eBPF
                 не смогут занять всю память).

              •  Функция map_update_elem() выполняет атомарную замену существующего элемента.

              Карты в виде хэш-таблицы оптимизированы под скоростной поиск.

       BPF_MAP_TYPE_ARRAY
              Карты в виде массива имеют следующие характеристики:

              •  Оптимизированы  под  самый быстрый поиск. В будущем механизм проверки/компилятор
                 JIT смогут распознавать  операции  lookup(),  которые  выдают  ключ-константу  и
                 оптимизировать  его в указатель-константу. Также возможно оптимизировать ключ не
                 константу в явный  указатель  арифметически,  так  как  указатели  и  value_size
                 являются  константами  на всём протяжении жизни программы eBPF. Другими словами,
                 array_map_lookup_elem() может быть «встроена»  механизмом  проверки/компилятором
                 JIT, одновременно сохраняя доступ к этой карте из пространства пользователя.

              •  Место  под  все  элементы  массива  выделяется  заранее и заполняется нулями при
                 инициализации.

              •  Ключом является индекс массива, и он всегда занимает четыре байта.

              •  Функция map_delete_elem() завершается с ошибкой EINVAL, так как элементы  нельзя
                 удалять.

              •  Функция  map_update_elem() заменяет элементы не атомарным образом; для атомарных
                 обновлений нужно использовать карту  в  виде  хэш-таблицы.  Однако  есть  особый
                 случай при работе и с массивами: можно использовать встроенную атомарную функцию
                 __sync_fetch_and_add() для работы с  32  и  64-битными  атомарными  указателями.
                 Например,  это можно использовать, если само значение целиком представляет собой
                 одиночный счётчик, или если есть структура с несколькими счётчиками, то  функцию
                 можно  применять  для  отдельных  счётчиков.  Это  довольно  часто  полезно  для
                 агрегирования событий и учёта.

              Возможные варианты использования карт в виде массивов:

              •  «Глобальные» переменные eBPF: массив из одного элемента  1,  чей  ключ  (индекс)
                 равен  0,  а  значение — набор «глобальных» переменных, в которых программы eBPF
                 могут хранить состояние между событиями.

              •  Агрегация событий трассировки в постоянный набор блоков (buckets).

              •  Учёт сетевых событий, например, количество и размер пакетов.

       BPF_MAP_TYPE_PROG_ARRAY (начиная с Linux 4.2)
              Карта в виде программного массива — специальный вариант карты  в  виде  массива,  в
              которой  значения  содержат  только  файловые  дескрипторы,  указывающие  на другие
              программы eBPF. То есть key_size и value_size  должны  занимать  по  четыре  байта.
              Данная карта используется вместе с функцией bpf_tail_call().

              Это  означает,  что  программу  eBPF  с  картой  в  виде программного массива можно
              присоединить из ядра с помощью вызова

                  void bpf_tail_call(void *context, void *prog_map,
                                     unsigned int index);

              и, таким образом, заменить свой  программный  поток  потоком  из  элемента  массива
              заданной  программы,  если  он  есть.  Это  можно рассматривать как подобие таблицы
              переходов к другой программе eBPF. Вызываемая программа использует тот же стек. При
              выполнении перехода в новую программу возврат к старой более невозможен.

              Если программа eBPF с указанным индексом не найдена  в программном массиве (так как
              элемент карты не содержит корректного  программного  файлового  дескриптора,  поиск
              индекса/ключа   вне  границ  диапазона  или  превышено  ограничение  на  количество
              вложенных вызовов (32)), то продолжается выполнение  текущей  программы  eBPF.  Это
              можно использовать для обработки значений по умолчанию.

              Например,  карта  в  виде  программного  массива  полезна, если при трассировке или
              работе с сетью нужно передать обработку отдельных системных вызовов или  протоколов
              их собственным подпрограммам и использовать их идентификаторы как отдельные индексы
              карт. Такой подход позволяет достичь  преимуществ  в  производительности,  а  также
              преодолеть  возможное  ограничение на количество инструкций одной программы eBPF. В
              динамичных окружениях пользовательский  демон  может  атомарно  заменить  отдельные
              подпрограммы  во  время выполнения на новые версии, чтобы полностью изменить работу
              программы, например, при глобальном изменении политик.

   Программы eBPF
       Команда BPF_PROG_LOAD используется  для  загрузки  программы  eBPF  в  ядро.  Возвращаемым
       значением является новый файловый дескриптор, связанный с этой программой eBPF.

           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));
           }

       Значением prog_type может быть один из типов программ:

                  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. */
                  };

       Дополнительную информацию о типах программ eBPF смотрите далее.

       Остальные поля bpf_attr заполняются следующим образом:

       •  Поле insns — массив инструкций struct bpf_insn.

       •  Поле insn_cnt — количество инструкций в программе, на которую ссылается insns.

       •  Поле  license — строка лицензии, которая должна быть совместима с GPL, чтобы можно было
          вызывать вспомогательные функции, помеченные как gpl_only (условия  лицензии  такие  же
          как  и  для  модулей  ядра, то есть также можно использовать двойные лицензии, например
          «Dual BSD/GPL»).

       •  Поле log_buf — указатель на буфер, выделенный вызывающим, в  котором  ядерный  механизм
          проверки  может хранить журнал проверки. Данный журнал представляет собой многострочный
          текст, из которого автор программы может понять как механизм проверки сделал вывод, что
          программа  eBPF  небезопасна.  Формат  вывода может поменяться в любое время, поскольку
          механизм проверки ещё дорабатывается.

       •  Поле log_size — размер  буфера,  на  который  указывает  log_buf.  Если  размер  буфера
          недостаточен  для  хранения всех сообщений от механизма проверки, то возвращается -1, а
          errno присваивается ENOSPC.

       •  Поле log_level — степень подробности отчёта механизма проверки. Значение ноль означает,
          что механизм проверки не будет вести журнал; в этом случае значение log_buf должно быть
          равно указателю NULL, а log_size равно нулю.

       При применении close(2) к файловому дескриптору, полученному от BPF_PROG_LOAD,  происходит
       выгрузка программы eBPF (но смотрите ЗАМЕЧАНИЯ).

       Карты  доступны из программ eBPF и используются для обмена данными между программами eBPF,
       а также между программами eBPF и приложениями  пользовательского  пространства.  Например,
       программы  eBPF  могут  обрабатывать  различные  события (kprobe, пакеты) и сохранять свои
       данные в карте, а затем программы пользовательского пространства могут выбирать данные  из
       карты.  И  наоборот,  программы  пользовательского пространства могут использовать карту в
       качестве механизма  настройки,  заполняя  карту  значениями,  читаемыми  программой  eBPF,
       которая затем, согласно этим значениям, изменяет своё поведение на лету.

   Типы программ eBPF
       The  eBPF  program type (prog_type)  determines the subset of kernel helper functions that
       the program may call.  The program type also determines the  program  input  (context)—the
       format  of  struct bpf_context (which is the data blob passed into the eBPF program as the
       first argument).

       Например, программа трассировки не имеет  доступа  к  тому  же  поднабору  вспомогательных
       функций,  как  у  программы  фильтрации  сокетов  (хотя  они  могут обращаться к некоторым
       одинаковым функциям). Также, входные данные (контекст) программы трассировки —  это  набор
       значений регистров, а у фильтра сокетов — сетевой пакет.

       Набор функций, доступных программам eBPF определённого типа, может увеличиться в будущем.

       Поддерживаются следующие типы программ:

       BPF_PROG_TYPE_SOCKET_FILTER (начиная с Linux 3.19)
              В настоящее время, набор функций для BPF_PROG_TYPE_SOCKET_FILTER такой:

                  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 */

              Аргумент bpf_context представляет собой указатель на struct __sk_buff.

       BPF_PROG_TYPE_KPROBE (начиная с Linux 4.1)
              [Будет описано]

       BPF_PROG_TYPE_SCHED_CLS (начиная с Linux 4.1)
              [Будет описано]

       BPF_PROG_TYPE_SCHED_ACT (начиная с Linux 4.1)
              [Будет описано]

   События
       После того как программа загружена, к ней можно присоединить событие. Различные подсистемы
       ядра делают это по-разному.

       Начиная с Linux 3.19, следующий вызов присоединяет  программу  prog_fd  к  сокету  sockfd,
       который был создан вызовом socket(2) ранее:

           setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_BPF,
                      &prog_fd, sizeof(prog_fd));

       Начиная  с Linux 4.1, следующий вызов можно использовать для присоединения программы eBPF,
       на которую ссылается файловый дескриптор prog_fd, к  файловому  дескриптору  событий  perf
       event_fd, созданному вызовом perf_event_open(2) ранее:

           ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

       При успешном выполнении возвращаемое значение зависит от используемой команды:

       BPF_MAP_CREATE
              Новый файловый дескриптор, связанный с картой eBPF.

       BPF_PROG_LOAD
              Новый файловый дескриптор, связанный с программой eBPF.

       Все остальные команды
              Ноль.

       В случае ошибки возвращается -1, а errno устанавливается в значение ошибки.

ОШИБКИ

       E2BIG  Программа eBPF слишком большая или достигнуто ограничение max_entries (максимальное
              количество элементов) в карте.

       EACCES Для BPF_PROG_LOAD: хотя все инструкции  программы  корректны,  программа  считается
              ошибочной,  так  как  признана  небезопасной.  Это  может возникать из-за доступа к
              запрещённой области или  неинициализированного  стека/регистра  или  функциональные
              ограничения  не совпадают с типами или выполняется невыровненный доступ к памяти. В
              этом случае рекомендуется вызвать bpf() ещё  раз  с  log_level  =  1  и  посмотреть
              log_buf на предмет причины, указанной механизмом проверки.

       EBADF  fd не является открытым файловым дескриптором.

       EFAULT Значение  одного  из  указателей  (key,  value,  log_buf  или  insns) находится вне
              доступного адресного пространства.

       EINVAL Значение cmd не распознано ядром.

       EINVAL Для BPF_MAP_CREATE: некорректное значение атрибутов или map_type.

       EINVAL Для команд BPF_MAP_*_ELEM: некоторые поля  union  bpf_attr,  не  используемые  этой
              командой, не обнулены.

       EINVAL Для  BPF_PROG_LOAD:  попытка загрузить некорректную программу. Программы eBPF могут
              быть  признаны  некорректными  из-за   нераспознанных   инструкций,   использования
              зарезервированных  полей,  переходов  за  пределы диапазона, бесконечных циклов или
              вызовов неизвестных функций.

       ENOENT Для BPF_MAP_LOOKUP_ELEM и BPF_MAP_DELETE_ELEM: элемент с заданным key не найден.

       ENOMEM Невозможно выделить достаточно памяти.

       EPERM  Вызов запущен без необходимых прав (без мандата CAP_SYS_ADMIN).

ВЕРСИИ

       Системный вызов bpf() впервые появился в Linux 3.18.

СТАНДАРТЫ

       Системный вызов bpf() есть только в Linux.

ЗАМЕЧАНИЯ

       Prior to Linux 4.4, all bpf()  commands require  the  caller  to  have  the  CAP_SYS_ADMIN
       capability.   From  Linux 4.4 onwards, an unprivileged user may create limited programs of
       type BPF_PROG_TYPE_SOCKET_FILTER and associated maps.  However they may not  store  kernel
       pointers within the maps and are presently limited to the following helper functions:

       •  get_random
       •  get_smp_processor_id
       •  tail_call
       •  ktime_get_ns

       Unprivileged   access   may   be   blocked   by   writing   the   value   1  to  the  file
       /proc/sys/kernel/unprivileged_bpf_disabled.

       Объекты eBPF (карты и программы) могут использоваться несколькими процессами одновременно.
       Например,  после fork(2) потомок наследует файловые дескрипторы, ссылающиеся на одинаковые
       объекты eBPF. Также, файловые дескрипторы, ссылающиеся на объекты eBPF,  можно  передавать
       через  доменные  сокеты  UNIX.  Файловые  дескрипторы,  ссылающиеся на объекты eBPF, можно
       дублировать обычным образом с помощью dup(2) и подобных вызовов. Объекты eBPF уничтожаются
       только после закрытия всех файловых дескрипторов, ссылающихся на объект.

       Программы eBPF можно писать на специализированной версии языка C, которая компилируется (с
       помощью компилятора clang)  в  байт-код  eBPF.  В  этой  версии  C  отсутствуют  различные
       свойства, например, глобальные переменные, функции с переменным числом аргументов, числа с
       плавающей запятой и нельзя передавать структуры в качестве аргументов. Примеры можно найти
       в файлах samples/bpf/*_kern.c из дерева исходного кода ядра.

       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 /proc/sys/net/core/bpf_jit_enable:

       0      Выключить компиляцию JIT (по умолчанию).

       1      Обычная компиляция.

       2      Режим отладки. Генерируемый код  операций  сбрасывается  в  виде  шестнадцатеричных
              чисел  в  журнал  ядра.  Затем  его  можно  дизассемблировать  с  помощью программы
              tools/net/bpf_jit_disasm.c, которая находится в дереве исходного кода ядра.

       Начиная с Linux 4.15, ядро можно настраивать через  параметр  CONFIG_BPF_JIT_ALWAYS_ON.  В
       этом  случае  компилятор  JIT  всегда  включён  и bpf_jit_enable устанавливается в 1 и это
       нельзя изменить (данный параметр ядра  был  добавлен  для  предотвращения  одной  из  атак
       «Спектр», направленной на интерпретатор BPF).

       В настоящее время компилятор JIT для eBPF доступен на следующих архитектурах:

       •  x86-64 (начиная с Linux 3.18; cBPF начиная с Linux 3.0);
       •  ARM32 (начиная с Linux 3.18; cBPF начиная с Linux 3.4);
       •  SPARC 32 (начиная с  Linux 3.18; cBPF начиная с Linux 3.5);
       •  ARM-64 (начиная с Linux 3.18);
       •  s390 (начиная с Linux 4.1; cBPF начиная с Linux 3.7);
       •  PowerPC 64 (начиная с Linux 4.8; cBPF начиная с Linux 3.1);
       •  SPARC 64 (начиная с Linux 4.12);
       •  x86-32 (начиная с Linux 4.18);
       •  MIPS 64 (начиная с Linux 4.18; cBPF начиная с Linux 3.16);
       •  riscv (начиная с Linux 5.1).

ПРИМЕРЫ

       /* пример bpf+sockets:
        * 1. создать карту в виде массива из 256 элементов
        * 2. загрузить программу, подсчитывающую количество принятых пакетов
        *    r0 = skb->data[ETH_HLEN + offsetof(struct iphdr, protocol)]
        *    map[r0]++
        * 3. присоединить prog_fd к неструктурированному сокету от setsockopt()
        * 4. напечатать количество пакетов TCP/UDP, принимаемых каждую секунду
        */
       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 '%s'\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(),                            /* вернуть 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 пакетов\n", tcp_cnt, udp_cnt);
               sleep(1);
           }

           return 0;
       }

       Другой рабочий код можно найти в каталоге samples/bpf дерева исходного кода ядра.

СМ. ТАКЖЕ

       seccomp(2), bpf-helpers(7), socket(7), tc(8), tc-bpf(8)

       Классический    и    расширенный    BPF    описаны    в    файле   исходного   кода   ядра
       Documentation/networking/filter.txt.

ПЕРЕВОД

       Русский    перевод    этой    страницы    руководства    был    сделан    Artyom    Kunyov
       <artkun@guitarplayer.ru>, Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitriy Ovchinnikov
       <dmitriyxt5@gmail.com>,    Dmitry     Bolkhovskikh     <d20052005@yandex.ru>,     ITriskTI
       <ITriskTI@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

       Этот  перевод  является  бесплатной  документацией;  прочитайте  Стандартную  общественную
       лицензию GNU версии 3 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

       Если  вы  обнаружите  ошибки  в  переводе этой страницы руководства, пожалуйста, отправьте
       электронное письмо на ⟨man-pages-ru-talks@lists.sourceforge.net⟩.