BPF mazajiem, nulles daļa: klasiskais BPF

Berkeley Packet Filters (BPF) ir Linux kodola tehnoloÄ£ija, kas jau vairākus gadus atrodas angļu valodas tehnoloÄ£iju publikāciju pirmajās lapās. Konferences ir piepildÄ«tas ar ziņojumiem par BPF izmantoÅ”anu un attÄ«stÄ«bu. Deivids Millers, Linux tÄ«kla apakÅ”sistēmas uzturētājs, sauc savu runu vietnē Linux Plumbers 2018 "Å Ä« runa nav par XDP" (XDP ir viens BPF lietoÅ”anas gadÄ«jums). Brendans Gregs uzstājas ar sarunām Linux BPF lielvaras. Toke HĆøiland-JĆørgensen smejaska kodols tagad ir mikrokodolu. Tomass Grafs popularizē ideju, ka BPF ir javascript kodolam.

Joprojām nav sistemātiska BPF apraksta par HabrĆ©, un tāpēc rakstu sērijā es mēģināŔu runāt par tehnoloÄ£ijas vēsturi, aprakstÄ«t arhitektÅ«ru un izstrādes rÄ«kus, kā arÄ« ieskicēt BPF izmantoÅ”anas un prakses jomas. Å is sērijas raksts, nulle, stāsta par klasiskā BPF vēsturi un arhitektÅ«ru, kā arÄ« atklāj tā darbÄ«bas principu noslēpumus. tcpdump, seccomp, strace, un daudz vairāk.

BPF izstrādi kontrolē Linux tÄ«kla kopiena, galvenās esoŔās BPF lietojumprogrammas ir saistÄ«tas ar tÄ«kliem un tāpēc ar atļauju @eukariots, seriālu nosaucu par ā€œBPF mazajiemā€, par godu lieliskajam seriālam "TÄ«kli mazajiem".

ÄŖss kurss BPF vēsturē (c)

MÅ«sdienu BPF tehnoloÄ£ija ir uzlabota un paplaÅ”ināta vecās tehnoloÄ£ijas versija ar tādu paÅ”u nosaukumu, ko tagad sauc par klasisko BPF, lai izvairÄ«tos no neskaidrÄ«bām. Pamatojoties uz klasisko BPF, tika izveidota labi zināma utilÄ«ta tcpdump, mehānisms seccomp, kā arÄ« mazāk zināmiem moduļiem xt_bpf par iptables un klasifikators cls_bpf. MÅ«sdienu Linux klasiskās BPF programmas tiek automātiski pārtulkotas jaunajā formā, tomēr no lietotāja viedokļa API ir palikusi vietā un joprojām tiek atrasti jauni klasiskā BPF lietojumi, kā redzēsim Å”ajā rakstā. Å Ä« iemesla dēļ, kā arÄ« tāpēc, ka, sekojot lÄ«dzi klasiskā BPF attÄ«stÄ«bas vēsturei operētājsistēmā Linux, kļūs skaidrāks, kā un kāpēc tas attÄ«stÄ«jās savā modernajā formā, es nolēmu sākt ar rakstu par klasisko BPF.

PagājuŔā gadsimta astoņdesmito gadu beigās inženieri no slavenās Lorensa Bērklija laboratorijas sāka interesēties par jautājumu, kā pareizi filtrēt tÄ«kla paketes aparatÅ«rā, kas bija moderna pagājuŔā gadsimta astoņdesmito gadu beigās. FiltrÄ“Å”anas pamatideja, kas sākotnēji tika ieviesta CSPF (CMU/Stanford Packet Filter) tehnoloÄ£ijā, bija pēc iespējas agrāk filtrēt nevajadzÄ«gās paketes, t.i. kodola telpā, jo tas ļauj izvairÄ«ties no nevajadzÄ«gu datu kopÄ“Å”anas lietotāja telpā. Lai nodroÅ”inātu izpildlaika droŔību lietotāja koda palaiÅ”anai kodola telpā, tika izmantota smilÅ”kastes virtuālā maŔīna.

Tomēr esoÅ”o filtru virtuālās maŔīnas bija paredzētas darbam ar steku balstÄ«tām iekārtām, un tās nedarbojās tik efektÄ«vi jaunākās RISC iekārtās. Rezultātā ar Berkeley Labs inženieru pÅ«lēm tika izstrādāta jauna BPF (Berkeley Packet Filters) tehnoloÄ£ija, kuras virtuālās maŔīnas arhitektÅ«ra tika izstrādāta, pamatojoties uz Motorola 6502 procesoru - tādu pazÄ«stamu produktu kā darba zirgu. Apple II vai NES. Jaunā virtuālā maŔīna, salÄ«dzinot ar esoÅ”ajiem risinājumiem, palielināja filtra veiktspēju desmitiem reižu.

BPF maŔīnu arhitektūra

Darbā iepazÄ«simies ar arhitektÅ«ru, analizējot piemērus. Tomēr, lai sāktu, pieņemsim, ka iekārtai bija divi lietotājam pieejami 32 bitu reÄ£istri, akumulators. A un indeksu reÄ£istrs X, 64 baiti atmiņa (16 vārdi), kas pieejama rakstÄ«Å”anai un turpmākai lasÄ«Å”anai, un neliela komandu sistēma darbam ar Å”iem objektiem. Programmās bija pieejami arÄ« lēcienu norādÄ«jumi nosacÄ«to izteiksmju ievieÅ”anai, taču, lai garantētu programmas savlaicÄ«gu izpildi, lēcienus varēja veikt tikai uz priekÅ”u, t.i., konkrēti, bija aizliegts veidot cilpas.

Vispārējā maŔīnas iedarbināŔanas shēma ir Ŕāda. Lietotājs izveido programmu BPF arhitektÅ«rai un, izmantojot daži kodola mehānisms (piemēram, sistēmas izsaukums), ielādē programmu un savieno ar to dažiem notikumu Ä£eneratoram kodolā (piemēram, notikums ir nākamās paketes ienākÅ”ana tÄ«kla kartē). Kad notiek notikums, kodols palaiž programmu (piemēram, tulkā), un maŔīnas atmiņa atbilst dažiem kodola atmiņas apgabals (piemēram, ienākoŔās paketes dati).

Lai sāktu aplÅ«kot piemērus, pietiks ar iepriekÅ”minēto: pēc nepiecieÅ”amÄ«bas iepazÄ«simies ar sistēmu un komandu formātu. Ja vēlaties nekavējoties izpētÄ«t virtuālās maŔīnas komandu sistēmu un uzzināt par visām tās iespējām, varat izlasÄ«t oriÄ£inālo rakstu BSD pakeÅ”u filtrs un/vai faila pirmo pusi Dokumentācija/tÄ«kls/filter.txt no kodola dokumentācijas. Turklāt jÅ«s varat izpētÄ«t prezentāciju libpcap: ArhitektÅ«ras un optimizācijas metodika pakeÅ”u uztverÅ”anai, kurā Makkens, viens no BPF autoriem, stāsta par radÄ«Å”anas vēsturi libpcap.

Tagad mēs turpinām apsvērt visus nozÄ«mÄ«gos piemērus klasiskā BPF izmantoÅ”anai operētājsistēmā Linux: tcpdump (libpcap), seccomp, xt_bpf, cls_bpf.

tcpdump

BPF izstrāde tika veikta paralēli pakeÅ”u filtrÄ“Å”anas priekÅ”gala izstrādei - plaÅ”i pazÄ«stamai utilÄ«tai. tcpdump. Un, tā kā Å”is ir vecākais un slavenākais klasiskā BPF izmantoÅ”anas piemērs, kas pieejams daudzās operētājsistēmās, mēs sāksim ar to tehnoloÄ£iju izpēti.

(Es izmantoju visus Å”ajā rakstā minētos piemērus operētājsistēmā Linux 5.6.0-rc6. Dažu komandu izvade ir rediģēta, lai nodroÅ”inātu labāku lasāmÄ«bu.)

Piemērs: IPv6 pakeÅ”u novēroÅ”ana

Iedomāsimies, ka mēs vēlamies apskatÄ«t visas IPv6 paketes interfeisā eth0. Lai to izdarÄ«tu, mēs varam palaist programmu tcpdump ar vienkārÅ”u filtru ip6:

$ sudo tcpdump -i eth0 ip6

Šajā gadījumā, tcpdump apkopo filtru ip6 BPF arhitektūras baitkodā un nosūtiet to kodolam (skatiet sīkāku informāciju sadaļā Tcpdump: tiek ielādēts). Ielādētais filtrs tiks palaists katrai paketei, kas iet caur saskarni eth0. Ja filtrs atgriež vērtību, kas nav nulle n, tad līdz n paketes baiti tiks kopēti lietotāja telpā, un mēs to redzēsim izvadē tcpdump.

BPF mazajiem, nulles daļa: klasiskais BPF

Izrādās, ka mēs varam viegli noskaidrot, kurÅ” baitkods tika nosÅ«tÄ«ts kodolam tcpdump ar palÄ«dzÄ«bu tcpdump, ja mēs to palaižam ar opciju -d:

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

Nulles rindā mēs izpildām komandu ldh [12], kas apzÄ«mē ā€œielādēt reÄ£istrā A puse vārda (16 biti), kas atrodas adresē 12ā€, un jautājums ir tikai par to, kāda veida atmiņu mēs adresējam? Atbilde ir tāda, ka plkst x sākas (x+1)analizētās tÄ«kla paketes baits. Mēs lasām paketes no Ethernet interfeisa eth0un tas nozÄ«mēka pakete izskatās Ŕādi (vienkārŔības labad mēs pieņemam, ka paketē nav VLAN tagu):

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

Tātad pēc komandas izpildÄ«Å”anas ldh [12] reÄ£istrā A bÅ«s lauks Ether Type ā€” Å”ajā Ethernet kadrā pārsÅ«tÄ«tās paketes veids. 1. rindā mēs salÄ«dzinām reÄ£istra saturu A (iepakojuma veids) c 0x86ddun tas un tur ir MÅ«s interesējoÅ”s veids ir IPv6. 1. rindā papildus salÄ«dzināŔanas komandai ir vēl divas kolonnas - jt 2 Šø jf 3 ā€” atzÄ«mes, uz kurām jāiet, ja salÄ«dzinājums ir veiksmÄ«gs (A == 0x86dd) un neveiksmÄ«gi. Tātad veiksmÄ«gā gadÄ«jumā (IPv6) mēs ejam uz 2. rindu, bet neveiksmÄ«gā gadÄ«jumā - uz 3. rindu. 3. rindā programma beidzas ar kodu 0 (nekopēt paketi), 2. rindā programma beidzas ar kodu. 262144 (kopējiet man maksimāli 256 kilobaitu paketi).

Sarežģītāks piemērs: mēs apskatām TCP paketes pēc mērķa porta

ApskatÄ«sim, kā izskatās filtrs, kas kopē visas TCP paketes ar mērÄ·a portu 666. Mēs apsvērsim IPv4 gadÄ«jumu, jo IPv6 gadÄ«jums ir vienkārŔāks. Pēc Ŕī piemēra izpētes varat pats izpētÄ«t IPv6 filtru kā vingrinājumu (ip6 and tcp dst port 666) un filtrs vispārējam gadÄ«jumam (tcp dst port 666). Tātad mÅ«s interesējoÅ”ais filtrs izskatās Ŕādi:

$ 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

Mēs jau zinām, ko dara rindas 0 un 1. 2. rindā mēs jau esam pārbaudÄ«juÅ”i, vai Ŕī ir IPv4 pakete (ētera tips = 0x800) un ielādējiet to reÄ£istrā A 24. paketes baits. MÅ«su iepakojums izskatās

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

kas nozīmē, ka mēs ielādējam reģistrā A IP galvenes lauks Protocol, kas ir loģiski, jo mēs vēlamies kopēt tikai TCP paketes. Mēs salīdzinām protokolu ar 0x6 (IPPROTO_TCP) 3. rindā.

4. un 5. rindā mēs ielādējam pusvārdus, kas atrodas adresē 20, un izmantojam komandu jset pārbaudiet, vai ir iestatÄ«ts kāds no trim karogi - izsniegtās maskas nēsāŔana jset tiek notÄ«rÄ«ti trÄ«s nozÄ«mÄ«gākie biti. Divi no trim bitiem norāda, vai pakete ir daļa no sadrumstalotas IP paketes, un, ja tā, vai tā ir pēdējais fragments. TreÅ”ais bits ir rezervēts, un tam jābÅ«t nullei. Mēs nevēlamies pārbaudÄ«t ne nepilnÄ«gas, ne bojātas paketes, tāpēc mēs pārbaudām visus trÄ«s bitus.

6. rinda ir visinteresantākā Å”ajā sarakstā. Izteiksme ldxb 4*([14]&0xf) nozÄ«mē, ka mēs ielādējam reÄ£istrā X vismazāk nozÄ«mÄ«gie četri paketes piecpadsmitā baita biti, kas reizināti ar 4. Piecpadsmitā baita vismazāk nozÄ«mÄ«gie četri biti ir lauks Interneta galvenes garums IPv4 galvene, kas saglabā galvenes garumu vārdos, tāpēc jums pēc tam jāreizina ar 4. Interesanti, ka izteiksme 4*([14]&0xf) ir apzÄ«mējums Ä«paÅ”ai adresācijas shēmai, ko var izmantot tikai Å”ajā formā un tikai reÄ£istram X, t.i. mēs arÄ« nevaram teikt ldb 4*([14]&0xf) nedz ldxb 5*([14]&0xf) (mēs varam norādÄ«t tikai citu nobÄ«di, piemēram, ldxb 4*([16]&0xf)). Skaidrs, ka Ŕī adresācijas shēma tika pievienota BPF tieÅ”i tāpēc, lai saņemtu X (indeksa reÄ£istrs) IPv4 galvenes garums.

Tātad 7. rindā mēs cenÅ”amies ielādēt pusi vārda plkst (X+16). Atceroties, ka 14 baitus aizņem Ethernet galvene, un X satur IPv4 galvenes garumu, mēs to saprotam A TCP mērÄ·a ports ir ielādēts:

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

Visbeidzot, 8. rindā mēs salīdzinām galamērķa portu ar vēlamo vērtību un 9. vai 10. rindā atgriežam rezultātu - vai kopēt paketi vai nē.

Tcpdump: tiek ielādēts

IepriekŔējos piemēros mēs Ä«paÅ”i nerunājām par to, kā tieÅ”i mēs ielādējam BPF baitu kodu kodolā pakeÅ”u filtrÄ“Å”anai. VispārÄ«gi runājot, tcpdump pārnests uz daudzām sistēmām un darbam ar filtriem tcpdump izmanto bibliotēku libpcap. ÄŖsumā, lai ievietotu filtru saskarnē, izmantojot libpcap, jums jāveic Ŕādas darbÄ«bas:

Lai redzētu, kā darbojas pcap_setfilter ieviests operētājsistēmā Linux, mēs izmantojam strace (dažas rindas ir noņemtas):

$ 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
...

Pirmajās divās izvades rindās mēs izveidojam neapstrādāta ligzda lai nolasÄ«tu visus Ethernet kadrus un saistÄ«tu to ar interfeisu eth0. no mÅ«su pirmais piemērs mēs zinām, ka filtrs ip sastāvēs no četrām BPF instrukcijām, un treÅ”ajā rindā mēs redzam, kā izmantot Å”o opciju SO_ATTACH_FILTER sistēmas zvans setsockopt mēs ielādējam un pievienojam filtru ar garumu 4. Å is ir mÅ«su filtrs.

Ir vērts atzÄ«mēt, ka klasiskajā BPF filtra ielāde un pievienoÅ”ana vienmēr notiek kā atomu darbÄ«ba, un jaunajā BPF versijā programmas ielāde un piesaiste notikumu Ä£eneratoram tiek atdalÄ«ta laikā.

Slēptā Patiesība

Nedaudz pilnīgāka izvades versija izskatās Ŕādi:

$ 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
...

Kā minēts iepriekÅ”, mēs ielādējam un pievienojam filtru 5. lÄ«nijas ligzdai, bet kas notiek 3. un 4. lÄ«nijā? Izrādās, ka Å”is libpcap rÅ«pējas par mums - lai mÅ«su filtra izvadā nebÅ«tu paketes, kas to neapmierina, bibliotēka savieno manekens filtrs ret #0 (atmest visas paketes), pārslēdz ligzdu uz nebloÄ·Ä“Å”anas režīmu un mēģina atņemt visas paketes, kas varētu palikt no iepriekŔējiem filtriem.

Kopumā, lai filtrētu pakotnes operētājsistēmā Linux, izmantojot klasisko BPF, jums ir jābūt filtram tādas struktūras formā kā struct sock_fprog un atvērta ligzda, pēc kuras filtru var pievienot ligzdai, izmantojot sistēmas zvanu setsockopt.

Interesanti, ka filtru var piestiprināt pie jebkuras ligzdas, ne tikai neapstrādātā. Å eit piemērs programma, kas nogriež visus ienākoÅ”os UDP datagrammus, izņemot pirmos divus baitus. (Kodā pievienoju komentārus, lai nepārblÄ«vētu rakstu.)

SÄ«kāka informācija par lietoÅ”anu setsockopt par filtru pievienoÅ”anu sk ligzda (7), bet gan par savu filtru rakstÄ«Å”anu, piemēram struct sock_fprog bez palÄ«dzÄ«bas tcpdump parunāsim sadaļā BPF programmÄ“Å”ana ar savām rokām.

Klasiskais BPF un XNUMX. gs

BPF tika iekļauts Linux 1997. gadā un ilgu laiku ir bijis darba zirgs libpcap bez Ä«paŔām izmaiņām (protams, Linux specifiskas izmaiņas, Mēs bijām, taču tie nemainÄ«ja globālo ainu). Pirmās nopietnās pazÄ«mes, ka BPF attÄ«stÄ«sies, parādÄ«jās 2011. gadā, kad Ēriks Dumazē ierosināja plāksteris, kas kodolam pievieno Just In Time Compiler ā€” tulkotāju BPF baitkoda konvertÄ“Å”anai uz vietējo. x86_64 kodu.

JIT kompilators bija pirmais pārmaiņu ķēdē: 2012. gadā parādÄ«jās spēja rakstÄ«t filtrus seccomp, izmantojot BPF, 2013. gada janvārÄ« bija piebilda modulis xt_bpf, kas ļauj rakstÄ«t noteikumus priekÅ” iptables ar BPF palÄ«dzÄ«bu, un 2013. gada oktobrÄ« bija piebilda arÄ« modulis cls_bpf, kas ļauj rakstÄ«t trafika klasifikatorus, izmantojot BPF.

DrÄ«zumā visus Å”os piemērus apskatÄ«sim sÄ«kāk, taču vispirms mums noderēs iemācÄ«ties rakstÄ«t un kompilēt patvaļīgas programmas BPF, jo bibliotēkas nodroÅ”inātās iespējas libpcap ierobežots (vienkārÅ”s piemērs: izveidots filtrs libpcap var atgriezt tikai divas vērtÄ«bas - 0 vai 0x40000) vai parasti, tāpat kā seccomp gadÄ«jumā, nav piemērojamas.

BPF programmÄ“Å”ana ar savām rokām

Iepazīsimies ar BPF instrukciju bināro formātu, tas ir ļoti vienkārŔi:

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

Katra instrukcija aizņem 64 bitus, no kuriem pirmie 16 biti ir instrukcijas kods, tad ir divi astoņu bitu ievilkumi, jt Šø jf, un 32 biti argumentam K, kuras mērÄ·is dažādās komandās ir atŔķirÄ«gs. Piemēram, komanda ret, kas pārtrauc programmu, ir kods 6, un atgrieÅ”anas vērtÄ«ba tiek ņemta no konstantes K. C valodā viena BPF instrukcija tiek attēlota kā struktÅ«ra

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

un visa programma ir struktūras formā

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

Tādējādi mēs jau varam rakstīt programmas (piemēram, mēs zinām instrukciju kodus no [1]). Šādi izskatīsies filtrs ip6 no mūsu pirmais piemērs:

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,
};

programma prog mēs varam legāli izmantot zvanā

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

Programmu rakstÄ«Å”ana maŔīnkodu veidā nav Ä«paÅ”i ērta, bet dažreiz tas ir nepiecieÅ”ams (piemēram, atkļūdoÅ”anai, vienÄ«bu testu izveidoÅ”anai, rakstu rakstÄ«Å”anai par HabrĆ© utt.). ĒrtÄ«bas labad failā <linux/filter.h> palÄ«gmakro ir definēti ā€” to paÅ”u piemēru kā iepriekÅ” var pārrakstÄ«t kā

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),
}

Tomēr Ŕī iespēja nav Ä«paÅ”i ērta. Tas ir tas, ko Linux kodola programmētāji argumentēja, un tāpēc direktorijā tools/bpf kodolos varat atrast montētāju un atkļūdotāju darbam ar klasisko BPF.

Montāžas valoda ir ļoti lÄ«dzÄ«ga atkļūdoÅ”anas izvadei tcpdump, bet papildus varam norādÄ«t simboliskas etiÄ·etes. Piemēram, Å”eit ir programma, kas atmet visas paketes, izņemot TCP/IPv4:

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

Pēc noklusējuma montētājs Ä£enerē kodu Ŕādā formātā <ŠŗŠ¾Š»ŠøчŠµŃŃ‚Š²Š¾ ŠøŠ½ŃŃ‚Ń€ŃƒŠŗцŠøŠ¹>,<code1> <jt1> <jf1> <k1>,..., mÅ«su piemēram ar TCP tā bÅ«s

$ 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 programmētāju ērtībām var izmantot citu izvades formātu:

$ 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 },

Å o tekstu var iekopēt tipa struktÅ«ras definÄ«cijā struct sock_filter, kā mēs to darÄ«jām Ŕīs sadaļas sākumā.

Linux un netsniff-ng paplaŔinājumi

Papildus standarta BPF, Linux un tools/bpf/bpf_asm atbalsts un nestandarta komplekts. BÅ«tÄ«bā instrukcijas tiek izmantotas, lai piekļūtu struktÅ«ras laukiem struct sk_buff, kas apraksta tÄ«kla paketi kodolā. Tomēr ir arÄ« cita veida palÄ«ga instrukcijas, piemēram ldw cpu tiks ielādēts reÄ£istrā A kodola funkcijas palaiÅ”anas rezultāts raw_smp_processor_id(). (Jaunajā BPF versijā Å”ie nestandarta paplaÅ”inājumi ir paplaÅ”ināti, lai nodroÅ”inātu programmām kodola palÄ«gu komplektu piekļuvei atmiņai, struktÅ«rām un notikumu Ä£enerÄ“Å”anai.) Å eit ir interesants piemērs filtram, kurā mēs kopējam tikai pakeÅ”u galvenes lietotāja telpā, izmantojot paplaÅ”inājumu poff, lietderÄ«gās slodzes nobÄ«de:

ld poff
ret a

BPF paplaÅ”inājumus nevar izmantot tcpdump, taču tas ir labs iemesls, lai iepazÄ«tos ar utilÄ«tu paketi netsniff-ng, kurā, cita starpā, ir iekļauta uzlabota programma netsniff-ng, kas papildus filtrÄ“Å”anai, izmantojot BPF, satur arÄ« efektÄ«vu trafika Ä£eneratoru un daudz modernāku nekā tools/bpf/bpf_asm, sauca BPF montētājs bpfc. Paketē ir diezgan detalizēta dokumentācija, skatiet arÄ« saites raksta beigās.

seccomp

Tātad, mēs jau zinām, kā rakstīt patvaļīgas sarežģītības BPF programmas un esam gatavi aplūkot jaunus piemērus, no kuriem pirmais ir seccomp tehnoloģija, kas ļauj, izmantojot BPF filtrus, pārvaldīt pieejamo sistēmas izsaukuma argumentu kopu un kopu. dotais process un tā pēcteči.

Pirmā seccomp versija tika pievienota kodolam 2005. gadā un nebija Ä«paÅ”i populāra, jo tā nodroÅ”ināja tikai vienu iespēju ā€” ierobežot procesam pieejamo sistēmas izsaukumu kopu lÄ«dz Ŕādam: read, write, exit Šø sigreturn, un process, kurā tika pārkāpti noteikumi, tika nogalināts, izmantojot SIGKILL. Tomēr 2012. gadā seccomp pievienoja iespēju izmantot BPF filtrus, ļaujot definēt atļauto sistēmas izsaukumu kopu un pat veikt to argumentu pārbaudes. (Interesanti, ka Chrome bija viens no pirmajiem Ŕīs funkcionalitātes lietotājiem, un Chrome lietotāji paÅ”laik izstrādā KRSI mehānismu, kura pamatā ir jauna BPF versija un kas ļauj pielāgot Linux droŔības moduļus.) Saites uz papildu dokumentāciju var atrast beigās. no raksta.

Ņemiet vērā, ka centrmezglā jau ir bijuÅ”i raksti par seccomp lietoÅ”anu, iespējams, kāds tos vēlēsies izlasÄ«t pirms (vai tā vietā), lai lasÄ«tu turpmākās apakÅ”sadaļas. Rakstā Konteineri un droŔība: seccomp sniedz seccomp izmantoÅ”anas piemērus, gan 2007. gada versiju, gan versiju, kas izmanto BPF (filtri tiek Ä£enerēti, izmantojot libseccomp), runā par seccomp savienojumu ar Docker, kā arÄ« sniedz daudzas noderÄ«gas saites. Rakstā Dēmonu izolÄ“Å”ana ar systemd vai ā€œjums Å”im nav nepiecieÅ”ams Docker!ā€ Tas jo Ä«paÅ”i attiecas uz to, kā pievienot sistēmas zvanu melnos sarakstus vai baltos sarakstus dēmoniem, kuros darbojas systemd.

Tālāk mēs redzēsim, kā rakstÄ«t un ielādēt filtrus seccomp tukÅ”ajā C valodā un izmantojot bibliotēku libseccomp un kādi ir katras opcijas plusi un mÄ«nusi, un, visbeidzot, redzēsim, kā programma izmanto seccomp strace.

Seccomp rakstīŔanas un ielādes filtri

Mēs jau zinām, kā rakstÄ«t BPF programmas, tāpēc vispirms apskatÄ«sim seccomp programmÄ“Å”anas saskarni. Varat iestatÄ«t filtru procesa lÄ«menÄ«, un visi pakārtotie procesi pārmantos ierobežojumus. Tas tiek darÄ«ts, izmantojot sistēmas zvanu seccomp(2):

seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)

kur &filter - Ŕī ir norāde uz mums jau pazīstamu struktūru struct sock_fprog, t.i. BPF programma.

Kā programmas seccomp atŔķiras no programmām ligzdām? PārraidÄ«ts konteksts. Sockets gadÄ«jumā mums tika pieŔķirts atmiņas apgabals, kurā bija pakete, un seccomp gadÄ«jumā mums tika dota tāda struktÅ«ra kā

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

Å eit nr ir sistēmas izsaukuma numurs, kas jāuzsāk, arch - paÅ”reizējā arhitektÅ«ra (vairāk par to zemāk), args - lÄ«dz seÅ”iem sistēmas izsaukuma argumentiem un instruction_pointer ir rādÄ«tājs uz lietotāja telpas instrukciju, kas veica sistēmas izsaukumu. Tā, piemēram, lai ielādētu reÄ£istrā sistēmas izsaukuma numuru A mums jāsaka

ldw [0]

Seccomp programmām ir arÄ« citas funkcijas, piemēram, kontekstam var piekļūt tikai ar 32 bitu izlÄ«dzināŔanu, un jÅ«s nevarat ielādēt pusi vārda vai baitu - mēģinot ielādēt filtru ldh [0] sistēmas zvans seccomp atgriezÄ«sies EINVAL. Funkcija pārbauda ielādētos filtrus seccomp_check_filter() kodoli. (SmieklÄ«gi ir tas, ka sākotnējā apņemÅ”anā, kas pievienoja seccomp funkcionalitāti, viņi aizmirsa pievienot atļauju izmantot instrukciju Å”ai funkcijai mod (sadalÄ«Å”anas atlikums) un tagad nav pieejams seccomp BPF programmām kopÅ” tā pievienoÅ”anas salÅ«zÄ«s ABI.)

Būtībā mēs jau zinām visu, lai rakstītu un lasītu seccomp programmas. Parasti programmas loģika tiek sakārtota kā balts vai melns sistēmas izsaukumu saraksts, piemēram, programma

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

pārbauda četru sistēmas zvanu melno sarakstu ar numuriem 304, 176, 239, 279. Kas ir Å”ie sistēmas zvani? Mēs nevaram droÅ”i pateikt, jo mēs nezinām, kādai arhitektÅ«rai programma tika rakstÄ«ta. Tāpēc seccomp autori piedāvājums sāciet visas programmas ar arhitektÅ«ras pārbaudi (paÅ”reizējā arhitektÅ«ra kontekstā tiek norādÄ«ta kā lauks arch struktÅ«ras struct seccomp_data). Pārbaudot arhitektÅ«ru, piemēra sākums izskatÄ«tos Ŕādi:

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

un tad mūsu sistēmas zvanu numuri iegūtu noteiktas vērtības.

Mēs rakstām un ielādējam filtrus seccomp lietoÅ”anai libseccomp

Filtru rakstÄ«Å”ana vietējā kodā vai BPF komplektācijā ļauj pilnÄ«bā kontrolēt rezultātu, taču tajā paŔā laikā dažreiz ir vēlams izmantot pārnēsājamu un/vai lasāmu kodu. Bibliotēka mums palÄ«dzēs Å”ajā jautājumā libseccomp, kas nodroÅ”ina standarta saskarni melnu vai baltu filtru rakstÄ«Å”anai.

Piemēram, uzrakstÄ«sim programmu, kas palaiž bināro failu pēc lietotāja izvēles, iepriekÅ” instalējot sistēmas zvanu melno sarakstu no iepriekÅ” minētais raksts (programma ir vienkārÅ”ota labākai lasāmÄ«bai, var atrast pilno versiju Å”eit):

#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]);
}

Vispirms mēs definējam masÄ«vu sys_numbers no 40+ sistēmas zvanu numuriem, ko bloķēt. Pēc tam inicializējiet kontekstu ctx un pastāstiet bibliotēkai, ko mēs vēlamies atļaut (SCMP_ACT_ALLOW) visi sistēmas zvani pēc noklusējuma (vieglāk ir izveidot melnos sarakstus). Pēc tam pa vienam pievienojam visus sistēmas zvanus no melnā saraksta. Atbildot uz sistēmas zvanu no saraksta, mēs pieprasām SCMP_ACT_TRAP, Å”ajā gadÄ«jumā seccomp nosÅ«tÄ«s signālu procesam SIGSYS ar aprakstu, kura sistēmas izsaukumā tika pārkāpti noteikumi. Visbeidzot, mēs ielādējam programmu kodolā, izmantojot seccomp_load, kas apkopos programmu un pievienos procesam, izmantojot sistēmas izsaukumu seccomp(2).

Veiksmīgai kompilācijai programmai jābūt saistītai ar bibliotēku libseccomp, piemēram:

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

VeiksmÄ«gas palaiÅ”anas piemērs:

$ ./seccomp_lib echo ok
ok

Bloķēta sistēmas zvana piemērs:

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

Mēs izmantojam stracesīkākai informācijai:

$ 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

kā mēs varam zināt, ka programma tika pārtraukta nelikumÄ«ga sistēmas izsaukuma izmantoÅ”anas dēļ mount(2).

Tātad, mēs uzrakstÄ«jām filtru, izmantojot bibliotēku libseccomp, iekļaujot netriviālu kodu četrās rindās. IepriekÅ” minētajā piemērā, ja ir liels sistēmas izsaukumu skaits, izpildes laiku var ievērojami samazināt, jo pārbaude ir tikai salÄ«dzinājumu saraksts. Optimizācijai libseccomp nesen bija komplektā plāksteris, kas pievieno atbalstu filtra atribÅ«tam SCMP_FLTATR_CTL_OPTIMIZE. Iestatot Å”o atribÅ«tu uz 2, filtrs tiks pārveidots par bināro meklÄ“Å”anas programmu.

Ja vēlaties redzēt, kā darbojas binārie meklÄ“Å”anas filtri, apskatiet vienkārÅ”s skripts, kas Ä£enerē Ŕādas programmas BPF montētājā, sastādot sistēmas zvanu numurus, piemēram:

$ 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

Nav iespējams kaut ko uzrakstīt ievērojami ātrāk, jo BPF programmas nevar veikt atkāpes lēcienus (mēs nevaram izdarīt, piemēram, jmp A vai jmp [label+X]), un tāpēc visas pārejas ir statiskas.

seccomp un strace

Ikviens zina lietderÄ«bu strace ir neaizstājams rÄ«ks procesu uzvedÄ«bas izpētei operētājsistēmā Linux. Tomēr daudzi ir dzirdējuÅ”i arÄ« par veiktspējas problēmas izmantojot Å”o utilÄ«tu. Fakts ir tāds strace ieviests, izmantojot ptrace(2), un Å”ajā mehānismā mēs nevaram norādÄ«t, pie kādas sistēmas izsaukumu kopas ir nepiecieÅ”ams process apturēt, t.i., komandas

$ 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

tiek apstrādāti aptuveni vienā laikā, lai gan otrajā gadījumā mēs vēlamies izsekot tikai vienam sistēmas izsaukumam.

Jauna iespēja --seccomp-bpf, pievienots strace versija 5.3, ļauj daudzkārt paātrināt procesu, un palaiÅ”anas laiks zem viena sistēmas zvana jau ir salÄ«dzināms ar parastās palaiÅ”anas laiku:

$ 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

(Å eit, protams, ir neliela maldināŔana, jo mēs neizsekojam Ŕīs komandas galveno sistēmas izsaukumu. Ja mēs izsekotu, piemēram, newfsstattad strace bremzētu tikpat spēcÄ«gi kā bez --seccomp-bpf.)

Kā Ŕī opcija darbojas? Bez viņas strace pieslēdzas procesam un sāk to lietot PTRACE_SYSCALL. Kad pārvaldÄ«tais process izdod (jebkuru) sistēmas izsaukumu, vadÄ«ba tiek nodota uz strace, kas aplÅ«ko sistēmas zvana argumentus un palaiž to ar PTRACE_SYSCALL. Pēc kāda laika process pabeidz sistēmas izsaukumu un, izejot no tā, vadÄ«ba tiek nodota vēlreiz strace, kas aplÅ«ko atgrieÅ”anas vērtÄ«bas un sāk procesu, izmantojot PTRACE_SYSCALL, un tā tālāk.

BPF mazajiem, nulles daļa: klasiskais BPF

Tomēr ar seccomp Å”o procesu var optimizēt tieÅ”i tā, kā mēs vēlētos. Proti, ja gribam aplÅ«kot tikai sistēmas izsaukumu X, tad mēs varam uzrakstÄ«t BPF filtru, kas paredzēts X atgriež vērtÄ«bu SECCOMP_RET_TRACE, un zvaniem, kas mÅ«s neinteresē - SECCOMP_RET_ALLOW:

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

Å ajā gadÄ«jumā strace sākotnēji procesu sāk kā PTRACE_CONT, mÅ«su filtrs tiek apstrādāts katram sistēmas zvanam, ja sistēmas zvans nav X, process turpinās darboties, bet, ja Å”is X, tad seccomp nodos vadÄ«bu stracekas izskatÄ«s argumentus un sāks procesu lÄ«dzÄ«gi PTRACE_SYSCALL (jo seccomp nevar palaist programmu, izejot no sistēmas izsaukuma). Kad sistēmas zvans atgriežas, strace atsāks procesu, izmantojot PTRACE_CONT un gaidÄ«s jaunus ziņojumus no seccomp.

BPF mazajiem, nulles daļa: klasiskais BPF

Izmantojot opciju --seccomp-bpf ir divi ierobežojumi. Pirmkārt, nebÅ«s iespējams pievienoties jau esoÅ”am procesam (opcija -p programmas strace), jo seccomp to neatbalsta. Otrkārt, nav iespēju nē apskatiet bērnprocesus, jo seccomp filtrus manto visi bērnprocesi bez iespējas to atspējot.

Nedaudz sÄ«kāk par to, kā tieÅ”i strace strādā ar seccomp var atrast no nesenais ziņojums. Mums interesantākais fakts ir tas, ka klasiskais BPF, ko pārstāv seccomp, tiek izmantots arÄ« mÅ«sdienās.

xt_bpf

Tagad atgriezīsimies tīklu pasaulē.

PriekÅ”vēsture: sen, 2007. gadā, kodols bija piebilda modulis xt_u32 tÄ«kla filtram. Tas tika uzrakstÄ«ts pēc analoÄ£ijas ar vēl senāku satiksmes klasifikatoru cls_u32 un ļāva rakstÄ«t patvaļīgus bināros noteikumus iptables, izmantojot Ŕādas vienkārÅ”as darbÄ«bas: ielādējiet 32 ā€‹ā€‹bitus no pakotnes un veiciet aritmētisko darbÄ«bu kopu ar tiem. Piemēram,

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

Ielādē IP galvenes 32 bitus, sākot ar 6. polsterējumu, un uzliek tiem masku 0xFF (paņemiet zemo baitu). Å is lauks protocol IP galveni un salÄ«dzinām to ar 1 (ICMP). Vienā noteikumā varat apvienot daudzas pārbaudes, kā arÄ« izpildÄ«t operatoru @ ā€” pārvietot X baitus pa labi. Piemēram, noteikums

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

pārbauda, ā€‹ā€‹vai TCP kārtas numurs nav vienāds 0x29. SÄ«kāk neiedziļināŔos, jo jau tagad ir skaidrs, ka Ŕādus noteikumus rakstÄ«t ar roku nav Ä«paÅ”i ērti. Rakstā BPF - aizmirstais baitkods, ir vairākas saites ar lietojuma piemēriem un noteikumu Ä£enerÄ“Å”anu xt_u32. Skatiet arÄ« saites Ŕī raksta beigās.

KopÅ” 2013. gada modulis moduļa vietā xt_u32 varat izmantot BPF balstÄ«tu moduli xt_bpf. Ikvienam, kurÅ” ir lasÄ«jis tik tālu, jau vajadzētu bÅ«t skaidram par tā darbÄ«bas principu: palaist BPF baitu kodu kā iptables noteikumus. Varat izveidot jaunu noteikumu, piemēram, Ŕādi:

iptables -A INPUT -m bpf --bytecode <Š±Š°Š¹Ń‚ŠŗŠ¾Š“> -j LOG

Å”eit <Š±Š°Š¹Ń‚ŠŗŠ¾Š“> - Å”is ir kods montētāja izvades formātā bpf_asm pēc noklusējuma, piemēram,

$ 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

Å ajā piemērā mēs filtrējam visas UDP paketes. BPF programmas konteksts modulÄ« xt_bpf, protams, norāda uz pakeÅ”u datiem, iptables gadÄ«jumā uz IPv4 galvenes sākumu. BPF programmas atgrieÅ”anas vērtÄ«ba BÅ«laKur false nozÄ«mē, ka pakete nesakrita.

Ir skaidrs, ka modulis xt_bpf atbalsta sarežģītākus filtrus nekā iepriekÅ” minētajā piemērā. ApskatÄ«sim reālus piemērus no Cloudfare. Vēl nesen viņi izmantoja moduli xt_bpf lai aizsargātu pret DDoS uzbrukumiem. Rakstā IepazÄ«stinām ar BPF rÄ«kiem viņi paskaidro, kā (un kāpēc) viņi Ä£enerē BPF filtrus un publicē saites uz utilÄ«tu kopu Ŕādu filtru izveidei. Piemēram, izmantojot utilÄ«tu bpfgen varat izveidot BPF programmu, kas atbilst vārda DNS vaicājumam 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

Programmā mēs vispirms ielādējam reģistrā X rindas sākuma adrese x04habrx03comx00 UDP datagrammā un pēc tam pārbaudiet pieprasījumu: 0x04686162 <-> "x04hab" uc

Nedaudz vēlāk Cloudfare publicēja p0f -> BPF kompilatora kodu. Rakstā Iepazīstinām ar p0f BPF kompilatoru viņi runā par to, kas ir p0f un kā pārvērst p0f parakstus par BPF:

$ ./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,
...

PaÅ”laik vairs neizmanto Cloudfare xt_bpf, jo viņi pārcēlās uz XDP - vienu no iespējām izmantot jauno BPF versiju, sk. L4Drop: XDP DDoS mazināŔanas pasākumi.

cls_bpf

Pēdējais piemērs klasiskā BPF izmantoÅ”anai kodolā ir klasifikators cls_bpf satiksmes vadÄ«bas apakÅ”sistēmai operētājsistēmā Linux, kas tika pievienota Linux 2013. gada beigās un konceptuāli aizstāj seno cls_u32.

Tomēr mēs tagad neaprakstÄ«sim darbu cls_bpf, jo no zināŔanu viedokļa par klasisko BPF tas mums neko nedos - mēs jau esam iepazinuÅ”ies ar visu funkcionalitāti. Turklāt turpmākajos rakstos, kas runā par paplaÅ”ināto BPF, mēs vairāk nekā vienu reizi tiksimies ar Å”o klasifikatoru.

Vēl viens iemesls, lai nerunātu par klasiskā BPF lietoÅ”anu c cls_bpf Problēma ir tāda, ka, salÄ«dzinot ar Extended BPF, pielietojamÄ«bas joma Å”ajā gadÄ«jumā ir radikāli saÅ”aurināta: klasiskās programmas nevar mainÄ«t pakotņu saturu un nevar saglabāt stāvokli starp zvaniem.

Tāpēc ir pienācis laiks atvadīties no klasiskā BPF un skatīties nākotnē.

Ardievas klasiskajam BPF

Mēs apskatÄ«jām, kā deviņdesmito gadu sākumā izstrādātā BPF tehnoloÄ£ija veiksmÄ«gi nodzÄ«voja gadsimta ceturksni un lÄ«dz beigām atrada jaunus pielietojumus. Tomēr lÄ«dzÄ«gi kā pāreja no steka maŔīnām uz RISC, kas kalpoja par stimulu klasiskā BPF attÄ«stÄ«bai, 32. gados notika pāreja no 64 bitu uz XNUMX bitu maŔīnām un klasiskais BPF sāka novecot. Turklāt klasiskā BPF iespējas ir ļoti ierobežotas, un papildus novecojuÅ”ajai arhitektÅ«rai - mums nav iespējas saglabāt stāvokli starp zvaniem uz BPF programmām, nav tieÅ”as lietotāja mijiedarbÄ«bas, nav mijiedarbÄ«bas iespējas. ar kodolu, izņemot ierobežota skaita struktÅ«ras lauku nolasÄ«Å”anu sk_buff un palaižot vienkārŔākās palÄ«gfunkcijas, nevar mainÄ«t pakeÅ”u saturu un tās pāradresēt.

Faktiski Å”obrÄ«d viss, kas paliek no klasiskā BPF operētājsistēmā Linux, ir API saskarne, un kodola iekÅ”pusē visas klasiskās programmas, neatkarÄ«gi no tā, vai tie bÅ«tu ligzdas filtri vai seccomp filtri, tiek automātiski tulkoti jaunā formātā Extended BPF. (Par to, kā tieÅ”i tas notiek, mēs runāsim nākamajā rakstā.)

Pāreja uz jaunu arhitektÅ«ru sākās 2013. gadā, kad Aleksejs Starovoitovs ierosināja BPF atjaunināŔanas shēmu. 2014. gadā atbilstoÅ”os ielāpus sāka parādÄ«ties kodolā. Cik es saprotu, sākotnējais plāns bija tikai optimizēt arhitektÅ«ru un JIT kompilatoru, lai tā darbotos efektÄ«vāk 64 bitu iekārtās, taču tā vietā Ŕīs optimizācijas iezÄ«mēja jaunas nodaļas sākumu Linux attÄ«stÄ«bā.

Turpmākie raksti Å”ajā sērijā aptvers jaunās tehnoloÄ£ijas arhitektÅ«ru un lietojumus, kas sākotnēji bija pazÄ«stami kā iekŔējais BPF, pēc tam paplaÅ”inātais BPF un tagad vienkārÅ”i BPF.

atsauces

  1. StÄ«vens Makkens un Van Džeikobsons, "BSD pakeÅ”u filtrs: jauna arhitektÅ«ra lietotāja lÄ«meņa pakeÅ”u uztverÅ”anai", https://www.tcpdump.org/papers/bpf-usenix93.pdf
  2. Stīvens Makkens, "libpcap: arhitektūra un optimizācijas metodika pakeŔu uztverŔanai", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf
  3. tcpdump, libpcap: https://www.tcpdump.org/
  4. IPtable U32 atbilstības apmācība.
  5. BPF ā€” aizmirstais baitkods: https://blog.cloudflare.com/bpf-the-forgotten-bytecode/
  6. Iepazīstinām ar BPF rīku: https://blog.cloudflare.com/introducing-the-bpf-tools/
  7. bpf_cls: http://man7.org/linux/man-pages/man8/tc-bpf.8.html
  8. Seccomp pārskats: https://lwn.net/Articles/656307/
  9. https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/seccomp_filter.rst
  10. habr: Konteineri un droŔība: seccomp
  11. habr: Dēmonu izolÄ“Å”ana ar systemd vai ā€œjums Å”im nav nepiecieÅ”ams Docker!ā€
  12. Pols Čeinons, "strace --seccomp-bpf: skatiens zem pārsega", https://fosdem.org/2020/schedule/event/debugging_strace_bpf/
  13. netsniff-ng: http://netsniff-ng.org/

Avots: www.habr.com

Pievieno komentāru