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 також може бути скомпільована у eBPF-байткод за допомогою BCC, та LLVM.[16]

ІсторіяРедагувати

Перша стаття, що описує BPF, була написана робітниками Lawrence Berkeley Laboratory, Стівеном МакКейном та Ван Якобсоном, у 1992 році.[1][17]

В Серпні 2003 року, SCO Group зробила публічну заяву, що у ядрі Linux присутній код Unix, правами на який вони, SCO Group, володіють.[18] Програмісти швидко дізнались, що у якості прикладу, було наведено BPF, правами на який SCO ніколи не володіла.[19] SCO не давала пояснень та не визнає своєї помилки, проте робота над порушеними юридичними справами може дати відповідь на це питання.[20]

Проблеми безпекиРедагувати

Атаки типу Spectre можуть використовувати eBPF JIT-компілятор для отримання доступу до даних процесів ядра, що може привести до читання захищених даних процесом з простору користувача.[21]

Див. такожРедагувати

ПосиланняРедагувати

  1. а б McCanne, Steven; Jacobson, Van (1992-12-19). The BSD Packet Filter: A New Architecture for User-level Packet Capture. 
  2. bpf(4) Berkeley Packet Filter. FreeBSD. 2010-06-15. 
  3. Watson, Robert N. M.; Peron, Christian S. J. (2007-03-09). Zero-Copy BPF. 
  4. Linux kernel 3.18, Section 1.3. bpf() syscall for eBFP virtual machine programs. kernelnewbies.org. December 7, 2014. Процитовано September 6, 2019. 
  5. Jonathan Corbet (September 24, 2014). The BPF system call API, version 14. LWN.net. Процитовано January 19, 2015. 
  6. Jonathan Corbet (July 2, 2014). Extending extended BPF. LWN.net. Процитовано January 19, 2015. 
  7. Linux kernel 3.19, Section 11. Networking. kernelnewbies.org. February 8, 2015. Процитовано February 13, 2015. 
  8. Jonathan Corbet (December 10, 2014). Attaching eBPF programs to sockets. LWN.net. Процитовано February 13, 2015. 
  9. Linux kernel 4.1, Section 11. Networking. kernelnewbies.org. June 21, 2015. Процитовано October 17, 2015. 
  10. BPF and XDP Reference Guide. cilium.readthedocs.io. April 24, 2017. Процитовано April 23, 2018. 
  11. BPF and XDP Reference Guide — Cilium 1.6.5 documentation. docs.cilium.io. Процитовано 2019-12-18. 
  12. Bounded loops in BPF for the 5.3 kernel [LWN.net]. lwn.net. Процитовано 2020-10-03. 
  13. iovisor/ubpf. IO Visor Project. 2020-10-01. Процитовано 2020-10-03. 
  14. generic-ebpf/generic-ebpf. GitHub (en). 
  15. BPF syntax. biot.com. 
  16. Dive into BPF: a list of reading material. qmonnet.github.io. 
  17. McCanne, Steven; Jacobson, Van (January 1993). The BSD Packet Filter: A New Architecture for User-level Packet Capture. USENIX. 
  18. SCOsource update. 15 Obfuscated Copying. Архів оригіналу за August 25, 2003. Процитовано September 5, 2019. 
  19. Bruce Perens. Analysis of SCO's Las Vegas Slide Show. Архів оригіналу за February 17, 2009. 
  20. Moglen, Eben (November 24, 2003). SCO: Without Fear and Without Research. GNU Operating System. The Free Software Foundation. Процитовано September 5, 2019. 
  21. Reading privileged memory with a side-channel. Project Zero team at Google. January 3, 2018. Процитовано January 20, 2018. 

Зовнішні посиланняРедагувати