Berkeley Packet Filters (BPF) on Linuxi tuumor, mis on juba mitu aastat olnud ingliskeelsete tehniliste vĂ€ljaannete esikĂŒlgedel. Konverentse on tĂ€is ettekandeid BPF-i kasutamise ja arendamise kohta. David Miller, Linuxi vĂ”rgu alamsĂŒsteemi hooldaja, nimetab oma ettekannet Linux Plumbers 2018. (XDP on ĂŒks BPF-i kasutamise variante). Brendan Gregg peab ettekandeid, mille pealkiri on . Toke HĂžiland-JĂžrgensen , et tuuma on nĂŒĂŒd microkernel. Thomas Graf reklaamib ideed, et .
HabrĂĄs ei ole veel sĂŒsteemset kirjeldust BPF-ist ja seetĂ”ttu pĂŒĂŒan ma oma artiklite seerias rÀÀkida tehnoloogia ajaloost, kirjeldada arhitektuuri ja arendusvahendeid, visandada kasutusalad ja BPF-i kasutamise tavad. Selles, nulls artiklis, antakse ĂŒlevaade klassikalise BPF-i ajaloost ja arhitektuurist, samuti paljastatakse tööpĂ”himĂ”tete saladused tcpdump, seccomp, strace, ja palju muud.
BPF-i arendust juhib Linuxi vÔrgu kogukond, peamised olemasolevad BPF-i rakendused on seotud vÔrkudega ja seetÔttu, lubades , nimetas ma seeria "BPF kÔige vÀiksematele", kuulsale seeriale .
BPF-i ajalugu lĂŒhidalt (c)
Kaasaegne BPF-tehnoloogia on tĂ€iustatud ja tĂ€iustatud versioon vanast tehnoloogiast, mida nimetatakse nĂŒĂŒd, et vĂ€ltida segadust, klassikaliseks BPF-iks. Klassikalise BPF-i baasil on loodud tuntud tööriist tcpdump, mehhanism seccomp, samuti vĂ€hem tuntud moodul xt_bpf jaoks iptables ja klassifikaator cls_bpf. Kaasaegses Linuxis tĂ”lgitakse klassikalised BPF-programmid automaatselt uude vormi, kuid kasutaja seisukohalt on API jÀÀnud samaks ja klassikalise BPF-i uued rakendused, nagu me selle artikli kĂ€igus nĂ€eme, on endiselt olemas. SellepĂ€rast, samuti seetĂ”ttu, et klassikalise BPF-i ajaloo jĂ€lgimine Linuxis nĂ€itab, kuidas ja miks see on arenenud kaasaegsesse vormi, otsustasin alustada just klassikalise BPF-i artiklist.
KaheksakĂŒmnendate aastate lĂ”pus huvitusid tuntud Lawrence Berkeley Laboratori insenerid kĂŒsimusest, kuidas Ă”igesti filtreerida vĂ”rgu pakette tol ajal olemasolevatel seadmetel. Filtreerimise pĂ”hiidee, mis algselt realiseeriti CSPF (CMU/Stanford Packet Filter) tehnoloogias, seisnes selles, et filtreerida liigsed paketid nii varakult kui vĂ”imalik, st kernelis, kuna see vĂ”imaldab mitte kopeerida liigseid andmeid kasutaja ruumi. KĂ€itusaja turvalisuse tagamiseks kasutati kernelis kasutajakoode kĂ€itamiseks virtuaalmasinat â liivakasti.
Kuid virtuaalmasinad olemasolevatele filtritele olid projekteeritud töötama kuhja arhitektuuril ja uutel RISC masinatel töö olid vĂ€hem efektiivsed. LĂ”puks töötas Berkeley Labs'i inseneride jĂ”upingutustel vĂ€lja uus tehnoloogia BPF (Berkeley Packet Filters), mille virtuaalmasina arhitektuur oli projekteeritud Motorola 6502 protsessori pĂ”hjal â tööloom selliste tuntud toodete vĂ€ltel nagu vĂ”i . Uus virtuaalmasin suurendas filtrite jĂ”udlust mitmekĂŒmne korra vĂ”rreldes olemasolevate lahendustega.
BPF masina arhitektuur
Me tutvume masina arhitektuuriga tööalaselt, analĂŒĂŒsides nĂ€iteid. Kuid alustuseks ĂŒtlemine, et masinal oli kaks kasutajale ligipÀÀsetavat 32-bitist registrit, akumulaator A ja indeksregister X, 64 baiti mĂ€lu (16 sĂ”na), mis oli kirjutamiseks ja edaspidiseks lugemiseks saadaval, ning vĂ€ike kĂ€skude sĂŒsteem nende objektidega töötamiseks. Programmides olid saadaval ka hĂŒppeinstruktsioonid tingimuslike lause rakendamiseks, kuid programmi Ă”igeaegse lĂ”petamise tagamiseks tohtis hĂŒpata ainult edasi, st konkreetselt oli keelatud luua tsĂŒkleid.
Masina töö ĂŒldskeem on jĂ€rgmine. Kasutaja koostab programmi BPF arhitektuurile ja, kasutades mingit kernelimehhanismi (nĂ€iteks sĂŒsteemi kĂ”ne), laadib ja ĂŒhendab programmi mingi sĂŒndmuste generaatoriga kernelis (nĂ€iteks sĂŒndmus â jĂ€rgmise paketi saabumine vĂ”rgu kaardile). SĂŒndmuse tekkimisel kĂ€ivitab kernel programmi (nĂ€iteks tĂ”lgendi kaudu), samal ajal kui masina mĂ€lu on kooskĂ”las mingi tuuma mĂ€lu tuumast (nĂ€iteks saabunud paketi andmetest).
Ălaltoodud piisab meile, et alustada nĂ€idete uurimist: vajaduse korral tutvume sĂŒsteemi ja kĂ€skude vorminguga. Kui aga soovite kohe teada saada virtuaalmasina kĂ€skude sĂŒsteemi ja selle kĂ”ikidest vĂ”imalustest, siis saate lugeda originaalartiklit ja/vĂ”i faili esimesest poolest tuuma dokumentatsioonist. Lisaks saab tutvuda esitlustega , kus McCanne, ĂŒks BPF autoritest, rÀÀgib selle loomise ajaloost libpcap.
KĂ€ime nĂŒĂŒd lĂ€bi kĂ”ik olulised klassikalise BPF rakendused Linuxis: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.
tcpdump
BPF arendamine toimus samaaegselt tuntud paketikottide filtreerimise tööriista - kĂ”igile teada oleva utiliidi - arendamisega. tcpdump. Kuna see on vanim ja kĂ”ige tuntum klassikalise BPF rakendustest, saadaval paljudes operatsioonisĂŒsteemides, alustame tehnoloogia uurimist just sellest.
(KÔiki selle artikli nÀiteid jooksutasin Linuxis 5.6.0-rc6. MÔnede kÀskude vÀljund on muudetud suurema loetavuse saavutamiseks.)
NÀide: jÀlgime IPv6 pakette
Kujutage ette, et soovime jÀlgida kÔiki IPv6 pakette liideses eth0. Selleks saame kÀivitada programmi tcpdump lihtsa filtriga ip6:
$ sudo tcpdump -i eth0 ip6Selle juures tcpdump kompileerib filtri ip6 BPF arhitektuuri baitkoodiks ja saadab selle tuumale (vt ĂŒksikasju peatĂŒkis ). Laaditud filter kĂ€ivitatakse iga paketi jaoks, mis lĂ€bib liidest eth0. Kui filter tagastab mitte nullvÀÀrtuse n, siis n paketi baitidest kopeeritakse kasutaja ruumi ja nĂ€eme seda vĂ€ljundis tcpdump.

Selgub, et saame lihtsalt teada, millist baitkoodi tuumale saatsime tcpdump kasutades tcpdump, kui kÀivitame selle valikuga -d:
$ sudo tcpdump -i eth0 -d ip6
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 3
(002) ret #262144
(003) ret #0Esimesel real kĂ€ivitame kĂ€su ldh [12], mis deĆĄifreeritakse kui "lae registrisse A pool-sĂ”na (16 bitti), mis asub aadressil 12" ja ainus kĂŒsimus on, millisest mĂ€lust me rÀÀgime? Vastus on see, et aadressil x alustatakse (x+1)-ndat baiti analĂŒĂŒsitavast vĂ”rgu paketist. Me loeme pakette Etherneti liidese kaudu eth0, ja see tĂ€hendab , et pakett nĂ€eb vĂ€lja jĂ€rgmine (lihtsuse huvides oletame, et pakett ei sisalda VLAN silte):
6 6 2
|Siht MAC|Allika MAC|Ether Type|...|Nii et pĂ€rast kĂ€su tĂ€itmist ldh [12] registreerimise A tuleb vĂ€lja vĂ€li Ether Type â edastatava paketi tĂŒĂŒp, mis sisaldub selles Etherneti raamises. Reedel 1 vĂ”rreldame registri sisu A (paketi tĂŒĂŒp) c 0x86dd, ja see tĂ€hendab meie huvi pakkuv IPv6 tĂŒĂŒp. Reedel 1 on lisaks vĂ”rdluskĂ€sule veel kaks veergu â jt 2 ja jf 3 â silte, kuhu tuleb liikuda eduka vĂ”rdluse korral (A == 0x86dd) ja ebaeduka korral. Nii et eduka juhtumi (IPv6) korral liigume reele 2, ebaeduka korral reele 3. Reedel 3 lĂ”petab programm koodiga 0 (paketti ei kopeerita), reedel 2 lĂ”petab programm koodiga 262144 (kopeeri mulle maksimaalselt 256 kilobaiti paketti).
Veidi keerulisem nÀide: vaatame TCP pakette sihtporti lÀbi
Vaadake, kuidas filtreerija, mis kopeerib kĂ”ik TCP paketid sihtporti 666. Uurime IPv4 juhtumit, kuna IPv6 juhtum on lihtsam. PĂ€rast selle nĂ€ite uurimist vĂ”ite harjutusena iseseisvalt uurida IPv6 filtri (ip6 and tcp dst port 666) ja filtri ĂŒldise juhtumi (tcp dst port 666). Nii et meie huvi pakkuv filter nĂ€eb vĂ€lja jĂ€rgmiselt:
$ 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 #0Mida teevad read 0 ja 1, me juba teame. Reedel 2 oleme juba kontrollinud, et see on IPv4 pakett (Ether Type = 0x800) ja laadime registrisse A paketi 24. baiti. Meie pakett nÀeb vÀlja nagu
14 8 1 1
|etherneti pea|ip vÀljad|ttl|protokoll|...seega laadime registrisse A IP peaaia Protokoll, mis on mÔistetav, sest me tahame kopeerida ainult TCP pakette. Me vÔrdleme Protokolli 0x6 () reedetel 3.
Reedel 4 ja 5 laadime pool sĂ”na, mis asub aadressil 20, ja kĂ€su abil jset kontrollime, kas ega ĂŒks kolmest â antud maskis jset on kolm kĂ”ige tĂ€htsamat bitti puhastamata. Kaks kolme bittide hulgast annavad meile teada, kas pakett on fraktsioonitud IP paketi osa, ja kui jah, kas see on viimane fraktsioon. Kolmas bitt on reserveeritud ja peab olema null. Me ei soovi kontrollida ei tĂ€is- ega vigased pakette, seetĂ”ttu kontrollime kĂ”iki kolme bitti.
Reede 6 on selle loendi kĂ”ige huvitavam. Avaldus ldxb 4*([14]&0xf) tĂ€hendab, et laadime registrisse X neljandad madalamad bitid viieteistkĂŒmnendas baitis, korrutatud 4. Neljandad madalamad bitid viieteistkĂŒmnendas baitis on vĂ€li IPv4 pĂ€ises, kus salvestatakse pĂ€ise pikkus sĂ”nades, seetĂ”ttu tuleb see hiljem korrutada 4-ga. Huvi pakub, et vĂ€ljend 4*([14]&0xf) on eriline adresseerimise skeem, mida saab kasutada ainult sellisel kujul ja ainult registri puhul X, st me ei saa öelda ei ldb 4*([14]&0xf) ei ldxb 5*([14]&0xf) (saame ainult mĂ€rkida teise offseti, nĂ€iteks ldxb 4*([16]&0xf)). Selge on, et see adresseerimise skeem lisati BPF just selleks, et jĂ”uda X (indekseerimisregister) IPv4 pĂ€ise pikkuseni.
Nii et real 7 proovime laadida pool-sÔna aadressilt (X+16). Meenutades, et 14 baiti hÔlmab Etherneti pÀist, ja X sisaldab IPv4 pÀise pikkust, mÔistame, et A laaditakse TCP sihtport:
14 X 2 2
|ethernet header|ip header|source port|destination port|LĂ”puks, real 8 vĂ”rdleme sihtporti soovitud vÀÀrtusega ja ridadel 9 vĂ”i 10 tagastame tulemuse â kas kopeerida pakett vĂ”i mitte.
Tcpdump: laadimine
Eelnevates nĂ€idetes ei peatunud me tĂ€pselt sellel, kuidas me laadime BPF bytecode'i kernelisse pakettide filtreerimiseks. Ăldiselt, tcpdump on see portiteeritud paljudele sĂŒsteemidele ja filtreid kasutades , tĂ€psemalt â kirjutame kaks funktsiooni: ĂŒks pseudo-ristkĂŒlikulise maatriksi kasutamisega (mida ei soovitata praktiliselt, kuna see on arvutuslikult keeruline ja ebatavaline), teine maatrikuvĂ”rrandi abil. . LĂŒhidalt, et panna filter liidesele kasutades libpcap, tuleb teha jĂ€rgmiselt:
- luua tĂŒĂŒpi deskriptor
pcap_tliidese nimest: , - aktiveerida liides: ,
- kompileerida filter: ,
- ĂŒhendada filter: .
Kuna nÀha, kuidas funktsioon pcap_setfilter on Linuxis teostatud, kasutame strace (mÔned read on eemaldatud):
$ 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
...Esimese kahe rea vĂ€ljundis loome kĂ”igi Etherneti kaadrite lugemiseks ja seome selle liidesega eth0. Meie teame, et filter ip koosneb neljast BPF kĂ€sklusest, ja kolmandal real nĂ€eme, kuidas lĂ€bi system call setsockopt laadime ja ĂŒhendame 4-pikkuse filtri. Just see on meie filter.
On tuleb mĂ€rkida, et klassikalises BPF-is toimub filtri laadimine ja liitmine alati aatomaarse operatsioonina, samas kui BPF-i uues versioonis on programmi laadimine ja selle sidumine sĂŒndmuste generaatoriga ajaliselt eraldatud.
Peidetud tÔde
Veidi tÀielikum versioon vÀljundist on jÀrgmine:
$ 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
...Nagu eespool mainitud, laadime ja liidame meie filtri soketiga real 5, kuid mis toimub ridadel 3 ja 4? Selgub, et see libpcap muretseb meie eest â et meie filtri vĂ€ljundisse ei satuks pakette, mis sellele ei vasta, raamatukogu vale filtri ret #0 (kĂ”ik paketid visata kĂ”rvale), muudab soketi mitteblokeerivaks ja proovib lugeda kĂ”ik paketid, mis vĂ”isid jÀÀda eelmistelt filtritelt.
Seega, et filtreerida pakette Linuxis klassikalise BPF-i kaudu, on vajalik filter struktuuri tĂŒĂŒbina struct sock_fprog ja avatud soket, pĂ€rast mida saab filtri soketiga siduda sĂŒsteemikĂ”ne abil setsockopt.
On huvitav, et filter saab liita igasuguste soketitega, mitte ainult raw. Siin on programm, mis lÔikab kÔik vÀlja, vÀlja arvatud kaks esimest bitti kÔigist sissetulevatest UDP datagrammidest. (Kommentaarid olen lisanud koodi, et artiklit mitte koormata.)
Rohkem teavet filtri setsockopt sidumise kohta vt , ja oma filtrite kirjutamise kohta struct sock_fprog ilma abita tcpdump rÀÀgime jaotises .
Klassikaline BPF ja XXI sajand
BPF lisati Linuxisse 1997. aastal ja pĂŒsis kaua aega tööhobuse rollis libpcap ilma eriliste muudatusteta (Linuxi-spetsiifilised muudatused, muidugi, , kuid need ei muutnud globaalses pildis midagi). Esimesed tĂ”sised mĂ€rgid, et BPF hakkab arenema, ilmusid 2011. aastal, kui Eric Dumazet pakkus , mis lisas kernelisse Just In Time Compileri â tĂ”lkija BPF-i bytecode-i muundamiseks natiivseks x86_64 koodiks.
JIT kompilaator oli esimeseks muutuste ahelas: 2012. aastal oli vÔimalik kirjutada filtreid , kasutades BPF-i, jaanuaris 2013 ilmus moodul xt_bpf, mis vÔimaldas kirjutada reegleid iptables BPF-i abil, ja oktoobris 2013 ilmus ka moodul cls_bpf, vÔimaldades BPF-i abil liiklusklassifikaatoreid kirjutada.
Kuna vĂ”imalused, mille raamatukogu pakub, on piiratud (lihtne nĂ€ide: filtreer, mis on genereeritud libpcap vĂ”ib tagastada ainult kaks vÀÀrtust â 0 vĂ”i 0x40000) vĂ”i ei ole ĂŒldse kohaldatavad, nagu seccompi puhul. libpcap Tutvume BPF-i binaarfaili formaadiga, mis on vĂ€ga lihtne:
BPF-i programmeerimine oma kÀtega
16 8 8 32 | kood | jt | jf | k |
Iga kĂ€sk vĂ”tab 64 bitti, kus esimesed 16 bitti on kĂ€su kood, seejĂ€rel kaks 8-bitist lĂŒlitust,jt jf ja , ja 32 bitti argumendi jaoks,, mille tĂ€hendus muutub vastavalt kĂ€sklusele. NĂ€iteks kĂ€sk K, mis lĂ”petab programmi, omab koodi ret, ja tagastatav vÀÀrtus vĂ”etakse konstandist 6. C keeles esindatakse ĂŒhte BPF-i kĂ€sku struktuurina Kstruct sock_filter { __u16 kood; __u8 jt; __u8 jf; __u32 k; }
ja tervet programmi struktuurinastruct sock_fprog { unsigned short len; struct sock_filter *filter; }
Nii et me saame juba kirjutada programme (kÀskude koodid on meil juba teada). Nii vÀlja nÀeb filter 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, }; ip6 API-s :
progProgramm saame seaduseselt kasutada funktsioonis setsockopt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))
Programme masina koodina kirjutamine ei ole vĂ€ga mugav, kuid vahel on see vajalik (nĂ€iteks silumise, ĂŒksuste testide loomise, artiklite kirjutamise ajal jne). Mugavuse huvides mÀÀratakse failis<linux/filter.h> abil abimakrosid â sama nĂ€ide, mis eespool, vĂ”iks ĂŒmber kirjutada kui 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), }
Kuid ka seda varianti ei saa mugavaks nimetada. Nii arvasid ka Linuxi kerneli arendajad, seetÔttu vÔib kernelikataloogisttools/bpf Assembleri keel sarnaneb vÀga silumise vÀljundiga
, kuid lisaks saame mÀÀrata sĂŒmboolseid silte. NĂ€iteks programm, mis viskab kĂ”ik paketid, vĂ€lja arvatud TCP/IPv4: tcpdump$ cat /tmp/tcp-over-ipv4.bpf ldh [12] jne #0x800, drop ldb [23] jneq #6, drop ret #-1 drop: ret #0
Vaikimisi genereerib assembler koodi formaadisVaikimisi genereerib assembler koodi formaadis , ,..., meie TCP nÀite puhul saadakse
$ 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 programmeerijate mugavuse huvides vÔib kasutada teistsugust vÀljundformaati:
$ 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 },Seda teksti saab kopeerida tĂŒĂŒbi struktuuri mÀÀratlemisse struct sock_filter, nagu me kĂ€esoleva jaotise alguses tegime.
Linuxi ja netsniff-ng laiendused
Lisaks standardsetele BPF kĂ€skudele toetavad Linux ja tools/bpf/bpf_asm ka . Peamiselt teenivad kĂ€skud nĂ”utud struktuuri laagri vĂ€ljadele pÀÀsemiseks struct sk_buff, mis kirjeldab vĂ”rkpakkide struktuuri tuumas. Samas on ka teise tĂŒĂŒbi abikĂ€skud, nĂ€iteks ldw cpu laadib registrisse A tuuma funktsiooni kĂ€itamise tulemuse raw_smp_processor_id().(Uues BPF versioonis on neid mittestandardseid laiendusi laiendatud programmide paaride ja kernel helpers kogumi kaudu mĂ€lule, struktuuridele ning sĂŒndmuste genereerimisele.) Siin on huvitav nĂ€ide filterist, kus kopeerime kasutajaruumi ainult pakettide pĂ€ised, kasutades laiendust poff, koormuse offset:
ld poff
ret aBPF laiendusi ei saa kasutada tcpdump, aga see on hea pĂ”hjus tutvuda utiliitide paketiga , mis sisaldab, muu hulgas, ka tĂ€iustatud programmi netsniff-ng, mis, peale BPF-ga filtreerimise, sisaldab ka efektiivset liikluse genereerijat ja sageli paremat BPF assemblerit nimega tools/bpf/bpf_asmbpfc. Paketis on ĂŒsna pĂ”hjalik dokumentatsioon, vt ka artikli lĂ”pus olevad lingid.Nii et me oskame juba kirjutada BPF programme mistahes keerukusega ja oleme valmis vaatama uusi nĂ€iteid, esimene neist on tehnoloogia seccomp, mis vĂ”imaldab BPF filterite kaudu hallata mitmeid ja sĂŒsteemikutsungite argumendi komplekti, mis on selle protsessi ja tema jĂ€reltulijate jaoks kĂ€ttevĂ”imalik.
seccomp
Esimene seccomp versioon lisati tuuma 2005. aastal ning see ei olnud suurt populaarsust saavutanud, kuna pakkus ainult ĂŒht vĂ”imalust â piirata mitmeid sĂŒsteemikutsungite varustust, mis sellele protsessile on kĂ€ttevĂ”imalik:
sigreturn lugema, kirjutama, exit ja , kuid protsess, mis reegleid rikkus, surmatiSIGKILL SIGKILL. Kuid 2012. aastal lisati seccompi vĂ”imalus kasutada BPF filtreid, mis vĂ”imaldavad mÀÀrata arvukalt lubatud sĂŒsteemikutseid ning isegi kontrollida nende argumente. (Huvitav on, et Chrome oli ĂŒks esimesi, kes seda funktsionaalsust kasutas, ja praegu töötavad Chrome'i inimesed vĂ€lja KRSI mehhanismi, mis pĂ”hineb uuemal BPF versioonil ja vĂ”imaldab kohandada Linuxi turvamooduleid.) Lisadokumendi viidatud leidub artikli lĂ”pus.
Oluline on ĂŒles mĂ€rkida, et Habril on juba olnud artikleid seccompi kasutamise kohta, vĂ”ib-olla tahab keegi neid lugeda enne (vĂ”i selle asemel), kui liigub edasi jĂ€rgmistele alajaotustele. Artiklis toodud nĂ€ited seccompi kasutamisest nii 2007. aasta versiooni kui ka BPF kasutamise versiooni kohta (filtrid genereeritakse libseccompi abil), rÀÀgitakse seccompi seosest Dockeriga ning antakse palju kasulikke viiteid. Artiklis rÀÀgitakse eelkĂ”ige sellest, kuidas lisada sĂŒsteemikutsed mustadesse vĂ”i valgetesse nimekirjadesse systemd halduses oleva sĂŒsteemi jaoks.
Edasi vaatame, kuidas kirjutada ja laadida filtreid seccomp puhta C keele abil ja libseccompi raamatukogust vÀlja. libseccomp ja millised on iga variandi plussid ja miinused; lÔpuks vaatame, kuidas seccompi kasutab programm. strace.
Kirjutame ja laadime seccompi filtreid
Me oskame juba kirjutada BPF programme, seega vaatame esmalt seccomp'i programmiliidest. Filtri saab seadistada protsessi tasemel, kus kĂ”ik lasteprotsessid pĂ€rivad piirangud. See toimub sĂŒsteemikutsungi abil :
seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)kus &filter â see on viide juba tuttavale struktuurile struct sock_fprog, st BPF programmile.
Kuidas erinevad seccompi programmid soketiprogrammidest? Edastatava konteksti poolest. Sokettide puhul edastati meile mĂ€luala, mis sisaldas paketti, kuid seccompi puhul edastatakse meile struktuur tĂŒĂŒbiga
struct seccomp_data {
int nr;
__u32 arch;
__u64 instruction_pointer;
__u64 args[6];
};Siin nr â see on kĂ€ivitatava sĂŒsteemikutse number, arch â praegune arhitektuur (sellest allpool), args â kuni kuue sĂŒsteemikutsesse kuuluva argumendiga, ja instruction_pointer â see on viide kasutaja ruumis entsĂŒklilise kĂ€su jaoks, mis tegi selle sĂŒsteemikĂ”ne. Seega peame nĂ€iteks sĂŒsteemikĂ”ne numbri registreerimiseks ĂŒtlema A me peame ĂŒtlema
ldw [0]Seccomp programmide jaoks on olemas ka teisi eripĂ€ra, nĂ€iteks on juurdepÀÀs kontekstile vĂ”imalik ainult 32-bitise eraldatuse korral ja ei saa laadida poole sĂ”na vĂ”i bait â filtri laadimise katse ldh [0] sĂŒsteemikĂ”ne seccomp tagastab EINVAL. Laadimise filtrite kontrolli teostab funktsioon kernis. (Naljakana, algses commit'is, mis lisas seccomp funktsionaalsuse, unustati sellele funktsioonile anda loa kasutada instruktsiooni mod (jÀÀk) ja nĂŒĂŒd ei ole see seccomp BPF programmidele saadaval, kuna selle lisamine ABI.)
PĂ”himĂ”tteliselt teame juba kĂ”ike, et kirjutada ja lugeda seccomp programme. Tavaliselt on programmi loogika ĂŒles ehitatud nagu sĂŒsteemikĂ”nede must nimekiri vĂ”i valge nimekiri, nĂ€iteks programm
ld [0]
jeq #304, bad
jeq #176, bad
jeq #239, bad
jeq #279, bad
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
bad: ret #0kontrollib musta nimekirja neljast sĂŒsteemikĂ”nest numbritega 304, 176, 239, 279. Millised need sĂŒsteemikĂ”ned on? Me ei saa tĂ€pselt öelda, kuna me ei tea, millise arhitektuuri jaoks programm kirjutati. SeetĂ”ttu peavad seccomp autorid kĂ”iki programme alustama arhitektuuri kontrollimisega (praegune arhitektuur on kontekstis vĂ€lja toodud valdkonnas arch struktuurid struct seccomp_data). Arhitektuuri kontrollimisega vĂ”iks nĂ€ite algus vĂ€lja nĂ€ha nagu:
ld [4]
jne #0xc000003e, bad_arch ; SCMP_ARCH_X86_64ja siis saaksime meie sĂŒsteemikĂ”nede numbrid kindlad vÀÀrtused.
Kirjutame ja laadime seccomp filtreid kasutades libseccomp
Filtide kirjutamine masinkoodina vĂ”i BPF assembleri jaoks vĂ”imaldab tĂ€ielikku kontrolli tulemuse ĂŒle, kuid samas on mĂ”nikord eelistatavam omada kantavat ja/vĂ”i loetavat koodi. Sellega aitab meil raamatukogu , mis pakub standardset liidest mustade vĂ”i valgete filtrite kirjutamiseks.
Kirjutame nĂ€iteks programmi, mis kĂ€ivitab kasutaja valitud binaarfaili, seadistades eelnevalt musta nimekirja sĂŒsteemikĂ”nedest (programm on lihtsustatud parema loetavuse nimel, tĂ€ielikku varianti saab leida ):
#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]);
}Alguses defineerime massiivi sys_numbers ĂŒle 40 sĂŒsteemikĂ”ne numbri blokeerimiseks. Siis initsialiseerime konteksti ctx ja rÀÀgime teegile, et soovime lubada (SCMP_ACT_ALLOW) kĂ”iki sĂŒsteemi kĂ”nesid vaikimisi (mustade nimekirjade koostamine on lihtsam). SeejĂ€rel lisame ĂŒkshaaval kĂ”ik sĂŒsteemi kĂ”ned musta nimekirja. Vastuseks mustal nimekirjal olevale sĂŒsteemi kĂ”nele kĂŒsime SCMP_ACT_TRAP, sel juhul saadab seccomp protsessile signaali SIGSYS kirjeldusega, milline sĂŒsteemi kĂ”ne reegleid rikkus. LĂ”puks laadime programmi kĂ€rku kasutades seccomp_load, mis kompileerib programmi ja ĂŒhendab selle protsessiga sĂŒsteemi kĂ”ne abil seccomp(2).
Eduka kompileerimise jaoks tuleb programm lingida teegiga libseccomp, nÀiteks:
cc -std=c17 -Wall -Wextra -c -o seccomp_lib.o seccomp_lib.c
cc -o seccomp_lib seccomp_lib.o -lseccompNÀide edukast kÀivitamisest:
$ .\/seccomp_lib echo ok
okNĂ€ide blokeeritud sĂŒsteemi kĂ”nest:
$ sudo .\/seccomp_lib mount -t bpf bpf \/tmp
Halb sĂŒsteemi kĂ”neKasutame strace, et teada saada rohkem:
$ 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} ---
+++ SIGSYS poolt tapetud (mÀlukogumine) +++
Halb sĂŒsteemi kĂ”nekust saame teada, et programm lĂ”petati keelatud sĂŒsteemi kĂ”ne kasutamise tĂ”ttu mount(2).
KokkuvĂ”ttes oleme kirjutanud filtri kasutades teeki libseccomp, mahtudes mittetĂŒĂŒpilisse koodi nelja rida. Ălaltoodud nĂ€ites vĂ”ib sĂŒsteemi kĂ”nede arvul olla oluline mĂ”ju tĂ€itmise ajale, kuna kontrollimine on lihtsalt loendite vĂ”rdlemine. Optimeerimiseks on hiljuti libseccompi lisatud , mis lisab toetuse filtri atribuudile SCMP_FLTATR_CTL_OPTIMIZE. Kui seadistate selle atribuudiks 2, muudetakse filter binaarse otsingu programmina.
Kui soovite nĂ€ha, kuidas töötavad binaarse otsinguga filtrid, vaadake , mis genereerib selliseid programmi assembler BPF sĂŒsteemi kĂ”nede numbrite kogumi pĂ”hjal, nĂ€iteks:
$ 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 #0Ei saa kirjutada midagi oluliselt kiiremat, kuna BPF programmid ei saa teha hargnemisi (me ei saa teha nÀiteks jmp A vÔi jmp [label+X]) ja seetÔttu on kÔik hargnemised staatilised.
seccomp ja strace
KĂ”ik teavad utiliiti strace â asendamatu tööriist protsesside kĂ€itumise uurimisel Linuxis. Kuid paljud on ka kuulnud selle tööriista kasutamise korral. Asi on selles, et strace on rakendatud ptrace(2), kuid selles mehhanismis ei saa me mÀÀrata, millistel tĂ€pselt sĂŒsteemi kutsungitel me protsessi peame peatama, st nĂ€iteks kĂ€sud
$ time strace du /usr/share/ >/dev/null 2>&1
real 0m3.081s
user 0m0.531s
sys 0m2.073sja
$ time strace -e open du /usr/share/ >/dev/null 2>&1
real 0m2.404s
user 0m0.193s
sys 0m1.800stöötavad enam-vĂ€hem sama ajaga, kuigi teises variandis tahame jĂ€lgida ainult ĂŒhte sĂŒsteemi kutsungit.
Uus valik --seccomp-bpf, lisatud strace versioonis 5.3, vĂ”imaldab protsessi kiirusel korduvalt suureneda ja ĂŒhe sĂŒsteemi kutsungi jĂ€lgimise kĂ€ivitamise aeg on nĂŒĂŒd vĂ”rreldav tavalise kĂ€ivitamise ajaga:
$ 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(Siin on loomulikult vĂ€ike petmine, et me ei jĂ€lgi selle kĂ€su peamist sĂŒsteemi kutsungit. Kui me jĂ€lgiksime nĂ€iteks newfsstat, siis strace peaks töötama sama aeglaselt kui ilma --seccomp-bpf.)
Kuidas see valik toimib? Ilma selleta strace ĂŒhendub protsessiga ja kĂ€ivitab selle PTRACE_SYSCALL. Kui hallatav protsess kĂ€ivitab (mille tahes) sĂŒsteemi kutsungi, antakse juhtimine strace, mis vaatab sĂŒsteemi kutsungi argumente ja kĂ€ivitab selle PTRACE_SYSCALL. MĂ”ne aja pĂ€rast lĂ”petab protsess sĂŒsteemi kutsungi ja vĂ€ljudes antakse juhtimine jĂ€lle strace, mis vaatab tagastatud vÀÀrtusi ja kĂ€ivitab protsessi PTRACE_SYSCALL, jne.

Kuid seccompi abil saab seda protsessi optimeerida just nii, nagu me soovime. Nimelt, kui tahame vaadata ainult sĂŒsteemi kutsungit X, saame kirjutada BPF filtri, mis X tagastab vÀÀrtuse SECCOMP_RET_TRACE, ja mittesoovitavate kutsungite jaoks â SECCOMP_RET_ALLOW:
ld [0]
jneq #X, ignore
trace: ret #0x7ff00000
ignore: ret #0x7fff0000Sellisel juhul strace kĂ€ivitab alguses protsessi kui PTRACE_CONT, iga sĂŒsteemi kutsungi korral töötab meie filter, kui sĂŒsteemi kutsung ei X, siis protsess jĂ€tkab tööd, kuid kui see X, siis seccomp edastab juhtimise strace, mis vaatab argumente ja kĂ€ivitab protsessi nagu PTRACE_SYSCALL (kuna seccompis ei ole vĂ”imalik programmi kĂ€ivitada sĂŒsteemi kutsungi vĂ€ljumisel). Kui sĂŒsteemi kutsung naaseb, strace kĂ€ivitab protsessi uuesti PTRACE_CONT ja ootab uusi teateid seccompilt.

valiku kasutamisel --seccomp-bpf on kaks piirangut. Esiteks ei saa ĂŒhendada juba toimivale protsessile (valik -p programmide strace), kuna seda ei toetata seccomp. Teiseks, puudub vĂ”imalus ei vaadata alaprotsesse, kuna seccomp filtrid pĂ€randuvad kĂ”igile alaprotsessidele ilma vĂ”imaluseta seda vĂ€lja lĂŒlitada.
Veidi rohkem ĂŒksikasju selle kohta, kuidas tĂ€pselt strace töötab seccomp saab teada meie viimasest aruandest Liigume nĂŒĂŒd tagasi vĂ”rkude maailma.
xt_bpf
Taust: ammu, 2007. aastal, murdis tuuma
xt_u32 moodul netfilter jaoks. See kirjutati sarnases stiilis nagu veelgi vana liikluse klassifikaator cls_u32 ja vÔimaldas kirjutada suvalisi binaarreegleid iptables'i abil, kasutades jÀrgmisi lihtsaid operatsioone: laadida 32 bitti paketist ja teha neile rida aritmeetilisi operatsioone. NÀiteks, sudo iptables -A INPUT -m u32 --u32 "6&0xFF=1" -j LOG --log-prefix "seen-by-xt_u32"
Laadib 32 bitti IP-pealkirjast, alustades nihkest 6, ja rakendab neile maske0xFF (vĂ”tab alumise baidi). See on IP-pealkirja piirkond ja me vĂ”rdleme seda 1-ga (ICMP). Ăhes reeglites saab koos kombineerida palju kontrolle ning samuti saab teha operatsiooni â liikuda X bitti paremale. NĂ€iteks reegel protocol iptables -m u32 --u32 "6&0xFF=0x6 && 0>>22&0x3C@4=0x29" @ kontrollib, kas TCP jĂ€rjestusnumber on
. Ma ei hakka sisse minema rohkem ĂŒksikasjadesse, kuna on juba selge, et kĂ€sitsi selliste reeglite kirjutamine ei ole just mugav. ArtiklisBPF â unustatud baitkood 0x29, on mitmeid linke nĂ€idiste ja reeglite genereerimise kohta Alates 2013. aastast saab mooduli asemel kasutada BPF-pĂ”hist moodulit. netfilter jaoks. See kirjutati sarnases stiilis nagu veelgi vana liikluse klassifikaatorKuni siia lugenud, peaks pĂ”himĂ”te olema selge: kĂ€ivitada BPF baitkood iptables'i reeglite puhul. Uue reegli loomine on vĂ”imalik nĂ€iteks nii:
iptables -A INPUT -m bpf --bytecode -j LOG netfilter jaoks. See kirjutati sarnases stiilis nagu veelgi vana liikluse klassifikaator saab kasutada BPF-pÔhist moodulit xt_bpfon assemblatori vÀljundformaat bpf_asm
vaikimisi, nĂ€iteks,siin $ 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 Selles nĂ€ites filtreerime kĂ”iki UDP pakette. BPF programmi kontekst moodulis , viitab loomulikult paketi andmetele, iptables'i puhul â IPv4 pealkirja algusele. BPF programmi tagastatav vÀÀrtus boolean
tÀhendab, et pakett ei vastanud.. xt_bpf. , kus false .
Selge, et moodul xt_bpf toetab keerukamaid filtre kui ĂŒlaltoodud nĂ€ites. Vaatame tĂ”elisi nĂ€iteid ettevĂ”ttelt Cloudflare. Kuni hiljutise ajani kasutasid nad moodulit xt_bpf DDoS rĂŒnnakute kaitseks. Artiklis rÀÀgivad nad, kuidas (ja miks) nad genereerivad BPF filtreid ja publitseerivad linke tööriistade komplektile selliste filtrite loomise jaoks. NĂ€iteks tööriista abil bpfgen vĂ”ib luua BPF programmi, mis sobitab DNS-pĂ€ringu nime 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 #0Programmis laadime me kÔigepealt registrisse X stringi alguse aadressi x04habrx03comx00 UDP-datagrammi sees ja seejÀrel kontrollime pÀringut: 0x04686162 "x04hab" jne.
Hiljem avaldas Cloudflare p0f kompilaatori koodi -> BPF. Artiklis rÀÀgivad nad, mis on p0f ja kuidas muuta p0f allkirju BPF-iks:
$ ./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,
...Praegu ei kasuta Cloudflare enam xt_bpf, kuna nad on kolinud XDP-le â ĂŒks variant uut BPF versiooni kasutada, vt. .
cls_bpf
Viimane klassikalise BPF nĂ€ide tuumas on klassifikaator cls_bpf Linuxi liikluse kontrollimise alamsĂŒsteemi jaoks, mis lisati Linuxi 2013. aasta lĂ”pus ja asendas kontseptuaalselt vana ja vĂ”imaldas kirjutada suvalisi binaarreegleid iptables'i abil, kasutades jĂ€rgmisi lihtsaid operatsioone: laadida 32 bitti paketist ja teha neile rida aritmeetilisi operatsioone. NĂ€iteks,.
Me ei kavatse praegu seda tööd kirjeldada cls_bpf, kuna klassikalise BPF-i tundmise seisukohalt ei anna see meile midagi â oleme juba tuttavad kogu funktsionaalsusega. Lisaks, jĂ€rgnevates artiklites, mis kĂ€sitlevad laiendatud BPF-i, kohtume me selle klassifikaatoriga veel korduvalt.
Veel ĂŒks pĂ”hjus, miks mitte rÀÀkida klassikalise BPF-i kasutamisest on see, et vĂ”rreldes laiendatud BPF-iga on selle puhul rakendatavuse ulatus oluliselt kitsenenud: klassikalised programmid ei saa muuta pakettide sisu ja ei saa salvestada olekut kutsungite vahel. cls_bpf Nii et on aeg hĂŒvasti jĂ€tta klassikalisest BPF-ist ja vaadata tulevikku.
HĂŒvasti klassikalise BPF-iga
HĂŒvasti klassikaline BPF
Oleme vaadanud, kuidas 1990. aastate alguses vĂ€lja töötatud BPF-tehnoloogia on edukalt kestnud veerand sajandit ja leidnud uusi rakendusi. Kuid sarnaselt siirdega virnastatud masinatelt RISC-ile, mis andis tĂ”uke klassikalise BPF-i vĂ€ljatöötamiseks, leidis 2000ndatel aset ĂŒleminek 32-bitiselt 64-bitisele arhitektuurile, mistĂ”ttu klassikaline BPF hakkas ajale jalgu jÀÀma. Lisaks on klassikalise BPF-i vĂ”imalused tugevalt piiratud ning lisaks vananenud arhitektuurile puudub meil vĂ”imalus salvestada olekut BPF-programmide vahel, ei ole vĂ”imalik otseselt suhelda kasutajaga ega suhtelda tuumaga, vĂ€lja arvatud piiratud arvu struktuuri vĂ€ljade lugemine. sk_buff ja kĂ€ivitada lihtsamaid abifunktsioone, ei saa pakettide sisu muuta ega neid suunata.
Tegelikult on praegu Linuxis klassikalisest BPF-ist jÀÀnud jÀrgi ainult API-liides, samas kui kogu klassikalised programmid, olgu need siis socket-filterid vÔi seccomp-filterid, tÔlgitakse automaatselt uude vormingusse, Extended BPF. (Kuidas seda tÀpselt tehakse, kirjeldame jÀrgmisel artiklil.)
Uuele arhitektuurile ĂŒleminek algas 2013. aastal, kui Aleksei Starovoitov pakkus vĂ€lja BPF-i vĂ€rskendamise skeemi. 2014. aastal hakkasid vastavad plaastrid tuumas. Nii palju tean, et algselt oli plaanis vaid optimeerida arhitektuuri ja JIT-kompileerijat efektiivsemaks tööks 64-bitistel masinatel, kuid selle asemel andsid need optimeerimised alguse uuele peatĂŒkile Linuxi arenduses.
Uurivad artiklid selles sarjas rÀÀgivad uue tehnoloogia arhitektuurist ja rakendustest, mis algselt oli tuntud kui internal BPF, seejĂ€rel extended BPF ning nĂŒĂŒd lihtsalt BPF.
Viidatud lingid
- Steven McCanne ja Van Jacobson, "The BSD Packet Filter: Uus arhitektuur kasutaja tasemel pakettide pĂŒĂŒdmiseks",
https://www.tcpdump.org/papers/bpf-usenix93.pdf - Steven McCanne, "libpcap: Arhitektuur ja optimeerimisvĂ€gitegu pakettide pĂŒĂŒdmiseks",
https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf tcpdump,libpcap:- .
- BPF â unustatud baitkood:
https://blog.cloudflare.com/bpf-the-forgotten-bytecode/ - BPF Tööriista tutvustus:
https://blog.cloudflare.com/introducing-the-bpf-tools/ bpf_cls:http://man7.org/linux/man-pages/man8/tc-bpf.8.html- Seccomp ĂŒlevaade:
https://lwn.net/Articles/656307/ https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst- Paul Chaignon, "strace âseccomp-bpf: pilguheit kapoti alla",
https://fosdem.org/2020/schedule/event/debugging_strace_bpf/ netsniff-ng:http://netsniff-ng.org/
Allikas: habr.com
