Разработчик(и) | Стивен МакКейн, Ван Якобсон |
---|---|
Первоначальный выпуск | 19 декабря 1992 г ( 1992-12-19 ) |
Операционная система | Несколько |
Berkeley Packet Filter ( BPF ; также BSD Packet Filter , классический BPF или cBPF ) — сетевой фильтр и фильтр пакетов , который позволяет захватывать и фильтровать пакеты компьютерной сети на уровне операционной системы . Он предоставляет необработанный интерфейс для уровней канала передачи данных , позволяя отправлять и получать необработанные пакеты уровня канала, [1] и позволяет процессу пользовательского пространства предоставлять программу-фильтр, которая указывает, какие пакеты он хочет получать. Например, процесс tcpdump может захотеть получать только пакеты, которые инициируют TCP-соединение. BPF возвращает только пакеты, которые проходят фильтр, предоставляемый процессом. Это позволяет избежать копирования нежелательных пакетов из ядра операционной системы в процесс, что значительно повышает производительность. Программа-фильтр имеет форму инструкций для виртуальной машины , которые интерпретируются или компилируются в машинный код с помощью механизма JIT и выполняются в ядре.
BPF используется программами, которым необходимо, помимо прочего, анализировать сетевой трафик. Если драйвер сетевого интерфейса поддерживает режим promiscuous , он позволяет перевести интерфейс в этот режим, чтобы можно было получать все пакеты в сети , даже те, которые предназначены для других хостов.
Механизм фильтрации BPF доступен в большинстве операционных систем типа Unix . Иногда BPF используется для обозначения только механизма фильтрации, а не всего интерфейса. Некоторые системы, такие как Linux и Tru64 UNIX , предоставляют необработанный интерфейс к канальному уровню, отличный от необработанного интерфейса BPF, но используют механизмы фильтрации BPF для этого необработанного интерфейса.
Ядро Linux предоставляет расширенную версию механизма фильтрации BPF, называемую eBPF , которая использует механизм JIT и которая используется для фильтрации пакетов, а также для других целей в ядре. eBPF также доступен для Microsoft Windows . [2]
Оригинальная статья была написана Стивеном МакКэнном и Ван Якобсоном в 1992 году в лаборатории Лоуренса в Беркли . [1] [3]
BPF предоставляет псевдоустройства , которые можно привязать к сетевому интерфейсу; операции чтения с устройства будут считывать буферы, заполненные пакетами, полученными на сетевом интерфейсе, а операции записи на устройство будут внедрять пакеты на сетевой интерфейс.
В 2007 году Роберт Уотсон и Кристиан Перон добавили расширения буфера нулевого копирования к реализации BPF в операционной системе FreeBSD , [4] что позволило захвату пакетов ядра в обработчике прерываний драйвера устройства записывать данные непосредственно в память пользовательского процесса, чтобы избежать необходимости в двух копиях для всех данных пакета, полученных через устройство BPF. В то время как одна копия остается в пути получения для пользовательских процессов, это сохраняет независимость различных потребителей устройств BPF, а также позволяет упаковывать заголовки в буфер BPF вместо копирования полных данных пакета. [5]
Возможности фильтрации BPF реализованы как интерпретатор машинного языка для виртуальной машины BPF , 32-битной машины с фиксированной длиной инструкций, одним аккумулятором и одним индексным регистром . Программы на этом языке могут извлекать данные из пакета, выполнять арифметические операции с данными из пакета и сравнивать результаты с константами или с данными в пакете или тестовыми битами в результатах, принимая или отклоняя пакет на основе результатов этих тестов.
BPF часто расширяется за счет «перегрузки» инструкций загрузки (ld) и сохранения (str).
Традиционные Unix-подобные реализации BPF могут использоваться в пользовательском пространстве, несмотря на то, что они написаны для пространства ядра. Это достигается с помощью условий препроцессора .
В некоторых проектах используются наборы инструкций BPF или методы выполнения, отличные от оригинальных.
Некоторые платформы, включая FreeBSD , NetBSD и WinPcap , используют JIT -компилятор (Just-in-Time Compiler , JIT) для преобразования инструкций BPF в машинный код с целью повышения производительности. Linux включает в себя JIT-компилятор BPF, который по умолчанию отключен.
Интерпретаторы режима ядра для того же языка виртуальной машины используются в механизмах уровня необработанных данных в других операционных системах, таких как Tru64 Unix , а также для фильтров сокетов в ядре Linux и в механизмах захвата пакетов WinPcap и Npcap .
Пользовательский интерпретатор для BPF предоставляется с реализацией libpcap/WinPcap/Npcap API pcap , так что при захвате пакетов в системах без поддержки режима ядра для этого механизма фильтрации пакеты могут быть отфильтрованы в пользовательском режиме; код, использующий API pcap, будет работать в обоих типах систем, хотя в системах, где фильтрация выполняется в пользовательском режиме, все пакеты, включая те, которые будут отфильтрованы, копируются из ядра в пользовательское пространство. Этот интерпретатор также может использоваться при чтении файла, содержащего пакеты, захваченные с помощью pcap.
Другой интерпретатор пользовательского режима — uBPF , который поддерживает JIT и eBPF (без cBPF). Его код был повторно использован для обеспечения поддержки eBPF в системах, отличных от Linux. [6] eBPF от Microsoft на Windows построен на uBPF и формальном верификаторе PREVAIL. [7] rBPF , переписанный на Rust uBPF, используется блокчейн-платформой Solana в качестве механизма выполнения. [8]
Классический BPF обычно выдается программой из некоторого очень высокоуровневого текстового правила, описывающего шаблон для сопоставления. Одно из таких представлений находится в libpcap . [9] Классический BPF и eBPF также могут быть записаны либо непосредственно как машинный код , либо с использованием языка ассемблера для текстового представления. Известные ассемблеры включают инструмент ядра Linux bpf_asm
(cBPF), bpfc
(cBPF) и ubpf
ассемблер (eBPF). bpftool
Команда также может действовать как дизассемблер для обеих разновидностей BPF. Языки ассемблера не обязательно совместимы друг с другом.
Байт-код eBPF недавно стал целью языков более высокого уровня. LLVM добавил поддержку eBPF в 2014 году, а GCC последовал за ним в 2019 году. Оба набора инструментов позволяют компилировать C и другие поддерживаемые языки в eBPF. Подмножество P4 также может быть скомпилировано в eBPF с помощью BCC, набора компиляторов на основе LLVM. [10]
Атака Spectre может использовать интерпретатор eBPF ядра Linux или JIT-компилятор для извлечения данных из других процессов ядра. [ 11] Функция усиления JIT в ядре смягчает эту уязвимость. [12]
Китайская группа по компьютерной безопасности Pangu Lab заявила, что АНБ использовало BPF для сокрытия сетевых коммуникаций как часть сложного бэкдора Linux . [13]
Начиная с версии 3.18, ядро Linux включает расширенную виртуальную машину BPF с десятью 64-битными регистрами, называемую eBPF . Она может использоваться для несетевых целей, например, для присоединения программ eBPF к различным точкам трассировки . [14] [15] [16] Начиная с версии ядра 3.19, фильтры eBPF могут быть присоединены к сокетам , [17] [18] и, начиная с версии ядра 4.1, к классификаторам управления трафиком для входящего и исходящего сетевого пути данных. [19] [20] Первоначальная и устаревшая версия была ретроактивно переименована в классический BPF ( cBPF ). В настоящее время ядро Linux запускает только eBPF, а загруженный байт-код cBPF прозрачно транслируется в представление eBPF в ядре перед выполнением программы. [21] Весь байт-код проверяется перед запуском, чтобы предотвратить атаки типа «отказ в обслуживании». До Linux 5.3 верификатор запрещал использование циклов, чтобы предотвратить потенциально неограниченное время выполнения; циклы с ограниченным временем выполнения теперь разрешены в более поздних ядрах. [22]