Berkeley Packet Filter
BPF (Berkeley Packet Filter), англ. Фільтр пакетів Берклі — технологія операційної системи, що надає інтерфейс для роботи з чистими мережевими кадрами на канальному рівні.[1] Більшість Unix-подібних операційних операційних систем мають підтримку BPF інтерфейсу.
BPF дозволяє процесам з простору користувача додавати до будь якого відкритого сокета фільтр та дозволяти або забороняти проходження певних даних через вибраний сокет. Для прикладу, використовуючи BPF фільтр, tcpdump може отримувати тільки TCP SYNC пакети. Для цього генерується відповідний BPF фільтр, що пропускає тільки потрібний користувачу трафік. Застосування такого фільтру покращує загальну продуктивність системи, оскільки ядро операційної системи не копіює небажаний трафік до буфера у просторі користувача.
Іноді під абревіатурою BPF мають на увазі тільки механізм фільтрації, а не весь інтерфейс BPF. Деякі системи, такі як Linux та Tru64 UNIX, надають інший, відмінний від BPF інтерфейс доступу до "сирих" мережевих кадрів на канальному рівні, залишаючи механізм фільтрації BPF для свого "сирого" інтерфейсу.
Також, BPF надає псевдо-пристрої (BPF-пристрої), які можуть бути прив'язані до мережевих інтерфейсів; читання з пристрою буде зчитувати буфер отриманих мережевим інтерфейсом пакетів, запис даних у пристрій буде випускати пакети з мережевого інтерфейсу.
В 2007, Роберт Уотсон та Крістіан Перон додали розширення з zero-copy buffer до імплементації BPF у FreeBSD[2], дозволяючи драйверу мережевої карти записувати дані прямо в пам'ять користувацького процесу, для уникнення зайвих копіювань даних. Таким чином, замість двох копіювань (драйвер -> буфер ядра -> пам'ять процесу), відбувається одне копіювання в спільну пам'ять BPF програм у просторі користувача. Дана імплементація зберігає незалежність різних BPF-пристроїв один від одного при використанні спільної пам'яті.[3]
Функціонал фільтрації BPF імплементований як інтерпретатор для машинної мови, що виконується віртуальною машиною BPF (розрядність 32-біт, фіксована довжина інструкцій, один акумулятор та один індексний регістр). Програми, написані цією мовою, можуть доступатись до даних пакету, виконувати арифметичні та логічні операції над даними пакета, виконувати порівняння константних значень, результатів обчислень, даних в пакеті, а також приймати рішення щодо пропуску або відкидання пакету.
Функціонал BPF часто розширюється шляхом "перевантаження" інструкцій "load" (ld) та "store" (st).
Традиційні Unix-подібні імплементації BPF можуть використовуватись програмами в просторі користувача. Для цього необхідно використати певні прапорців препроцесора, оскільки в оригіналі BPF був написаний для роботи в просторі ядра.
Не всі імплементації BPF однакові з функціональної точки зору. Деякі проекти мають відмінний від оригінальної імплементації набір інструкцій або техніки виконання коду.
Деякі платформи, такі як FreeBSD, NetBSD та WinPcap, використовують JIT компілятори для перетворення BPF інструкцій у машинний код заради збільшення швидкодії. Імплементація в Linux підтримує режим робити з JIT-компілятором, який виключений за замовчуванням.
Інтерпретатори в ядрі ОС можуть використовуватись для різних цілей. В ядрі Tru64 Unix присутній на канальному рівні, та для фільтрації на рівні сокетів у ядрі Linux, WinPcap та Npcap.
З версії ядра 3.18, в Linux з'являється розширена версія віртуальної машини BPF з 10-ю 64-бітних регістрів, названий extended BPF (eBPF, англ. розширений фільтр пакетів Берклі). Він може бути використаний виконання не мережевих задач, наприклад для обробки інформації у точках трасування в ядрі ОС.[4][5][6] У версії ядра 3.19 Linux eBPF фільтри можуть бути закріплені на сокетах,[7][8] а з версії 4.1 - до класифікаторів керування трафіку вхідних та вихідних шляхів даних.[9][10] З поширенням eBPF стало прийнято називати оригінальну версію classic BPF (cBPF, англ. класичний фільтр пакетів Берклі). Поточні версії ядра Linux працюють тільки з eBPF-байт кодом, в той час як cBPF-байт код транслюється у eBPF безпосередньо перед виконанням.[11] Весь байт код, перед завантаженням в ядро проходить перевірку на відповідність певних безпекових параметрів. З версії ядра Linux 5.3, верифікатор дозволяє використання циклів.[12]
Наявна імплементація pcap API (з бібліотек libpcap/WinPcap/Npcap) інтерпретатора BPF для простору користувача, що дозволяє використовувати фільтрувальний функціонал BPF на ОС, що не мають такої підтримки у ядрі ОС. Код, що використовує pcap API, буде працювати на обох системах, проте у випадку відсутності підтримки фільтру на рівні ОС, всі пакети будуть копіюватись з простору ядра до буфера програми у простір користувача. Цей інтерпретатор також може бути використаний для фільтрації пакетів з файлу.
Інтерпретатор uBPF працює у просторі користувача, підтримує JIT-компіляцію та розширення eBPF.[13] uBPF ліг в основу інтерпретатора проєкта generic-eBPF, що використовується у не-Linux системах.[14]
Класичний BPF код зазвичай генерується програмою з високорівневого текстового правила, що описує шаблон роботи фільтру. Прикладом такого використання є libpcap.[15] Класичний BPF та eBPF код також може бути згенерований одразу у вигляді машинного коду, або з текстового опису мовою асемблера. Прикладами таких асемблерів в ядрі Linux є bpf_asm
(cBPF), bpfc
(cBPF), а також ubpf
ассемблер (eBPF). Команда bpftool
має вбудований функціонал дизассемблера для cBPF та eBPF. Сумісність різних мов асемблера між собою не гарантується.
eBPF-байткод отримує підтримку все більшої кількості високорівневих мов програмування. LLVM отримав підтримку eBPF у 2014, GCC - у 2019. Обидва інструменти дозволяють компіляцію C - коду та інших підтримуваних мов програмування у eBPF-байткод. Підмножина мови програмування P4[en] також може бути скомпільована у eBPF-байткод за допомогою BCC, та LLVM.[16]
Перша стаття, що описує BPF, була написана робітниками Національної лабораторії ім. Лоуренса в Берклі, Стівеном МакКейном та Ван Якобсоном, у 1992 році.[1][17]
В Серпні 2003 року, SCO Group[en] зробила публічну заяву, що у ядрі Linux присутній код Unix, правами на який вони, SCO Group, володіють.[18] Програмісти швидко дізнались, що у якості прикладу, було наведено BPF, правами на який SCO ніколи не володіла.[19] SCO не давала пояснень та не визнає своєї помилки, проте робота над порушеними юридичними справами може дати відповідь на це питання.[20]
Атаки типу Spectre можуть використовувати eBPF JIT-компілятор для отримання доступу до даних процесів ядра, що може привести до читання захищених даних процесом з простору користувача.[21]
- ↑ а б McCanne, Steven; Jacobson, Van (19 грудня 1992). The BSD Packet Filter: A New Architecture for User-level Packet Capture (PDF). Архів оригіналу (PDF) за 19 квітня 2009. Процитовано 3 жовтня 2020.
- ↑ bpf(4) Berkeley Packet Filter. FreeBSD. 15 червня 2010. Архів оригіналу за 21 жовтня 2020. Процитовано 3 жовтня 2020.
- ↑ Watson, Robert N. M.; Peron, Christian S. J. (9 березня 2007). Zero-Copy BPF (PDF). Архів оригіналу (PDF) за 16 травня 2008. Процитовано 3 жовтня 2020.
- ↑ Linux kernel 3.18, Section 1.3. bpf() syscall for eBFP virtual machine programs. kernelnewbies.org. 7 грудня 2014. Архів оригіналу за 25 жовтня 2019. Процитовано 6 вересня 2019.
- ↑ Jonathan Corbet (24 вересня 2014). The BPF system call API, version 14. LWN.net. Архів оригіналу за 27 грудня 2014. Процитовано 19 січня 2015.
- ↑ Jonathan Corbet (2 липня 2014). Extending extended BPF. LWN.net. Архів оригіналу за 24 квітня 2019. Процитовано 19 січня 2015.
- ↑ Linux kernel 3.19, Section 11. Networking. kernelnewbies.org. 8 лютого 2015. Архів оригіналу за 12 лютого 2015. Процитовано 13 лютого 2015.
- ↑ Jonathan Corbet (10 грудня 2014). Attaching eBPF programs to sockets. LWN.net. Архів оригіналу за 14 лютого 2015. Процитовано 13 лютого 2015.
- ↑ Linux kernel 4.1, Section 11. Networking. kernelnewbies.org. 21 червня 2015. Архів оригіналу за 16 жовтня 2015. Процитовано 17 жовтня 2015.
- ↑ BPF and XDP Reference Guide. cilium.readthedocs.io. 24 квітня 2017. Архів оригіналу за 24 квітня 2018. Процитовано 23 квітня 2018.
- ↑ BPF and XDP Reference Guide — Cilium 1.6.5 documentation. docs.cilium.io. Архів оригіналу за 18 вересня 2020. Процитовано 18 грудня 2019.
- ↑ Bounded loops in BPF for the 5.3 kernel [LWN.net]. lwn.net. Архів оригіналу за 20 вересня 2020. Процитовано 3 жовтня 2020.
- ↑ iovisor/ubpf, IO Visor Project, 1 жовтня 2020, архів оригіналу за 9 листопада 2020, процитовано 3 жовтня 2020
- ↑ generic-ebpf/generic-ebpf. GitHub (англ.). Архів оригіналу за 27 вересня 2020. Процитовано 3 жовтня 2020.
- ↑ BPF syntax. biot.com. Архів оригіналу за 14 серпня 2020. Процитовано 3 жовтня 2020.
- ↑ Dive into BPF: a list of reading material. qmonnet.github.io. Архів оригіналу за 2 жовтня 2019. Процитовано 3 жовтня 2020.
- ↑ McCanne, Steven; Jacobson, Van (January 1993). The BSD Packet Filter: A New Architecture for User-level Packet Capture. USENIX. Архів оригіналу за 3 серпня 2020. Процитовано 3 жовтня 2020.
- ↑ SCOsource update. 15 Obfuscated Copying. Архів оригіналу за 25 серпня 2003. Процитовано 5 вересня 2019.
- ↑ Bruce Perens. Analysis of SCO's Las Vegas Slide Show. Архів оригіналу за 17 лютого 2009.
- ↑ Moglen, Eben (24 листопада 2003). SCO: Without Fear and Without Research. GNU Operating System. The Free Software Foundation. Архів оригіналу за 5 вересня 2019. Процитовано 5 вересня 2019.
- ↑ Reading privileged memory with a side-channel. Project Zero team at Google. 3 січня 2018. Архів оригіналу за 1 жовтня 2019. Процитовано 20 січня 2018.
- bpfc, Berkeley Packet Filter компілятор, Linux BPF JIT дизасемблер [Архівовано 8 вересня 2016 у Wayback Machine.]
- McCanne, Steven; Jacobson, Van (19 грудня 1992). The BSD Packet Filter: A New Architecture for User-level Packet Capture (PDF). Архів оригіналу (PDF) за 19 квітня 2009. Процитовано 3 жовтня 2020.
- man про спеціальні файли GNU/Linux (англ.) - FreeBSD – приклади використання BPF – сторінка довідки
- BPF Documdntation [Архівовано 6 жовтня 2020 у Wayback Machine.], документація ядра Linux
- Linux filter documentation [Архівовано 5 грудня 2020 у Wayback Machine.], формат байткоду для обох варіантів, cBPF та eBPF