Він використовується для безпечного та ефективного розширення можливостей ядра під час виконання без необхідності вносити зміни до початкового коду ядра або завантажувати модулі ядра.[5] Безпека забезпечується через внутрішньоядерний верифікатор, який виконує статичний аналіз коду та відхиляє програми, які викликають збої, зависання або іншим чином негативно впливають на ядро.[6][7]
Ця модель валідації відрізняється від середовищ з пісочницею, де середовище виконання обмежене і не має уявлення про програму.[8] Прикладом програм, які автоматично відхиляються, є програми без надійних умов завершення (наприклад, цикли for/while без умов завершення) та програми, які здійснюють розіменування вказівників без перевірок на безпеку.[9]
Дизайн
Завантажені програми, які пройшли перевірку, або інтерпретуються, або компілюються в ядрі за допомогою компіляції під час виконання (JIT) для досягнення високої продуктивності. Модель виконання є подійно-орієнтованою та, за рідкими винятками, працює за принципом виконання до завершення,[2] що означає, що програми можуть бути прикріплені до різних точок перехоплення в ядрі операційної системи та виконуються під час спрацювання події. Випадки використання eBPF включають (але не обмежуються) мережеві підсистеми, такі як XDP, трасування та підсистеми безпеки.[4] Завдяки ефективності та гнучкості eBPF відкрилися нові можливості для вирішення виробничих проблем, і Брендан Ґреґґ знаменито назвав eBPF "суперсилою для Linux".[10]Лінус Торвальдс зазначив: "BPF насправді був дуже корисним, і його реальна сила полягає в тому, як він дозволяє створювати спеціалізований код, який активується лише тоді, коли це потрібно".[11] Завдяки успіху в Linux, середовище виконання eBPF було портовано на інші операційні системи, такі як Windows.[3]
Історія
eBPF еволюціонував із класичного Berkeley Packet Filter (cBPF, ретроактивно надана назва). На базовому рівні він запровадив використання десяти 64-бітних регістрів (замість двох 32-бітних регістрів у cBPF), інші семантики переходів, інструкцію виклику та відповідну конвенцію передачі регістрів, нові інструкції та інше кодування для цих інструкцій.[12]
Найважливіші етапи еволюції eBPF
Дата
Подія
Квітень 2011
Перший JIT-компілятор (Just-in-time compiler) для класичного фільтра пакетів Берклі (BPF) був інтегрований в ядро Linux.[13]
Січень 2012
Перше некласичне використання класичного фільтра пакетів Берклі — seccomp-bpf.[14]
Березень 2014
Девід С. Міллер, головний адміністратор мережевої підсистеми Linux, прийняв переробку старого інтерпретатора BPF в ядрі. Його замінив інтерпретатор eBPF, а ядро Linux тепер перекладає класичний BPF (cBPF) у команди eBPF.[15]
Вересень 2014
Введено системний виклик bpf, який забезпечив доступ до набору інструкцій eBPF з користувацького простору.[16]
Березень 2015
Можливість прикріплювати eBPF до kprobe була інтегрована як перший випадок використання для трасування.[17]
Серпень 2015
Модуль компілятора eBPF був інтегрований у версію LLVM 3.7.0.[18]
Вересень 2015
Брендан Грегг оголосив про новий набір інструментів для трасування на основі eBPF, відомий як проект bcc.[19]
Липень 2016
eBPF отримав можливість бути прикріпленим до ядра драйвера для обробки вхідних пакетів. Цей рівень відомий як eXpress DataPath XDP.[20]
Серпень 2016
Cilium був представлений як проект для швидкої мережевої комунікації IPv6 для контейнерів за допомогою eBPF і XDP.[21]
Листопад 2016
Netronome додав можливість оффлоаду програм eBPF для XDP та BPF.[22]
Травень 2017
Впроваджено балансувальник навантаження Katran від Meta, який використовує eBPF та XDP.[23]
Листопад 2017
eBPF став окремим підсистемою ядра для спрощення управління патчами ядра.[24]
Січень 2018
Випущена нова родина сокетів AF_XDP для високошвидкісної обробки пакетів.[25]
Грудень 2019
Випущено першу книгу на 880 сторінок про BPF, написану Бренданом Греггом.[26]
eBPF мапи — це ефективні сховища ключ/значення, які знаходяться в просторі ядра та можуть використовуватися для обміну даними між декількома програмами eBPF або для комунікації між користувацьким простором та кодом eBPF, що працює в ядрі. Програми eBPF можуть використовувати мапи eBPF для зберігання та отримання даних у широкому спектрі структур даних. Реалізації мап надаються ядром. Існують різні типи,[27] включаючи хеш-мапи, масиви та кільцеві буфери.
На практиці eBPF мапи зазвичай використовуються у таких сценаріях, як запис конфігураційної інформації програмою користувацького простору для отримання програмою eBPF, збереження стану програмою eBPF для подальшого отримання іншою програмою eBPF (або під час повторного запуску тієї ж програми), або запис результатів чи метрик програмою eBPF у мапу для отримання програмою користувацького простору, яка представить результати.[28]
Віртуальна машина eBPF
Віртуальна машина eBPF працює в межах ядра та приймає програму у вигляді байткоду eBPF, який конвертується у нативні машинні інструкції, що виконуються на процесорі. Ранні реалізації eBPF виконували інтерпретацію байткоду, проте тепер цей процес замінено на компіляцію Just-in-Time (JIT) для підвищення продуктивності та безпеки.[28]
Віртуальна машина eBPF складається з одинадцяти 64-бітних регістрів з 32-бітними підрегістрами, лічильника команд та 512-байтного стека BPF. Ці регістри загального призначення зберігають стан під час виконання програм eBPF.[29]
Tail calls (хвостові виклики)
Хвостовий виклик дозволяє викликати та виконувати іншу програму eBPF, замінюючи контекст виконання, подібно до того, як системний виклик execve() працює для звичайних процесів. Це дозволяє програмі eBPF викликати іншу програму eBPF. Хвостові виклики реалізуються як довгий стрибок, використовуючи ту саму фрейм стека. Хвостові виклики особливо корисні в eBPF, де стек обмежений 512 байтами. Під час виконання функціональність може бути додана або замінена атомарно, змінюючи поведінку виконання програми BPF.[29] Популярним випадком використання хвостових викликів є розподіл складності програм eBPF на кілька програм. Інший варіант використання — заміна або розширення логіки шляхом заміни вмісту масиву програми під час її використання. Наприклад, для оновлення версії програми без перерви в роботі або для ввімкнення/вимкнення логіки.[30]
Виклики BPF до BPF
Загальноприйнятою практикою в розробці програмного забезпечення є групування загального коду в функції, що інкапсулюють логіку для повторного використання. До версії ядра Linux 4.16 та LLVM 6.0 типова програма eBPF на мові C повинна була явно вказувати компілятору вбудовувати функцію, що призводило до створення об'єктного файлу BPF з дубльованими функціями. Це обмеження було знято, і сучасні компілятори eBPF тепер підтримують написання функцій у програмах eBPF природним чином. Це зменшує розмір згенерованого коду eBPF, роблячи його більш дружнім до кешу інструкцій процесора.[28][29]
Верифікатор eBPF
Верифікатор є основним компонентом eBPF, і його головне завдання — гарантувати, що програма eBPF є безпечною для виконання. Він проводить статичний аналіз байткоду eBPF для забезпечення його безпеки. Верифікатор аналізує програму, оцінюючи всі можливі шляхи виконання. Він крокує по інструкціях у порядку їх виконання і оцінює їх. Процес верифікації починається з пошуку в глибину по всіх можливих шляхах програми, при цьому верифікатор симулює виконання кожної інструкції, відстежуючи стан регістрів і стека. Якщо якась інструкція може призвести до небезпечного стану, верифікація не проходить. Процес триває доти, поки всі шляхи не будуть проаналізовані або не буде знайдено порушення. Залежно від типу програми, верифікатор перевіряє порушення певних правил. До цих правил може входити перевірка того, що програма eBPF завжди завершується за розумний час (без нескінченних циклів або нескінченної рекурсії), перевірка того, що програмі eBPF не дозволяється читати довільну пам'ять, оскільки це може дозволити програмі витікати конфіденційну інформацію, перевірка того, що програми, пов'язані з мережею, не мають доступу до пам'яті поза межами пакета, оскільки сусідня пам'ять може містити конфіденційну інформацію, перевірка того, що програми не можуть створювати взаємне блокування, тому всі Spinlock повинні бути звільнені, і одночасно можна утримувати лише один замок, щоб уникнути взаємного блокування між кількома програмами, перевірка того, що програми не мають доступу до неініціалізованої пам'яті. Це не є вичерпним списком перевірок верифікатора, і є винятки з цих правил. Наприклад, програми трасування мають доступ до хелперів, які дозволяють їм читати пам'ять контрольованим чином, але такі типи програм вимагають привілеїв суперкористувача і тому не становлять загрози для безпеки.[30][28]
З часом верифікатор eBPF розвинувся, включивши нові функції та оптимізації, такі як підтримка обмежених циклів, Елімінація мертвого коду, верифікація по функціям і зворотні виклики.
eBPF CO-RE (Compile Once - Run Everywhere)
Програми eBPF використовують пам'ять і структури даних ядра. Деякі структури можуть змінюватися між різними версіями ядра, змінюючи розташування пам'яті. Оскільки ядро Linux постійно розвивається, немає гарантії, що внутрішні структури даних залишаться незмінними в різних версіях. CO-RE є основним поняттям у сучасній розробці eBPF, яке дозволяє програмам eBPF бути портативними між різними версіями та конфігураціями ядра. Це вирішує проблему варіацій у структурах ядра між різними дистрибутивами Linux і версіями. CO-RE складається з BTF (BPF Type Format) — формату метаданих, який описує типи, що використовуються в ядрі та програмах eBPF, і надає детальну інформацію про структури, зсуви полів і типи даних. Це забезпечує доступ до типів ядра під час виконання, що є ключовим для розробки та верифікації програм BPF. BTF включений в образ ядра для підтримуваних версій. Спеціальні релокації генеруються компілятором (наприклад, LLVM). Ці релокації захоплюють опис високого рівня того, до якої інформації програма eBPF має намір звертатися. Бібліотека libbpf адаптує програми eBPF для роботи з макетом структури даних на цільовому ядрі, де вони виконуються, навіть якщо цей макет відрізняється від того, на якому код було скомпільовано. Для цього libbpf потребує інформацію про релокації BPF CO-RE, згенеровану Clang під час процесу компіляції.[28] Скомпільована програма eBPF зберігається у форматі ELF (Executable and Linkable Format). Цей файл містить інформацію про типи BTF та релокації, згенеровані Clang. Формат ELF дозволяє завантажувачу eBPF (наприклад, libbpf) обробляти та налаштовувати програму BPF динамічно для цільового ядра.[31]
Брендинг
Назва eBPF часто використовується взаємозамінно з BPF,[2][32] наприклад, у спільноті ядра Linux. eBPF і BPF згадуються як технологія, подібно до LLVM.[2] eBPF розвинувся з машинної мови для фільтруючої віртуальної машини в Фільтр Berkeley Packet як розширена версія, але оскільки його сфери використання вийшли за межі мереж, сьогодні "eBPF" найчастіше тлумачиться як псевдоакронім.[2]
Бджола є офіційним логотипом eBPF. Під час першого саміту eBPF було проведено голосування, на якому маскот-бджолу назвали "eBee".[33][34] Логотип спочатку був створений Вадимом Щеколдіним.[34] Раніше існували неофіційні маскоти eBPF,[35] але вони не отримали широкого поширення.
Управління
Фонд eBPF був створений у серпні 2021 року з метою розширення внесків, спрямованих на розширення потужних можливостей eBPF та виходу за межі Linux.[1] Засновниками виступили Meta, Google, Isovalent, Microsoft та Netflix. Метою є збір, бюджетування та витрачання коштів на підтримку різних проєктів з відкритим вихідним кодом, відкритими даними та/або відкритими стандартами, пов'язаних із технологіями eBPF,[36] для подальшого розвитку та впровадження екосистеми eBPF. З моменту заснування до них приєдналися Red Hat, Huawei, CrowdStrike, Tigera, DaoCloud, Datoms, FutureWei.[37]
Впровадження
eBPF був прийнятий рядом великих користувачів на виробництві, наприклад:
Meta використовує eBPF через свій балансувальник навантаження 4-го рівня Katran для всього трафіку, що йде на facebook.com.[38][39][40][23]
Google використовує eBPF у GKE, розробив та використовує BPF LSM для заміни аудиту та використовує eBPF для роботи в мережі.[41][42][43][44]
Cloudflare використовує eBPF для балансування навантаження, захисту від DDoS та забезпечення безпеки.[45][46][47][48][49]
Netflix використовує eBPF для моніторингу мережі в масштабах усієї інфраструктури та діагностики продуктивності.[50][51]
Dropbox використовує eBPF через Katran для балансування навантаження на 4-му рівні.[52]
Red Hat використовує eBPF в масштабах для балансування навантаження та трасування в їх приватному хмарі.
Palantir Technologies використовує eBPF для налагодження мережевих проблем у великих кластерах Kubernetes.[82]
Безпека
Через легкість програмування, eBPF використовувався як інструмент для реалізації мікроархітектурних атак через бічний канал, таких як Spectre, проти вразливих мікропроцесорів.[83] Хоча eBPF без привілеїв реалізовував міграційні заходи проти атак тимчасового виконання,[84] використання без привілеїв зрештою було відключене спільнотою ядра за замовчуванням для захисту від використання проти майбутніх апаратних вразливостей.[85]
Rice, Liz (Квітень 2023). Learning eBPF: Programming the Linux Kernel for Enhanced Observability, Networking, and Security. O'Reilly Media. ISBN978-1098135126.