Kichkintoylar uchun BPF, nol qism: klassik BPF

Berkeley Packet Filters (BPF) — Linux yadro texnologiyasi boʻlib, u bir necha yillardan buyon ingliz tilidagi texnologik nashrlarning birinchi sahifalarida joylashgan. Konferentsiyalar BPF dan foydalanish va rivojlantirish bo'yicha hisobotlar bilan to'ldiriladi. Devid Miller, Linux tarmog'i quyi tizimini ta'minlovchisi, Linux Plumbers 2018 ko'rgazmasida nutq so'zlaydi "Bu gap XDP haqida emas" (XDP BPF uchun bitta foydalanish holati). Brendan Gregg ma'ruza qiladi Linux BPF super kuchlari. Toke Xoyland-Yorgensen kuladimiyadro endi mikroyadro ekanligini. Tomas Graf bu fikrni ilgari suradi BPF yadro uchun JavaScript hisoblanadi.

Habré-da BPF ning tizimli tavsifi hali ham mavjud emas va shuning uchun men bir qator maqolalarda texnologiyaning tarixi haqida gapirishga, arxitektura va rivojlanish vositalarini tavsiflashga, shuningdek, BPF-dan foydalanish sohalari va amaliyotini tavsiflashga harakat qilaman. Ushbu maqola, nol, seriyali, klassik BPF tarixi va arxitekturasini aytib beradi, shuningdek, uning ishlash tamoyillari sirlarini ochib beradi. tcpdump, seccomp, strace, va boshqalar.

BPF ning rivojlanishi Linux tarmoq hamjamiyati tomonidan nazorat qilinadi, BPF ning asosiy mavjud ilovalari tarmoqlar bilan bog'liq va shuning uchun ruxsat bilan @eucariot, Men seriyani "Kichiklar uchun BPF" deb nomladim, buyuk seriyalar sharafiga "Kichiklar uchun tarmoqlar".

BPF tarixi bo'yicha qisqa kurs (c)

Zamonaviy BPF texnologiyasi eski texnologiyaning bir xil nomdagi takomillashtirilgan va kengaytirilgan versiyasidir, endi chalkashmaslik uchun klassik BPF deb ataladi. Klassik BPF asosida taniqli yordamchi dastur yaratilgan tcpdump, mexanizm seccomp, shuningdek, kam ma'lum bo'lgan modullar xt_bpf uchun iptables va tasniflagich cls_bpf. Zamonaviy Linux-da klassik BPF dasturlari avtomatik ravishda yangi shaklga tarjima qilinadi, ammo foydalanuvchi nuqtai nazaridan, API o'z joyida qoldi va klassik BPF uchun yangi foydalanishni ushbu maqolada ko'rib chiqamiz. Shu sababli, shuningdek, Linuxda klassik BPF ning rivojlanish tarixidan so'ng, uning qanday va nima uchun zamonaviy shaklga aylangani aniqroq bo'lishi sababli, men klassik BPF haqidagi maqoladan boshlashga qaror qildim.

O'tgan asrning saksoninchi yillarining oxirida mashhur Lourens Berkli laboratoriyasining muhandislari o'tgan asrning saksoninchi yillarining oxirida zamonaviy bo'lgan uskunada tarmoq paketlarini qanday qilib to'g'ri filtrlash masalasi bilan qiziqdilar. Dastlab CSPF (CMU/Stanford Packet Filter) texnologiyasida amalga oshirilgan filtrlashning asosiy g'oyasi keraksiz paketlarni imkon qadar tezroq filtrlash edi, ya'ni. yadro maydonida, chunki bu foydalanuvchi maydoniga keraksiz ma'lumotlarni nusxalashdan qochadi. Yadro makonida foydalanuvchi kodini ishlatish uchun ish vaqti xavfsizligini ta'minlash uchun sinov muhitiga o'rnatilgan virtual mashina ishlatilgan.

Biroq, mavjud filtrlar uchun virtual mashinalar stekga asoslangan mashinalarda ishlash uchun mo'ljallangan va yangi RISC mashinalarida unchalik samarali ishlamagan. Natijada, Berkeley Labs muhandislarining sa'y-harakatlari bilan yangi BPF (Berkeley Packet Filters) texnologiyasi ishlab chiqildi, uning virtual mashina arxitekturasi Motorola 6502 protsessori - kabi taniqli mahsulotlarning ishchi kuchi asosida ishlab chiqilgan. Apple II yoki NES. Yangi virtual mashina filtr ish faoliyatini mavjud echimlarga nisbatan o'nlab marta oshirdi.

BPF mashinasi arxitekturasi

Biz arxitektura bilan ish uslubida, misollarni tahlil qilib tanishamiz. Biroq, avvalo, aytaylik, mashinada foydalanuvchi foydalanishi mumkin bo'lgan ikkita 32 bitli registr, akkumulyator mavjud edi. A va indeks registri X, 64 bayt xotira (16 so'z), yozish va keyingi o'qish uchun mavjud va bu ob'ektlar bilan ishlash uchun kichik buyruqlar tizimi. Shartli iboralarni amalga oshirish bo'yicha ko'rsatmalar dasturlarda ham mavjud edi, ammo dasturni o'z vaqtida bajarilishini ta'minlash uchun sakrashlar faqat oldinga amalga oshirilishi mumkin edi, ya'ni, xususan, ko'chadan yaratish taqiqlangan.

Mashinani ishga tushirishning umumiy sxemasi quyidagicha. Foydalanuvchi BPF arxitekturasi uchun dastur yaratadi va, yordamida biroz yadro mexanizmi (masalan, tizim chaqiruvi), dasturni yuklaydi va ulaydi ba'zilariga yadrodagi hodisa generatoriga (masalan, hodisa tarmoq kartasiga keyingi paketning kelishi). Voqea sodir bo'lganda, yadro dasturni ishga tushiradi (masalan, tarjimonda) va mashina xotirasi mos keladi. ba'zilariga yadro xotirasi hududi (masalan, kiruvchi paket ma'lumotlari).

Yuqoridagilar misollarni ko'rib chiqishni boshlashimiz uchun etarli bo'ladi: kerak bo'lganda tizim va buyruq formati bilan tanishamiz. Agar siz virtual mashinaning buyruqlar tizimini darhol o'rganmoqchi bo'lsangiz va uning barcha imkoniyatlarini o'rganmoqchi bo'lsangiz, unda asl maqolani o'qishingiz mumkin. BSD paket filtri va/yoki faylning birinchi yarmi Documentation/networking/filter.txt yadro hujjatlaridan. Bundan tashqari, siz taqdimotni o'rganishingiz mumkin libpcap: Packet Capture uchun arxitektura va optimallashtirish metodologiyasi, unda BPF mualliflaridan biri MakKen yaratilish tarixi haqida gapiradi libpcap.

Biz Linuxda klassik BPF dan foydalanishning barcha muhim misollarini ko'rib chiqishga o'tamiz: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

BPF-ni ishlab chiqish paketlarni filtrlash uchun frontend - taniqli yordamchi dasturni ishlab chiqish bilan parallel ravishda amalga oshirildi. tcpdump. Va bu ko'plab operatsion tizimlarda mavjud bo'lgan klassik BPF-dan foydalanishning eng qadimgi va eng mashhur namunasi bo'lgani uchun biz texnologiyani o'rganishni u bilan boshlaymiz.

(Men ushbu maqoladagi barcha misollarni Linuxda ishlatdim 5.6.0-rc6. Ba'zi buyruqlarning chiqishi yaxshiroq o'qilishi uchun tahrirlangan.)

Misol: IPv6 paketlarini kuzatish

Tasavvur qilaylik, biz interfeysdagi barcha IPv6 paketlarini ko'rib chiqmoqchimiz eth0. Buning uchun dasturni ishga tushirishimiz mumkin tcpdump oddiy filtr bilan ip6:

$ sudo tcpdump -i eth0 ip6

Shunday qilib tcpdump filtrni tuzadi ip6 BPF arxitektura bayt-kodiga kiriting va uni yadroga yuboring (batafsilroq bo'limga qarang Tcpdump: yuklanmoqda). Yuklangan filtr interfeys orqali o'tadigan har bir paket uchun ishga tushiriladi eth0. Agar filtr nolga teng bo'lmagan qiymatni qaytarsa n, keyin gacha n paketning baytlari foydalanuvchi maydoniga ko'chiriladi va biz uni chiqishda ko'ramiz tcpdump.

Kichkintoylar uchun BPF, nol qism: klassik BPF

Ma'lum bo'lishicha, biz yadroga qaysi bayt-kod yuborilganligini osongina bilib olamiz tcpdump yordamida tcpdump, agar biz uni variant bilan ishga tushirsak -d:

$ sudo tcpdump -i eth0 -d ip6
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 3
(002) ret      #262144
(003) ret      #0

Nolinchi qatorda biz buyruqni bajaramiz ldh [12], bu “registrga yuklash A 16" manzilda joylashgan yarim so'z (12 bit) va yagona savol - biz qanday xotiraga murojaat qilyapmiz? Javob shu x boshlanadi (x+1)tahlil qilingan tarmoq paketining th bayti. Biz Ethernet interfeysidan paketlarni o'qiymiz eth0, bu esa deganipaketning ko'rinishi (oddiylik uchun paketda VLAN teglari yo'q deb taxmin qilamiz):

       6              6          2
|Destination MAC|Source MAC|Ether Type|...|

Shunday qilib, buyruqni bajargandan so'ng ldh [12] registrda A maydon bo'ladi Ether Type — ushbu Ethernet freymida uzatiladigan paket turi. 1-qatorda biz registrning mazmunini solishtiramiz A (paket turi) c 0x86dd, bu esa va u erda Bizni qiziqtirgan tur IPv6. 1-qatorda taqqoslash buyrug'iga qo'shimcha ravishda yana ikkita ustun mavjud - jt 2 и jf 3 — solishtirish muvaffaqiyatli boʻlsa, borish kerak boʻlgan belgilar (A == 0x86dd) va muvaffaqiyatsiz. Shunday qilib, muvaffaqiyatli holatda (IPv6) biz 2-qatorga, muvaffaqiyatsiz holatda - 3-qatorga o'tamiz. 3-qatorda dastur 0 kodi bilan tugaydi (paketdan nusxa ko'chirmang), 2-qatorda dastur kod bilan tugaydi. 262144 (maksimal 256 kilobaytlik paketni menga nusxalash).

Murakkabroq misol: biz TCP paketlarini maqsad port bo'yicha ko'rib chiqamiz

Keling, 666 maqsadli portga ega bo'lgan barcha TCP paketlarini nusxalaydigan filtr qanday ko'rinishini ko'rib chiqaylik. Biz IPv4 holatini ko'rib chiqamiz, chunki IPv6 ishi oddiyroq. Ushbu misolni o'rgangach, siz IPv6 filtrini mashq sifatida o'zingiz o'rganishingiz mumkin (ip6 and tcp dst port 666) va umumiy holat uchun filtr (tcp dst port 666). Shunday qilib, bizni qiziqtiradigan filtr quyidagicha ko'rinadi:

$ sudo tcpdump -i eth0 -d ip and tcp dst port 666
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 10
(002) ldb      [23]
(003) jeq      #0x6             jt 4    jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10   jf 6
(006) ldxb     4*([14]&0xf)
(007) ldh      [x + 16]
(008) jeq      #0x29a           jt 9    jf 10
(009) ret      #262144
(010) ret      #0

0 va 1 qatorlar nima qilishini allaqachon bilamiz. 2-qatorda biz allaqachon bu IPv4 paketi ekanligini tekshirdik (Eter turi = 0x800) va uni registrga yuklang A Paketning 24-bayti. Bizning paketimiz o'xshaydi

       14            8      1     1
|ethernet header|ip fields|ttl|protocol|...|

demak, biz registrga yuklaymiz A IP sarlavhasining Protokol maydoni, bu mantiqiy, chunki biz faqat TCP paketlarini nusxalashni xohlaymiz. Protokolni bilan solishtiramiz 0x6 (IPPROTO_TCP) 3-qatorda.

4 va 5-qatorlarda biz 20-manzilda joylashgan yarim so'zlarni yuklaymiz va buyruqdan foydalanamiz jset uchtadan biri o'rnatilganligini tekshiring bayroqlar - chiqarilgan niqobni kiyish jset uchta eng muhim bit tozalanadi. Uch bitdan ikkitasi paketning bo'laklangan IP-paketning bir qismi ekanligini va agar shunday bo'lsa, u oxirgi fragment ekanligini aytadi. Uchinchi bit ajratilgan va nol bo'lishi kerak. Biz to'liq bo'lmagan yoki buzilgan paketlarni tekshirishni xohlamaymiz, shuning uchun biz uchta bitni tekshiramiz.

6-qator ushbu ro'yxatdagi eng qiziqarli. Ifoda ldxb 4*([14]&0xf) registrga yuklaganimizni bildiradi X paketning o'n beshinchi baytining eng kam ahamiyatli to'rt biti 4 ga ko'paytiriladi. O'n beshinchi baytning eng muhim to'rt biti maydondir. Internet sarlavhasi uzunligi IPv4 sarlavhasi, sarlavha uzunligini so'zlarda saqlaydi, shuning uchun siz keyin 4 ga ko'paytirishingiz kerak. Qizig'i shundaki, ifoda 4*([14]&0xf) faqat ushbu shaklda va faqat registr uchun ishlatilishi mumkin bo'lgan maxsus manzillash sxemasi uchun belgidir X, ya'ni. biz ham ayta olmaymiz ldb 4*([14]&0xf) bizga ldxb 5*([14]&0xf) (biz faqat boshqa ofsetni belgilashimiz mumkin, masalan, ldxb 4*([16]&0xf)). Ko'rinib turibdiki, ushbu manzillash sxemasi BPFga aniq qabul qilish uchun qo'shilgan X (indeks registr) IPv4 sarlavhasi uzunligi.

Shunday qilib, 7-qatorda biz yarim so'zni yuklashga harakat qilamiz (X+16). 14 baytni Ethernet sarlavhasi egallaganligini eslab, va X IPv4 sarlavhasining uzunligini o'z ichiga oladi, biz buni tushunamiz A TCP maqsad porti yuklandi:

       14           X           2             2
|ethernet header|ip header|source port|destination port|

Nihoyat, 8-qatorda biz maqsad portni kerakli qiymat bilan solishtiramiz va 9 yoki 10-qatorlarda natijani qaytaramiz - paketni nusxalash kerakmi yoki yo'qmi.

Tcpdump: yuklanmoqda

Oldingi misollarda biz paketlarni filtrlash uchun yadroga BPF bayt kodini qanday yuklashimiz haqida batafsil to'xtalib o'tmadik. Umuman aytganda, tcpdump ko'plab tizimlarga va filtrlar bilan ishlash uchun ko'chiriladi tcpdump kutubxonadan foydalanadi libpcap. Qisqacha aytganda, filtrni interfeysga joylashtirish uchun libpcap, quyidagilarni qilishingiz kerak:

Funktsiya qanday ishlashini ko'rish uchun pcap_setfilter Linuxda amalga oshirilgan, biz foydalanamiz strace (ba'zi qatorlar olib tashlandi):

$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

Chiqishning dastlabki ikki qatorida biz yaratamiz xom rozetka barcha Ethernet freymlarini o'qish va uni interfeysga ulash uchun eth0. Of birinchi misolimiz filtr ekanligini bilamiz ip to'rtta BPF ko'rsatmalaridan iborat bo'ladi va uchinchi qatorda biz variantdan qanday foydalanishni ko'ramiz SO_ATTACH_FILTER tizim chaqiruvi setsockopt biz uzunlikdagi filtrni yuklaymiz va ulaymiz 4. Bu bizning filtrimiz.

Shuni ta'kidlash kerakki, klassik BPFda filtrni yuklash va ulash har doim atom operatsiyasi sifatida sodir bo'ladi va BPF ning yangi versiyasida dasturni yuklash va uni voqea generatoriga ulash vaqt bo'yicha ajratilgan.

Yashirin haqiqat

Chiqishning biroz to'liqroq versiyasi quyidagicha ko'rinadi:

$ sudo strace -f -e trace=%network tcpdump -p -i eth0 ip
socket(AF_PACKET, SOCK_RAW, 768)        = 3
bind(3, {sa_family=AF_PACKET, sll_protocol=htons(ETH_P_ALL), sll_ifindex=if_nametoindex("eth0"), sll_hatype=ARPHRD_NETROM, sll_pkttype=PACKET_HOST, sll_halen=0}, 20) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=1, filter=0xbeefbeefbeef}, 16) = 0
recvfrom(3, 0x7ffcad394257, 1, MSG_TRUNC, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, {len=4, filter=0xb00bb00bb00b}, 16) = 0
...

Yuqorida aytib o'tilganidek, biz filtrimizni 5-qatordagi rozetkaga yuklaymiz va ulaymiz, lekin 3 va 4-qatorlarda nima sodir bo'ladi? Ma'lum bo'lishicha, bu libpcap bizga g'amxo'rlik qiladi - bizning filtrimiz chiqishi uni qoniqtirmaydigan paketlarni o'z ichiga olmaydi, kutubxona bog‘laydi soxta filtr ret #0 (barcha paketlarni tashlab qo'ying), rozetkani blokirovka qilmaslik rejimiga o'tkazadi va oldingi filtrlardan qolishi mumkin bo'lgan barcha paketlarni olib tashlashga harakat qiladi.

Umuman olganda, klassik BPF-dan foydalangan holda Linuxda paketlarni filtrlash uchun siz shunday tuzilish ko'rinishidagi filtrga ega bo'lishingiz kerak. struct sock_fprog va ochiq rozetka, shundan so'ng filtr tizim chaqiruvi yordamida rozetkaga ulanishi mumkin setsockopt.

Qizig'i shundaki, filtr faqat xom ashyoni emas, balki har qanday rozetkaga ulanishi mumkin. Bu yerga misol barcha kiruvchi UDP datagrammalaridan birinchi ikki baytdan tashqari hammasini kesadigan dastur. (Maqolani chalkashtirmaslik uchun kodga sharhlar qo'shdim.)

Foydalanish haqida batafsil ma'lumot setsockopt Filtrlarni ulash uchun qarang rozetka (7), lekin kabi o'z filtrlaringizni yozish haqida struct sock_fprog yordamisiz tcpdump bo'limida gaplashamiz O'z qo'llarimiz bilan BPF dasturlash.

Klassik BPF va XNUMX-asr

BPF 1997 yilda Linux-ga kiritilgan va uzoq vaqt davomida ishchi kuchi bo'lib qoldi libpcap hech qanday maxsus o'zgarishlarsiz (Linux-ga xos o'zgarishlar, albatta, Bu edi, lekin ular global rasmni o'zgartirmadi). BPF rivojlanishining birinchi jiddiy belgilari 2011 yilda, Erik Dumazet taklif qilganida paydo bo'ldi. yamoq, bu yadroga Just In Time kompilyatorini qo'shadi - BPF bayt kodini mahalliyga aylantirish uchun tarjimon x86_64 kod.

JIT kompilyatori o'zgarishlar zanjirida birinchi bo'ldi: 2012 yilda paydo bo'ldi uchun filtrlar yozish qobiliyati ikkilamchi, BPF yordamida, 2013 yil yanvar oyida bor edi qo'shildi modul xt_bpfuchun qoidalar yozish imkonini beradi iptables BPF yordami bilan va 2013 yil oktyabr oyida bo'lgan qo'shildi shuningdek, modul cls_bpf, bu BPF yordamida trafik tasniflagichlarini yozish imkonini beradi.

Tez orada biz ushbu misollarning barchasini batafsil ko'rib chiqamiz, lekin birinchi navbatda BPF uchun ixtiyoriy dasturlarni yozish va kompilyatsiya qilishni o'rganish foydali bo'ladi, chunki kutubxona tomonidan taqdim etilgan imkoniyatlar. libpcap cheklangan (oddiy misol: filtr yaratilgan libpcap faqat ikkita qiymatni qaytarishi mumkin - 0 yoki 0x40000) yoki umuman, seccomp holatida bo'lgani kabi, qo'llanilmaydi.

O'z qo'llarimiz bilan BPF dasturlash

Keling, BPF ko'rsatmalarining ikkilik formati bilan tanishamiz, bu juda oddiy:

   16    8    8     32
| code | jt | jf |  k  |

Har bir ko'rsatma 64 bitni egallaydi, unda birinchi 16 bit ko'rsatmalar kodi, keyin ikkita sakkiz bitli chekinish mavjud, jt и jf, va argument uchun 32 bit K, maqsadi buyruqdan buyruqqa farq qiladi. Masalan, buyruq ret, dasturni tugatadigan kod mavjud 6, va qaytish qiymati doimiydan olinadi K. C tilida bitta BPF ko'rsatmasi struktura sifatida ifodalanadi

struct sock_filter {
        __u16   code;
        __u8    jt;
        __u8    jf;
        __u32   k;
}

va butun dastur struktura shaklida bo'ladi

struct sock_fprog {
        unsigned short len;
        struct sock_filter *filter;
}

Shunday qilib, biz allaqachon dasturlarni yozishimiz mumkin (masalan, biz ko'rsatmalar kodlarini bilamiz [1]). Filtr shunday ko'rinishga ega bo'ladi ip6 dan birinchi misolimiz:

struct sock_filter code[] = {
        { 0x28, 0, 0, 0x0000000c },
        { 0x15, 0, 1, 0x000086dd },
        { 0x06, 0, 0, 0x00040000 },
        { 0x06, 0, 0, 0x00000000 },
};
struct sock_fprog prog = {
        .len = ARRAY_SIZE(code),
        .filter = code,
};

dastur prog biz qo'ng'iroqda qonuniy foydalanishimiz mumkin

setsockopt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))

Dasturlarni mashina kodlari ko'rinishida yozish unchalik qulay emas, lekin ba'zida kerak bo'ladi (masalan, disk raskadrovka, birlik testlarini yaratish, Habré bo'yicha maqolalar yozish va boshqalar). Qulaylik uchun faylda <linux/filter.h> yordamchi makroslar aniqlangan - yuqoridagi kabi bir xil misol sifatida qayta yozilishi mumkin

struct sock_filter code[] = {
        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_IPV6, 0, 1),
        BPF_STMT(BPF_RET|BPF_K, 0x00040000),
        BPF_STMT(BPF_RET|BPF_K, 0),
}

Biroq, bu variant juda qulay emas. Buni Linux yadrosi dasturchilari o'ylagan va shuning uchun katalogda tools/bpf yadrolari klassik BPF bilan ishlash uchun assembler va tuzatuvchini topishingiz mumkin.

Assembly tili disk raskadrovka chiqishiga juda o'xshaydi tcpdump, lekin qo'shimcha ravishda biz ramziy belgilarni belgilashimiz mumkin. Misol uchun, bu erda TCP/IPv4 dan tashqari barcha paketlarni tashlaydigan dastur mavjud:

$ cat /tmp/tcp-over-ipv4.bpf
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0

Odatiy bo'lib, assembler formatda kod ishlab chiqaradi <количество инструкций>,<code1> <jt1> <jf1> <k1>,..., TCP bilan bizning misolimiz uchun shunday bo'ladi

$ tools/bpf/bpf_asm /tmp/tcp-over-ipv4.bpf
6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 4294967295,6 0 0 0,

C dasturchilariga qulaylik yaratish uchun boshqa chiqish formatidan foydalanish mumkin:

$ tools/bpf/bpf_asm -c /tmp/tcp-over-ipv4.bpf
{ 0x28,  0,  0, 0x0000000c },
{ 0x15,  0,  3, 0x00000800 },
{ 0x30,  0,  0, 0x00000017 },
{ 0x15,  0,  1, 0x00000006 },
{ 0x06,  0,  0, 0xffffffff },
{ 0x06,  0,  0, 0000000000 },

Ushbu matnni tip strukturasi ta'rifiga ko'chirish mumkin struct sock_filter, biz ushbu bo'limning boshida qilganimizdek.

Linux va netsniff-ng kengaytmalari

Standart BPF dan tashqari, Linux va tools/bpf/bpf_asm qo'llab-quvvatlash va nostandart to'plam. Asosan, strukturaning maydonlariga kirish uchun ko'rsatmalar ishlatiladi struct sk_buff, yadrodagi tarmoq paketini tavsiflaydi. Biroq, masalan, yordamchi ko'rsatmalarning boshqa turlari ham mavjud ldw cpu registrga yuklanadi A yadro funktsiyasini ishga tushirish natijasi raw_smp_processor_id(). (BPF ning yangi versiyasida ushbu nostandart kengaytmalar dasturlarni xotira, tuzilmalarga kirish va hodisalarni yaratish uchun yadro yordamchilari toʻplami bilan taʼminlash uchun kengaytirilgan.) Mana biz filtrning qiziqarli misoli, unda biz faqat faylni nusxalashimiz mumkin. paket sarlavhalarini kengaytma yordamida foydalanuvchi maydoniga kiriting poff, foydali yukni qoplash:

ld poff
ret a

BPF kengaytmalaridan foydalanib bo'lmaydi tcpdump, lekin bu kommunal to'plam bilan tanishish uchun yaxshi sababdir netsniff-ng, u boshqa narsalar qatorida rivojlangan dasturni o'z ichiga oladi netsniff-ng, BPF yordamida filtrlashdan tashqari, samarali trafik generatorini ham o'z ichiga oladi va undan ham ilg'or tools/bpf/bpf_asm, BPF assembler chaqirildi bpfc. Paket juda batafsil hujjatlarni o'z ichiga oladi, shuningdek, maqolaning oxiridagi havolalarga qarang.

ikkilamchi

Shunday qilib, biz ixtiyoriy murakkablikdagi BPF dasturlarini qanday yozishni allaqachon bilamiz va yangi misollarni ko'rib chiqishga tayyormiz, ulardan birinchisi seccomp texnologiyasi bo'lib, u BPF filtrlaridan foydalanib, mavjud bo'lgan tizim qo'ng'iroqlari argumentlari to'plamini va to'plamini boshqarishga imkon beradi. berilgan jarayon va uning avlodlari.

Seccomp ning birinchi versiyasi yadroga 2005 yilda qo'shilgan va unchalik mashhur emas edi, chunki u faqat bitta variantni taqdim etdi - jarayon uchun mavjud bo'lgan tizim qo'ng'iroqlari to'plamini quyidagilar bilan cheklash: read, write, exit и sigreturn, va qoidalarni buzgan jarayon yordamida o'ldirilgan SIGKILL. Biroq, 2012 yilda seccomp BPF filtrlaridan foydalanish imkoniyatini qo'shdi, bu sizga ruxsat etilgan tizim qo'ng'iroqlari to'plamini aniqlash va hatto ularning argumentlarini tekshirish imkonini beradi. (Qizig‘i shundaki, Chrome ushbu funksiyadan birinchi foydalanuvchilardan biri bo‘lgan va Chrome foydalanuvchilari hozirda BPF ning yangi versiyasiga asoslangan va Linux xavfsizlik modullarini moslashtirishga ruxsat beruvchi KRSI mexanizmini ishlab chiqmoqda.) Qo‘shimcha hujjatlarga havolalarni oxirida topish mumkin. maqoladan.

E'tibor bering, hubda seccomp-dan foydalanish haqida allaqachon maqolalar bo'lgan, ehtimol kimdir ularni keyingi bo'limlarni o'qishdan oldin (yoki o'rniga) o'qishni xohlaydi. Maqolada Konteynerlar va xavfsizlik: seccomp seccomp-dan foydalanish misollarini taqdim etadi, ham 2007-yil versiyasi, ham BPF-dan foydalanadigan versiya (filtrlar libseccomp yordamida yaratiladi), seccomp-ning Docker bilan ulanishi haqida gapiradi va shuningdek, ko'plab foydali havolalarni taqdim etadi. Maqolada Demonlarni systemd bilan izolyatsiya qilish yoki "buning uchun sizga Docker kerak emas!" Bu, xususan, systemd bilan ishlaydigan demonlar uchun tizim chaqiruvlarining qora ro'yxati yoki oq ro'yxatini qanday qo'shishni o'z ichiga oladi.

Keyinchalik filtrlarni qanday yozish va yuklashni ko'rib chiqamiz seccomp yalang'och C va kutubxonadan foydalanish libseccomp va har bir variantning ijobiy va salbiy tomonlari nimada va nihoyat, seccomp dastur tomonidan qanday ishlatilishini ko'rib chiqaylik. strace.

Seccomp uchun filtrlarni yozish va yuklash

Biz BPF dasturlarini qanday yozishni allaqachon bilamiz, shuning uchun avval seccomp dasturlash interfeysini ko'rib chiqamiz. Jarayon darajasida filtr o'rnatishingiz mumkin va barcha bolalar jarayonlari cheklovlarni meros qilib oladi. Bu tizim chaqiruvi yordamida amalga oshiriladi seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

qayerda &filter - bu bizga allaqachon tanish bo'lgan tuzilishga ishora struct sock_fprog, ya'ni. BPF dasturi.

Seccomp uchun dasturlar rozetkalar uchun dasturlardan qanday farq qiladi? O'tkazilgan kontekst. Soketlar holatida bizga paketni o'z ichiga olgan xotira maydoni berildi va seccomp holatida bizga o'xshash tuzilma berildi.

struct seccomp_data {
    int   nr;
    __u32 arch;
    __u64 instruction_pointer;
    __u64 args[6];
};

u nr ishga tushiriladigan tizim qo'ng'irog'ining raqami, arch - joriy arxitektura (quyida batafsilroq), args - oltitagacha tizim chaqiruvi argumentlari va instruction_pointer tizim chaqiruvini amalga oshirgan foydalanuvchi maydoni ko'rsatmasiga ko'rsatgichdir. Shunday qilib, masalan, tizim qo'ng'iroq raqamini registrga yuklash A aytishimiz kerak

ldw [0]

Seccomp dasturlari uchun boshqa xususiyatlar mavjud, masalan, kontekstga faqat 32-bitli tekislash orqali kirish mumkin va siz yarim so'z yoki baytni yuklay olmaysiz - filtrni yuklamoqchi bo'lganingizda ldh [0] tizim chaqiruvi seccomp qaytadi EINVAL. Funktsiya yuklangan filtrlarni tekshiradi seccomp_check_filter() yadrolari. (Qiziq tomoni shundaki, seccomp funksiyasini qo'shgan asl majburiyatda ular ushbu funktsiyaga yo'riqnomadan foydalanishga ruxsat berishni unutishgan. mod (bo'linish qoldig'i) va endi u qo'shilganidan beri seccomp BPF dasturlari uchun mavjud emas buziladi ABI.)

Asosan, biz seccomp dasturlarini yozish va o'qish uchun hamma narsani bilamiz. Odatda dastur mantig'i tizim qo'ng'iroqlarining oq yoki qora ro'yxati sifatida tartibga solinadi, masalan, dastur

ld [0]
jeq #304, bad
jeq #176, bad
jeq #239, bad
jeq #279, bad
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
bad: ret #0

304, 176, 239, 279 raqamli to'rtta tizim qo'ng'iroqlarining qora ro'yxatini tekshiradi. Bu tizim qo'ng'iroqlari nima? Biz aniq ayta olmaymiz, chunki dastur qaysi arxitektura uchun yozilganligini bilmaymiz. Shuning uchun, seccomp mualliflari taklif barcha dasturlarni arxitektura tekshiruvi bilan boshlang (joriy arxitektura kontekstda maydon sifatida ko'rsatilgan arch tuzilmalar struct seccomp_data). Arxitektura tekshirilganda, misolning boshlanishi quyidagicha ko'rinadi:

ld [4]
jne #0xc000003e, bad_arch ; SCMP_ARCH_X86_64

va keyin bizning tizim qo'ng'iroq raqamlari ma'lum qiymatlarni oladi.

Biz seccomp foydalanish uchun filtrlarni yozamiz va yuklaymiz libseccomp

Filtrlarni mahalliy kodda yoki BPF yig'ilishida yozish sizga natijani to'liq nazorat qilish imkonini beradi, lekin shu bilan birga, ba'zan ko'chma va/yoki o'qiladigan kodga ega bo'lish afzalroqdir. Bunda bizga kutubxona yordam beradi libseccomp, bu qora yoki oq filtrlarni yozish uchun standart interfeysni ta'minlaydi.

Keling, masalan, foydalanuvchi tanlagan ikkilik faylni ishga tushiradigan dastur yozaylik, bundan oldin tizim qo'ng'iroqlarining qora ro'yxatini o'rnatgan. yuqoridagi maqola (Dastur ko'proq o'qilishi uchun soddalashtirilgan, to'liq versiyasini topish mumkin shu yerda):

#include <seccomp.h>
#include <unistd.h>
#include <err.h>

static int sys_numbers[] = {
        __NR_mount,
        __NR_umount2,
       // ... еще 40 системных вызовов ...
        __NR_vmsplice,
        __NR_perf_event_open,
};

int main(int argc, char **argv)
{
        scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);

        for (size_t i = 0; i < sizeof(sys_numbers)/sizeof(sys_numbers[0]); i++)
                seccomp_rule_add(ctx, SCMP_ACT_TRAP, sys_numbers[i], 0);

        seccomp_load(ctx);

        execvp(argv[1], &argv[1]);
        err(1, "execlp: %s", argv[1]);
}

Avval massivni aniqlaymiz sys_numbers 40 dan ortiq tizim qo'ng'iroq raqamlari bloklanadi. Keyin kontekstni ishga tushiring ctx va kutubxonaga nimaga ruxsat berishimizni ayting (SCMP_ACT_ALLOW) sukut bo'yicha barcha tizim qo'ng'iroqlari (qora ro'yxatlarni tuzish osonroq). Keyin, biz qora ro'yxatdagi barcha tizim qo'ng'iroqlarini birma-bir qo'shamiz. Ro'yxatdagi tizim chaqiruviga javoban biz so'raymiz SCMP_ACT_TRAP, bu holda seccomp jarayonga signal yuboradi SIGSYS qaysi tizim chaqiruvi qoidalarni buzganligi tavsifi bilan. Nihoyat, dasturni yadroga yuklaymiz seccomp_load, u dasturni kompilyatsiya qiladi va tizim chaqiruvi yordamida jarayonga biriktiradi seccomp(2).

Muvaffaqiyatli kompilyatsiya qilish uchun dastur kutubxona bilan bog'langan bo'lishi kerak libseccomp, masalan:

cc -std=c17 -Wall -Wextra -c -o seccomp_lib.o seccomp_lib.c
cc -o seccomp_lib seccomp_lib.o -lseccomp

Muvaffaqiyatli ishga tushirishga misol:

$ ./seccomp_lib echo ok
ok

Bloklangan tizim chaqiruviga misol:

$ sudo ./seccomp_lib mount -t bpf bpf /tmp
Bad system call

Biz foydalanamiz stracetafsilotlar uchun:

$ sudo strace -e seccomp ./seccomp_lib mount -t bpf bpf /tmp
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=50, filter=0x55d8e78428e0}) = 0
--- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0xboobdeadbeef, si_syscall=__NR_mount, si_arch=AUDIT_ARCH_X86_64} ---
+++ killed by SIGSYS (core dumped) +++
Bad system call

noqonuniy tizim qo'ng'irog'idan foydalanish tufayli dastur tugatilganligini qanday bilishimiz mumkin mount(2).

Shunday qilib, biz kutubxona yordamida filtr yozdik libseccomp, trivial bo'lmagan kodni to'rt qatorga joylashtirish. Yuqoridagi misolda, agar tizim qo'ng'iroqlari ko'p bo'lsa, bajarish vaqtini sezilarli darajada qisqartirish mumkin, chunki tekshirish faqat taqqoslashlar ro'yxatidir. Optimallashtirish uchun libseccomp yaqinda bor edi patch kiritilgan, bu filtr atributini qo'llab-quvvatlaydi SCMP_FLTATR_CTL_OPTIMIZE. Ushbu atributni 2 ga o'rnatish filtrni ikkilik qidiruv dasturiga aylantiradi.

Ikkilik qidiruv filtrlari qanday ishlashini ko'rishni istasangiz, ko'rib chiqing oddiy skript, bu kabi dasturlarni BPF assemblerda tizim chaqiruv raqamlarini terish orqali yaratadi, masalan:

$ echo 1 3 6 8 13 | ./generate_bin_search_bpf.py
ld [0]
jeq #6, bad
jgt #6, check8
jeq #1, bad
jeq #3, bad
ret #0x7fff0000
check8:
jeq #8, bad
jeq #13, bad
ret #0x7fff0000
bad: ret #0

Siz tezroq hech narsa yoza olmaysiz, chunki BPF dasturlari chekinishlarni amalga oshira olmaydi (masalan, biz qila olmaymiz jmp A yoki jmp [label+X]) va shuning uchun barcha o'tishlar statikdir.

seccomp va strace

Foydali dasturni hamma biladi strace Linuxda jarayonlarning harakatini o'rganish uchun ajralmas vositadir. Biroq, ko'pchilik bu haqda eshitgan ishlash muammolari ushbu yordam dasturidan foydalanganda. Gap shundaki strace yordamida amalga oshirildi ptrace(2), va bu mexanizmda biz tizim chaqiruvlarining qaysi to'plamida jarayonni to'xtatishimiz kerakligini aniqlay olmaymiz, ya'ni, masalan, buyruqlar.

$ time strace du /usr/share/ >/dev/null 2>&1

real    0m3.081s
user    0m0.531s
sys     0m2.073s

и

$ time strace -e open du /usr/share/ >/dev/null 2>&1

real    0m2.404s
user    0m0.193s
sys     0m1.800s

Taxminan bir vaqtning o'zida qayta ishlanadi, ammo ikkinchi holatda biz faqat bitta tizim chaqiruvini kuzatishni xohlaymiz.

Yangi variant --seccomp-bpf, ga qo'shildi strace 5.3 versiyasi jarayonni ko'p marta tezlashtirishga imkon beradi va bitta tizim qo'ng'irog'i ostida ishga tushirish vaqti allaqachon oddiy ishga tushirish vaqti bilan taqqoslanadi:

$ time strace --seccomp-bpf -e open du /usr/share/ >/dev/null 2>&1

real    0m0.148s
user    0m0.017s
sys     0m0.131s

$ time du /usr/share/ >/dev/null 2>&1

real    0m0.140s
user    0m0.024s
sys     0m0.116s

(Bu erda, albatta, biz ushbu buyruqning asosiy tizim chaqiruvini kuzatmayapmiz, deb bir oz aldamchilik bor. Agar biz kuzatayotgan bo'lsak, masalan, newfsstatso'ng strace bo'lmagandek qattiq tormozlanadi --seccomp-bpf.)

Bu variant qanday ishlaydi? Usiz strace jarayonga ulanadi va undan foydalanishni boshlaydi PTRACE_SYSCALL. Boshqariladigan jarayon (har qanday) tizim chaqiruvini chiqarganda, boshqaruv unga o'tkaziladi strace, bu tizim chaqiruvining argumentlarini ko'rib chiqadi va u bilan ishlaydi PTRACE_SYSCALL. Bir muncha vaqt o'tgach, jarayon tizim chaqiruvini tugatadi va undan chiqishda boshqaruv yana o'tkaziladi strace, Qaytish qiymatlarini ko'rib chiqadi va foydalanish jarayonini boshlaydi PTRACE_SYSCALL, va h.k.

Kichkintoylar uchun BPF, nol qism: klassik BPF

Biroq, seccomp bilan bu jarayonni biz xohlagandek optimallashtirish mumkin. Ya'ni, agar biz faqat tizim chaqiruviga qarashni istasak X, keyin biz BPF filtrini yozishimiz mumkin X qiymat qaytaradi SECCOMP_RET_TRACE, va bizni qiziqtirmaydigan qo'ng'iroqlar uchun - SECCOMP_RET_ALLOW:

ld [0]
jneq #X, ignore
trace: ret #0x7ff00000
ignore: ret #0x7fff0000

Bunday holda strace deb jarayonni dastlab boshlaydi PTRACE_CONT, agar tizim chaqiruvi bo'lmasa, filtrimiz har bir tizim chaqiruvi uchun qayta ishlanadi X, keyin jarayon ishlashni davom ettiradi, lekin agar bu X, keyin seccomp boshqaruvni uzatadi straceargumentlarni ko'rib chiqadi va jarayonni boshlaydi PTRACE_SYSCALL (chunki seccomp tizim chaqiruvidan chiqishda dasturni ishga tushirish imkoniyatiga ega emas). Tizim chaqiruvi qaytganida, strace yordamida jarayonni qayta ishga tushiradi PTRACE_CONT va seccompdan yangi xabarlarni kutadi.

Kichkintoylar uchun BPF, nol qism: klassik BPF

Variantdan foydalanganda --seccomp-bpf ikkita cheklov mavjud. Birinchidan, allaqachon mavjud jarayonga qo'shilish mumkin bo'lmaydi (variant -p dasturlari strace), chunki bu seccomp tomonidan qo'llab-quvvatlanmaydi. Ikkinchidan, hech qanday imkoniyat yo'q yo'q bolalar jarayonlariga qarang, chunki seccomp filtrlari barcha bolalar jarayonlari tomonidan meros qilib olinadi, uni o'chirish imkoniyatisiz.

Qanday qilib aniqroq haqida bir oz ko'proq strace bilan ishlaydi seccomp dan topish mumkin so'nggi hisobot. Biz uchun eng qiziq fakt shundaki, seccomp tomonidan taqdim etilgan klassik BPF bugungi kunda ham qo'llaniladi.

xt_bpf

Endi tarmoqlar olamiga qaytaylik.

Ma'lumot: uzoq vaqt oldin, 2007 yilda yadro edi qo'shildi modul xt_u32 netfiltr uchun. U yanada qadimgi transport klassifikatoriga o'xshash tarzda yozilgan cls_u32 va quyidagi oddiy amallar yordamida iptables uchun ixtiyoriy ikkilik qoidalarni yozish imkonini berdi: paketdan 32 bit yuklash va ular ustida arifmetik amallar to‘plamini bajarish. Masalan,

sudo iptables -A INPUT -m u32 --u32 "6&0xFF=1" -j LOG --log-prefix "seen-by-xt_u32"

32-to‘ldirishdan boshlab IP sarlavhasining 6 bitini yuklaydi va ularga niqob qo‘llaydi. 0xFF (past baytni oling). Bu maydon protocol IP sarlavhasi va biz uni 1 (ICMP) bilan solishtiramiz. Ko'p cheklarni bitta qoidada birlashtira olasiz, shuningdek, operatorni ham bajarishingiz mumkin @ — X baytni o'ngga siljiting. Masalan, qoida

iptables -m u32 --u32 "6&0xFF=0x6 && 0>>22&0x3C@4=0x29"

TCP tartib raqami teng emasligini tekshiradi 0x29. Men batafsil ma'lumotga kirmayman, chunki bunday qoidalarni qo'lda yozish juda qulay emasligi allaqachon aniq. Maqolada BPF - unutilgan bayt-kod, uchun foydalanish va qoidalar yaratish misollari bilan bir nechta havolalar mavjud xt_u32. Shuningdek, ushbu maqolaning oxiridagi havolalarga qarang.

2013 yildan boshlab modul o'rniga modul xt_u32 BPF asosidagi moduldan foydalanishingiz mumkin xt_bpf. Hozirgacha o'qigan har bir kishi uning ishlash printsipi haqida allaqachon aniq bo'lishi kerak: BPF bayt kodini iptables qoidalari sifatida ishlating. Siz yangi qoida yaratishingiz mumkin, masalan:

iptables -A INPUT -m bpf --bytecode <байткод> -j LOG

shu yerda <байткод> - bu assembler chiqish formatidagi kod bpf_asm sukut bo'yicha, masalan,

$ cat /tmp/test.bpf
ldb [9]
jneq #17, ignore
ret #1
ignore: ret #0

$ bpf_asm /tmp/test.bpf
4,48 0 0 9,21 0 1 17,6 0 0 1,6 0 0 0,

# iptables -A INPUT -m bpf --bytecode "$(bpf_asm /tmp/test.bpf)" -j LOG

Ushbu misolda biz barcha UDP paketlarini filtrlaymiz. Moduldagi BPF dasturi uchun kontekst xt_bpf, albatta, paketli ma'lumotlarga ishora qiladi, iptables bo'lsa, IPv4 sarlavhasining boshlanishi. BPF dasturidan qiymatni qaytarish mantiqiyqayerda false paketning mos kelmasligini bildiradi.

Modul ekanligi aniq xt_bpf yuqoridagi misoldan ko'ra murakkabroq filtrlarni qo'llab-quvvatlaydi. Keling, Cloudfare-dan haqiqiy misollarni ko'rib chiqaylik. Yaqin vaqtgacha ular moduldan foydalanganlar xt_bpf DDoS hujumlaridan himoya qilish uchun. Maqolada BPF vositalari bilan tanishtirish ular qanday qilib (va nima uchun) BPF filtrlarini yaratishlarini va bunday filtrlarni yaratish uchun yordamchi dasturlar to'plamiga havolalarni nashr etishlarini tushuntiradilar. Masalan, yordamchi dasturdan foydalanish bpfgen nom uchun DNS so'roviga mos keladigan BPF dasturini yaratishingiz mumkin habr.com:

$ ./bpfgen --assembly dns -- habr.com
ldx 4*([0]&0xf)
ld #20
add x
tax

lb_0:
    ld [x + 0]
    jneq #0x04686162, lb_1
    ld [x + 4]
    jneq #0x7203636f, lb_1
    ldh [x + 8]
    jneq #0x6d00, lb_1
    ret #65535

lb_1:
    ret #0

Dasturda biz birinchi navbatda registrga yuklaymiz X qatorning bosh manzili x04habrx03comx00 UDP datagrami ichida va so'rovni tekshiring: 0x04686162 <-> "x04hab" va hokazo.

Biroz vaqt o'tgach, Cloudfare p0f -> BPF kompilyator kodini nashr etdi. Maqolada p0f BPF kompilyatori bilan tanishish ular p0f nima ekanligi va p0f imzolarini BPF ga qanday aylantirish haqida gapiradilar:

$ ./bpfgen p0f -- 4:64:0:0:*,0::ack+:0
39,0 0 0 0,48 0 0 8,37 35 0 64,37 0 34 29,48 0 0 0,
84 0 0 15,21 0 31 5,48 0 0 9,21 0 29 6,40 0 0 6,
...

Hozirda Cloudfare ishlatilmaydi xt_bpf, ular XDP ga o'tganligi sababli - BPF ning yangi versiyasidan foydalanish variantlaridan biri, qarang. L4Drop: XDP DDoS yumshatishlari.

cls_bpf

Yadroda klassik BPF dan foydalanishning oxirgi misoli klassifikatordir cls_bpf 2013 yil oxirida Linux-ga qo'shilgan va kontseptual ravishda qadimgi tizimni almashtirgan Linux-dagi trafikni boshqarish quyi tizimi uchun cls_u32.

Biroq, biz endi ishni tasvirlamaymiz cls_bpf, chunki klassik BPF haqidagi bilim nuqtai nazaridan bu bizga hech narsa bermaydi - biz allaqachon barcha funktsiyalar bilan tanishib chiqdik. Bundan tashqari, kengaytirilgan BPF haqida gapiradigan keyingi maqolalarda biz ushbu tasniflagich bilan bir necha bor uchrashamiz.

Klassik BPF dan foydalanish haqida gapirmaslikning yana bir sababi c cls_bpf Muammo shundaki, Kengaytirilgan BPF bilan solishtirganda, bu holda qo'llash doirasi tubdan toraygan: klassik dasturlar paketlar tarkibini o'zgartira olmaydi va qo'ng'iroqlar orasidagi holatni saqlay olmaydi.

Shunday qilib, klassik BPF bilan xayrlashish va kelajakka qarash vaqti keldi.

Klassik BPF bilan xayrlashing

Biz 32-yillarning boshlarida ishlab chiqilgan BPF texnologiyasi chorak asr davomida qanday muvaffaqiyatli yashaganini va oxirigacha yangi ilovalarni topganini ko'rib chiqdik. Biroq, klassik BPF ning rivojlanishiga turtki bo'lgan stekli mashinalardan RISC ga o'tishga o'xshab, 64-yillarda XNUMX-bitli mashinalardan XNUMX-bitli mashinalarga o'tish sodir bo'ldi va klassik BPF eskira boshladi. Bundan tashqari, klassik BPF imkoniyatlari juda cheklangan va eskirgan arxitekturaga qo'shimcha ravishda - bizda BPF dasturlariga qo'ng'iroqlar orasidagi holatni saqlash imkoniyati yo'q, foydalanuvchining to'g'ridan-to'g'ri muloqot qilish imkoniyati yo'q, o'zaro ta'sir qilish imkoniyati yo'q. yadro bilan, cheklangan miqdordagi struktura maydonlarini o'qishdan tashqari sk_buff va eng oddiy yordamchi funksiyalarni ishga tushirganda, siz paketlar tarkibini o'zgartira olmaysiz va ularni qayta yo'naltira olmaysiz.

Haqiqatan ham, hozirda Linuxda klassik BPF-dan qolgan barcha narsa API interfeysi bo'lib, yadro ichida barcha klassik dasturlar, xoh u rozetka filtrlari, xoh seccomp filtrlari avtomatik ravishda kengaytirilgan BPF formatiga tarjima qilinadi. (Bu qanday sodir bo'lishi haqida keyingi maqolada gaplashamiz.)

Yangi arxitekturaga o'tish 2013 yilda, Aleksey Starovoitov BPF yangilash sxemasini taklif qilganida boshlangan. 2014 yilda tegishli yamalar paydo bo'la boshladi yadroda. Men tushunganimdek, dastlabki reja faqat 64-bitli mashinalarda samaraliroq ishlashi uchun arxitektura va JIT kompilyatorini optimallashtirish edi, ammo bu optimallashtirishlar Linuxni rivojlantirishda yangi bobning boshlanishini belgiladi.

Ushbu turkumdagi keyingi maqolalar dastlab ichki BPF, keyin kengaytirilgan BPF va endi oddiygina BPF deb nomlanuvchi yangi texnologiyaning arxitekturasi va ilovalarini qamrab oladi.

Manbalar

  1. Stiven MakKen va Van Jeykobson, "BSD paket filtri: foydalanuvchi darajasidagi paketlarni yozib olish uchun yangi arxitektura", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Stiven MakKen, "libpcap: Paketlarni yozib olish uchun arxitektura va optimallashtirish metodologiyasi", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 oʻyiniga oid qoʻllanma.
  5. BPF - unutilgan bayt-kod: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. BPF vositasi bilan tanishish: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Qisqacha sharh: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Konteynerlar va xavfsizlik: seccomp
  11. habr: Demonlarni systemd bilan izolyatsiya qilish yoki "buning uchun sizga Docker kerak emas!"
  12. Pol Chaignon, "strace --seccomp-bpf: kaput ostidagi ko'rinish", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Manba: www.habr.com

a Izoh qo'shish