Ekuqaleni kwakunobuchwepheshe futhi babubizwa nge-BPF. Sambuka
Uma sikhuluma nje, i-BPF ikuvumela ukuthi usebenzise ikhodi ehlinzekwe ngabasebenzisi ngokungafanele endaweni ye-Linux kernel, futhi ukwakheka okusha kuphumelele kangangokuthi sizodinga izindatshana eziyishumi nambili ukuchaza zonke izinhlelo zokusebenza. (Okuwukuphela kwento onjiniyela abangayenzanga kahle, njengoba ubona kukhodi yokusebenza engezansi, bekuwukwenza ilogo ehloniphekile.)
Lesi sihloko sichaza isakhiwo somshini we-BPF virtual, i-kernel interfaces yokusebenza ne-BPF, amathuluzi okuthuthukisa, kanye nombono omfushane, omfushane kakhulu wamakhono akhona, i.e. yonke into esizoyidinga esikhathini esizayo ukuze sicwaninge ngokujulile ukusetshenziswa okusebenzayo kwe-BPF.
Isifinyezo se-athikili
bpf(2)
.
ΠΠΈΡΠ΅ΠΌ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ BPF Ρ ΠΏΠΎΠΌΠΎΡΡΡ libbpf
.libbpf
. Sizodala uhlaka lwamathambo oluyisisekelo lwe-BPF esizolusebenzisa ezibonelweni ezilandelayo.
Isingeniso se-BPF Architecture
Ngaphambi kokuthi siqale ukucabangela ukwakheka kwe-BPF, sizobhekisela okokugcina (oh) kuyo
I-BPF entsha yathuthukiswa njengempendulo ekutholakaleni kwemishini engama-64-bit, izinsizakalo zamafu kanye nesidingo esikhulayo samathuluzi wokudala i-SDN (Si-offware-defined nukusebenza). Ithuthukiswe onjiniyela benethiwekhi ye-kernel njengendlela ethuthukisiwe esikhundleni se-BPF yakudala, i-BPF entsha ezinyangeni eziyisithupha kamuva yathola izinhlelo zokusebenza emsebenzini onzima wokulandela izinhlelo ze-Linux, futhi manje, eminyakeni eyisithupha ngemuva kokuvela kwayo, sizodinga indatshana elandelayo bhala izinhlobo ezahlukene zezinhlelo.
Izithombe ezihlekisayo
Emgogodleni wayo, i-BPF ingumshini obonakalayo we-sandbox okuvumela ukuthi usebenzise ikhodi βengenasizathuβ esikhaleni se-kernel ngaphandle kokubeka engcupheni ukuphepha. Izinhlelo ze-BPF zenziwa endaweni yomsebenzisi, zilayishwe ku-kernel, futhi zixhunywe komunye umthombo womcimbi. Umcimbi ungaba, isibonelo, ukulethwa kwephakethe kusixhumi esibonakalayo senethiwekhi, ukwethulwa komsebenzi othile we-kernel, njll. Endabeni yephakheji, uhlelo lwe-BPF luzokwazi ukufinyelela idatha kanye nemethadatha yephakheji (yokufunda futhi, ngokunokwenzeka, ukubhala, kuye ngohlobo lohlelo); esimweni sokuqalisa umsebenzi we-kernel, izimpikiswano ze umsebenzi, okuhlanganisa izikhombisi zenkumbulo ye-kernel, njll.
Ake siyibhekisise le nqubo. Okokuqala, ake sikhulume ngomehluko wokuqala we-BPF yakudala, izinhlelo zazo ezibhalwe ku-assembler. Kule nguqulo entsha, ukwakhiwa kwandiswa ukuze izinhlelo zibhalwe ngezilimi ezisezingeni eliphezulu, ngokuyinhloko, ngokuqinisekile, ku-C. Ngenxa yalokhu, i-backend ye-llvm yathuthukiswa, okuvumela ukuthi ukhiqize i-bytecode ye-architecture ye-BPF.
Isakhiwo se-BPF saklanywa, ngokwengxenye, ukuze sisebenze kahle emishinini yesimanje. Ukwenza lokhu kusebenze, i-BPF bytecode, uma isilayishiwe ku-kernel, ihunyushelwa kukhodi yomdabu kusetshenziswa ingxenye ebizwa ngokuthi i-JIT compiler (Ju In Time). Okulandelayo, uma ukhumbula, ku-BPF yakudala uhlelo lwalayishwa ku-kernel futhi lwanamathiselwa kumthombo womcimbi nge-athomu - kumongo wocingo olulodwa lwesistimu. Ekwakhiweni okusha, lokhu kwenzeka ngezigaba ezimbili - okokuqala, ikhodi ilayishwa ku-kernel kusetshenziswa ucingo lwesistimu. bpf(2)
bese, kamuva, ngezinye izindlela ezihluka ngokuya ngohlobo lohlelo, uhlelo lunamathisele kumthombo womcimbi.
Lapha umfundi angase abe nombuzo: kwakungenzeka yini? Kuqinisekiswa kanjani ukuphepha kokusetshenziswa kwekhodi enjalo? Ukuphepha kokubulawa kuqinisekisiwe kithi esigabeni sokulayisha izinhlelo ze-BPF ezibizwa ngokuthi i-verifier (ngesiNgisi lesi sigaba sibizwa ngokuthi i-verifier futhi ngizoqhubeka nokusebenzisa igama lesiNgisi):
Isiqinisekisi siyisihlaziyi esimile esiqinisekisa ukuthi uhlelo aluphazamisi ukusebenza okuvamile kwe-kernel. Lokhu, ngendlela, akusho ukuthi uhlelo alukwazi ukuphazamisa ukusebenza kohlelo - izinhlelo ze-BPF, kuye ngokuthi hlobo luni, zingakwazi ukufunda futhi zibhale kabusha izingxenye zememori ye-kernel, ukubuyisela amanani emisebenzi, ukunquma, ukufaka, ukubhala kabusha. ngisho namaphakethe enethiwekhi. Isiqinisekisi siqinisekisa ukuthi ukuqalisa uhlelo lwe-BPF ngeke kuphahlazeke i-kernel nokuthi uhlelo, ngokwemithetho, olunokufinyelela kokubhala, isibonelo, idatha yephakethe eliphumayo, ngeke lukwazi ukubhala phezu kwememori ye-kernel ngaphandle kwephakethe. Sizobheka isiqinisekisi ngokuningiliziwe okwengeziwe esigabeni esihambelanayo, ngemva kokujwayelana nazo zonke ezinye izingxenye ze-BPF.
Manje yini esiyifundile kuze kube manje? Umsebenzisi ubhala uhlelo ngo-C, alulayishe ku-kernel esebenzisa ikholi yesistimu bpf(2)
, lapho ihlolwa yisiqinisekisi bese ihunyushelwa ku-bytecode yomdabu. Bese lowo msebenzisi noma omunye uxhuma uhlelo emthonjeni womcimbi futhi luqala ukuqalisa. Ukuhlukanisa i-boot nokuxhumeka kuyadingeka ngenxa yezizathu eziningana. Okokuqala, ukusebenzisa isiqinisekisi kubiza kakhulu futhi ngokulanda uhlelo olufanayo izikhathi ezimbalwa sichitha isikhathi sekhompyutha. Okwesibili, ukuthi uhlelo luxhunywe kanjani kuncike ohlotsheni lwalo, futhi isikhombimsebenzisi esisodwa βsendawo yonkeβ esakhiwe ngonyaka odlule singase singalungeli izinhlobo ezintsha zezinhlelo. (Nakuba manje i-architecture ikhula kakhulu, kunombono wokuhlanganisa lesi sikhombimsebenzisi ezingeni libbpf
.)
Umfundi olalelayo angase aqaphele ukuthi asikaqedi ngezithombe. Impela, konke lokhu okungenhla akuchazi ukuthi kungani i-BPF ishintsha ngokuyisisekelo isithombe uma siqhathaniswa ne-BPF yakudala. Okusha okubili okunweba kakhulu ububanzi bokusebenza yikhono lokusebenzisa inkumbulo eyabiwe nemisebenzi yomsizi we-kernel. Ku-BPF, inkumbulo eyabiwe isetshenziswa kusetshenziswa okuthiwa amamephu - izakhiwo zedatha ezabiwe nge-API ethile. Cishe bathole leli gama ngoba uhlobo lokuqala lwemephu oluzovela kwakuyithebula le-hash. Kwabe sekuvela ama-arrays, amathebula e-hashi wendawo (i-CPU ngayinye) nezinhlu zendawo, izihlahla zokusesha, amamephu aqukethe izinkomba zezinhlelo ze-BPF nokunye okuningi. Okusijabulisayo manje ukuthi izinhlelo ze-BPF manje zinamandla okuphikelela phakathi kwezingcingo futhi sabelane ngazo nezinye izinhlelo kanye nesikhala somsebenzisi.
Amamephu afinyelelwa ezinqubweni zomsebenzisi kusetshenziswa ikholi yesistimu bpf(2)
, futhi kusukela ezinhlelweni ze-BPF ezisebenza ku-kernel zisebenzisa imisebenzi yomsizi. Ngaphezu kwalokho, abasizi abakhona nje kuphela ukusebenza ngamamephu, kodwa futhi nokufinyelela amanye amakhono e-kernel. Isibonelo, izinhlelo ze-BPF zingasebenzisa imisebenzi yomsizi ukuze udlulisele amaphakethe kwezinye izixhumanisi, ukukhiqiza imicimbi ye-perf, ukufinyelela izakhiwo ze-kernel, njalonjalo.
Kafushane, i-BPF inikeza amandla okulayisha ngokunganaki, okungukuthi, ukuhlolwa kokuqinisekisa, ikhodi yomsebenzisi endaweni ye-kernel. Le khodi ingalondoloza isimo phakathi kwamakholi kanye nedatha yokushintshisana ngesikhala somsebenzisi, futhi inokufinyelela kumasistimu angaphansi kwe-kernel avunyelwe yilolu hlobo lohlelo.
Lokhu sekuvele kufana namakhono anikezwe amamojula we-kernel, uma kuqhathaniswa ne-BPF enezinzuzo ezithile (yebo, ungaqhathanisa kuphela izinhlelo zokusebenza ezifanayo, isibonelo, ukulandelela uhlelo - awukwazi ukubhala umshayeli ongekho emthethweni nge-BPF). Ungaqaphela umkhawulo wokungena ophansi (ezinye izinsiza ezisebenzisa i-BPF azidingi ukuthi umsebenzisi abe namakhono okuhlela i-kernel, noma amakhono okuhlela ngokuvamile), ukuphepha kwesikhathi sokusebenza (phakamisa isandla sakho kumazwana walabo abangazange baphule isistimu lapho bebhala noma amamojula okuhlola), i-athomu - kunesikhathi sokuphumula lapho ulayisha kabusha amamojula, futhi isistimu engaphansi ye-BPF iqinisekisa ukuthi azikho izehlakalo ezigejiwe (ukuba ukulunga, lokhu akulona iqiniso kuzo zonke izinhlobo zezinhlelo ze-BPF).
Ukuba khona kwamakhono anjalo kwenza i-BPF ibe yithuluzi lomhlaba wonke lokwandisa i-kernel, eqinisekiswa ekusebenzeni: izinhlobo eziningi zezinhlelo zengezwa ku-BPF, izinkampani ezinkulu kakhulu zisebenzisa i-BPF kumaseva wokulwa 24 Γ 7, nokuningi nangaphezulu. abaqalayo bakha amabhizinisi abo ezisombululweni ezisuselwe ku-BPF. I-BPF isetshenziswa yonke indawo: ekuvikeleni ekuhlaselweni kwe-DDoS, ukudala i-SDN (isibonelo, ukusebenzisa amanethiwekhi e-kubernetes), njengethuluzi eliyinhloko lokulandelela isistimu kanye nomqoqi wezibalo, kumasistimu okuthola ukungena kokungena kanye nezinhlelo ze-sandbox, njll.
Ake siqedele ukubuka konke kwengxenye yendatshana lapha futhi sibheke umshini obonakalayo kanye ne-ecosystem ye-BPF ngemininingwane eyengeziwe.
Ukwehla: izinsiza
Ukuze ukwazi ukusebenzisa izibonelo ezigabeni ezilandelayo, ungase udinge inani lezinsiza, okungenani llvm
/clang
ngokusekelwa kwe-bpf kanye bpftool
. Esigabeni
Amarejista omshini we-BPF Virtual kanye nohlelo lwemiyalo
I-architecture kanye nohlelo lomyalo lwe-BPF lwathuthukiswa kucatshangelwa iqiniso lokuthi izinhlelo zizobhalwa ngolimi C futhi, ngemva kokulayisha ku-kernel, zihunyushwe kukhodi yomdabu. Ngakho-ke, inani lamarejista kanye nesethi yemiyalo yakhethwa ngeso lokuhlangana, ngomqondo wezibalo, wamakhono emishini yesimanje. Ngaphezu kwalokho, imikhawulo ehlukahlukene yabekwa ezinhlelweni, ngokwesibonelo, kuze kube muva nje kwakungenakwenzeka ukubhala izihibe nama-subroutines, futhi inani lemiyalo lalinqunyelwe ku-4096 (manje izinhlelo ezinelungelo zingalayisha imiyalelo efinyelela kwesigidi).
I-BPF inamarejista angama-64-bit afinyelelekayo ayishumi nanye r0
-r10
kanye nekhawunta yohlelo. Bhalisa r10
iqukethe isikhombi sozimele futhi ifundwa kuphela. Izinhlelo zinokufinyelela kusitaki esingu-512-byte ngesikhathi sokusebenza kanye nenani elingenamkhawulo lememori eyabiwe ngendlela yamamephu.
Izinhlelo ze-BPF zivunyelwe ukusebenzisa isethi ethile yabasizi be-kernel yohlelo futhi, kamuva nje, imisebenzi evamile. Umsebenzi ngamunye obizwayo ungathatha izimpikiswano ezinhlanu, eziphasiswe kumarejista r1
-r5
, futhi inani lokubuyisela lidluliselwe kulo r0
. Kuqinisekisiwe ukuthi ngemva kokubuya emsebenzini, okuqukethwe kwamarejista r6
-r9
Ngeke kushintshe.
Ukuze uthole ukuhumusha kahle kohlelo, amarejista r0
-r11
kuzo zonke izakhiwo ezisekelwayo zidwetshwe ngokuhlukile kumarejista angempela, kucatshangelwa izici ze-ABI zezakhiwo zamanje. Ngokwesibonelo, for x86_64
amarejista r1
-r5
, esetshenziswa ukudlulisa amapharamitha omsebenzi, ayavezwa rdi
, rsi
, rdx
, rcx
, r8
, ezisetshenziselwa ukudlulisa amapharamitha ekusebenzeni kwawo x86_64
. Isibonelo, ikhodi engakwesokunxele ihumushela kukhodi engakwesokudla kanje:
1: (b7) r1 = 1 mov $0x1,%rdi
2: (b7) r2 = 2 mov $0x2,%rsi
3: (b7) r3 = 3 mov $0x3,%rdx
4: (b7) r4 = 4 mov $0x4,%rcx
5: (b7) r5 = 5 mov $0x5,%r8
6: (85) call pc+1 callq 0x0000000000001ee8
Bhalisa r0
iphinde isetshenziselwe ukubuyisela umphumela wokwenziwa kohlelo, nakurejista r1
uhlelo ludluliswa i-pointer kumongo - kuye ngohlobo lohlelo, lokhu kungaba, isibonelo, isakhiwo struct xdp_md
struct __sk_buff
struct pt_regs
Ngakho-ke, sibe nesethi yamarejista, abasizi be-kernel, isitaki, isikhombisi somongo kanye nenkumbulo eyabiwe ngendlela yamamephu. Akukhona ukuthi konke lokhu kuyadingeka ngempela ohambweni, kodwa...
Masiqhubeke nencazelo futhi sikhulume ngohlelo lomyalo lokusebenza ngalezi zinto. Konke (
kuyinto Code
- lokhu ukubhalwa ngekhodi komyalelo, Dst
/Src
kungumbhalo wekhodi womamukeli nomthombo, ngokulandelana, Off
- 16-bit ukuhlehlisa esayiniwe, kanye Imm
iyinombolo esayiniwe engamabhithi angu-32 esetshenziswa kweminye imiyalelo (efana ne-cBPF engaguquki K). Ukufaka ikhodi Code
inolunye lwezinhlobo ezimbili:
Amakilasi eziyalezo 0, 1, 2, 3 achaza imiyalo yokusebenza ngenkumbulo. Bona BPF_LD
, BPF_LDX
, BPF_ST
, BPF_STX
, ngokulandelana. Amakilasi 4, 7 (BPF_ALU
, BPF_ALU64
) bakha isethi yemiyalelo ye-ALU. Amakilasi 5, 6 (BPF_JMP
, BPF_JMP32
) aqukethe imiyalelo ye-jump.
Uhlelo olwengeziwe lokufunda uhlelo lwemiyalo ye-BPF lumi kanje: esikhundleni sokubhala ngokucophelela yonke imiyalelo nemingcele yayo, sizobheka izibonelo ezimbalwa kulesi sigaba futhi kuzo kuzocaca ukuthi imiyalelo isebenza kanjani ngempela nokuthi kwenziwa kanjani. qaqa ngesandla noma yiliphi ifayela kanambambili le-BPF. Ukuze sihlanganise okuqukethwe kamuva esihlokweni, sizophinde sihlangane nemiyalelo ngayinye ezigabeni ezimayelana ne-Verifier, i-JIT compiler, ukuhumusha kwe-BPF yakudala, kanye nalapho ufunda amamephu, imisebenzi yokushaya ucingo, njll.
Uma sikhuluma ngemiyalelo ngayinye, sizobhekisela kumafayela ayisisekelo bpf.h
bpf_common.h
Isibonelo: ukuqaqa i-BPF ekhanda lakho
Ake sibheke isibonelo lapho sihlanganisa khona uhlelo readelf-example.c
futhi sibheke kanambambili umphumela. Sizodalula okuqukethwe kwasekuqaleni readelf-example.c
ngezansi, ngemva kokubuyisela ingqondo yayo kumakhodi kanambambili:
$ clang -target bpf -c readelf-example.c -o readelf-example.o -O2
$ llvm-readelf -x .text readelf-example.o
Hex dump of section '.text':
0x00000000 b7000000 01000000 15010100 00000000 ................
0x00000010 b7000000 02000000 95000000 00000000 ................
Ikholomu yokuqala kokuphumayo readelf
i-indent futhi uhlelo lwethu luqukethe imiyalo emine:
Code Dst Src Off Imm
b7 0 0 0000 01000000
15 0 1 0100 00000000
b7 0 0 0000 02000000
95 0 0 0000 00000000
Amakhodi womyalo ayalingana b7
, 15
, b7
ΠΈ 95
. Khumbula ukuthi izingcezu ezintathu ezibaluleke kakhulu yisigaba sokufundisa. Esimweni sethu, ingxenye yesine yayo yonke imiyalelo ayinalutho, ngakho-ke amakilasi okufundisa alingana no-7, 5, 7, 5 ngokulandelana. Ikilasi lesi-7 BPF_ALU64
, futhi u5 BPF_JMP
. Kuwo womabili amakilasi, ifomethi yemiyalelo iyafana (bona ngenhla) futhi singalubhala kabusha uhlelo lwethu kanje (ngasikhathi sinye sizophinda sibhale amakholomu asele ngesimo somuntu):
Op S Class Dst Src Off Imm
b 0 ALU64 0 0 0 1
1 0 JMP 0 1 1 0
b 0 ALU64 0 0 0 2
9 0 JMP 0 0 0 0
Ukusebenza b
class ALU64
Ingabe s
(umthombo), khona-ke inani lithathwa kurejista yomthombo, futhi uma, njengakithi, lingasethiwe, inani lithathwa ensimini. Imm
. Ngakho emiyalweni yokuqala neyesithathu senza umsebenzi r0 = Imm
. Ngaphezu kwalokho, ukusebenza kwe-JMP class 1 kuyinto S
inguziro, iqhathanisa inani lerejista yomthombo nenkambu Imm
. Uma amanani ehlangana, khona-ke ukuguquka kwenzeka PC + Off
kuphi PC
, njengokujwayelekile, iqukethe ikheli lomyalelo olandelayo. Ekugcineni, i-JMP Class 9 Operation is BPF_EXIT
r0
. Ake sengeze ikholomu entsha etafuleni lethu:
Op S Class Dst Src Off Imm Disassm
MOV 0 ALU64 0 0 0 1 r0 = 1
JEQ 0 JMP 0 1 1 0 if (r1 == 0) goto pc+1
MOV 0 ALU64 0 0 0 2 r0 = 2
EXIT 0 JMP 0 0 0 0 exit
Singakubhala kabusha lokhu ngendlela elula kakhulu:
r0 = 1
if (r1 == 0) goto END
r0 = 2
END:
exit
Uma sikhumbula okuserejista r1
uhlelo ludluliswa isikhombi kumongo kusuka ku-kernel, nakurejista r0
inani libuyiselwa ku-kernel, khona-ke singabona ukuthi uma i-pointer yengqikithi inguziro, bese sibuyisela u-1, futhi ngaphandle kwalokho - 2. Ake sihlole ukuthi siqinisile ngokubheka umthombo:
$ cat readelf-example.c
int foo(void *ctx)
{
return ctx ? 2 : 1;
}
Yebo, wuhlelo olungasho lutho, kodwa luhumushela emiyalweni emine nje elula.
Isibonelo esihlukile: imiyalo ye-16-byte
Sishilo ekuqaleni ukuthi eminye imiyalelo ithatha amabhithi angaphezu kuka-64. Lokhu kusebenza, isibonelo, emiyalweni lddw
(Ikhodi = 0x18
= BPF_LD
BPF_DW
BPF_IMM
Imm
. Iqiniso liwukuthi Imm
inosayizi ongu-32, futhi igama eliphindwe kabili lingamabhithi angu-64, ngakho ukulayisha inani elisheshayo le-64-bit kurejista kumyalelo owodwa we-64-bit ngeke kusebenze. Ukwenza lokhu, imiyalo emibili eseduze isetshenziswa ukugcina ingxenye yesibili yenani le-64-bit ensimini. Imm
. Isibonelo:
$ cat x64.c
long foo(void *ctx)
{
return 0x11223344aabbccdd;
}
$ clang -target bpf -c x64.c -o x64.o -O2
$ llvm-readelf -x .text x64.o
Hex dump of section '.text':
0x00000000 18000000 ddccbbaa 00000000 44332211 ............D3".
0x00000010 95000000 00000000 ........
Kuneziqondiso ezimbili kuphela kuhlelo olunambambili:
Binary Disassm
18000000 ddccbbaa 00000000 44332211 r0 = Imm[0]|Imm[1]
95000000 00000000 exit
Sizophinde sihlangane neziyalezo lddw
, uma sikhuluma ngokuthuthwa kanye nokusebenza ngamamephu.
Isibonelo: ukuqaqa i-BPF usebenzisa amathuluzi ajwayelekile
Ngakho-ke, sifunde ukufunda amakhodi kanambambili we-BPF futhi silungele ukuncozulula noma yimuphi umyalo uma kunesidingo. Kodwa-ke, kufanelekile ukusho ukuthi ekusebenzeni kulula futhi kuyashesha ukuqaqa izinhlelo usebenzisa amathuluzi ajwayelekile, isibonelo:
$ llvm-objdump -d x64.o
Disassembly of section .text:
0000000000000000 <foo>:
0: 18 00 00 00 dd cc bb aa 00 00 00 00 44 33 22 11 r0 = 1234605617868164317 ll
2: 95 00 00 00 00 00 00 00 exit
Umjikelezo wempilo wezinto ze-BPF, uhlelo lwefayela le-bpffs
(Ngiqale ngafunda eminye yemininingwane echazwe kulesi sigatshana
Izinto ze-BPF - izinhlelo namamephu - zidalwe endaweni yomsebenzisi kusetshenziswa imiyalo BPF_PROG_LOAD
ΠΈ BPF_MAP_CREATE
ikholi yesistimu bpf(2)
, sizokhuluma ngendlela lokhu okwenzeka ngayo esigabeni esilandelayo. Lokhu kudala izakhiwo zedatha ye-kernel futhi ngayinye yazo refcount
(isibalo sereferensi) isethelwe kokukodwa, futhi isichazi sefayela esikhomba entweni sibuyiselwa kumsebenzisi. Ngemva kokuba isibambo sivaliwe refcount
into iyancipha ngokukodwa, futhi lapho ifinyelela ku-zero, into iyabhujiswa.
Uma uhlelo lusebenzisa amamephu, ke refcount
lawa mamephu akhuphuka ngelinye ngemuva kokulayisha uhlelo, i.e. izichazi zefayela zabo zingavalwa kusukela kunqubo yomsebenzisi futhi kumile refcount
ngeke ibe nguziro:
Ngemva kokulayisha ngempumelelo uhlelo, ngokuvamile silunamathisela ohlotsheni oluthile lwejeneretha yomcimbi. Isibonelo, singayibeka kunethiwekhi yokuxhumana ukuze sicubungule amaphakethe angenayo noma siyixhume kwamanye tracepoint
emnyombeni. Kuleli qophelo, ikhawunta yereferensi nayo izokhuphuka ngokukodwa futhi sizokwazi ukuvala isichazi sefayela kuhlelo lwesilayishi.
Kwenzekani uma manje sivala i-bootloader? Kuya ngohlobo lwejeneretha yomcimbi (hook). Zonke izingwegwe zenethiwekhi zizoba khona ngemuva kokuthi isilayishi sesiqediwe, lawa abizwa ngokuthi ama-hook global. Futhi, ngokwesibonelo, izinhlelo zokulandela umkhondo zizokhishwa ngemuva kokuthi inqubo ezakhiwe inqanyuliwe (ngakho-ke zibizwa ngokuthi zendawo, kusukela "kwendawo kuya enqubweni"). Ngobuchwepheshe, izingwegwe zendawo zihlala zinezichazi zefayela ezihambisanayo endaweni yomsebenzisi futhi ngenxa yalokho ziyavala lapho inqubo ivaliwe, kodwa izingwegwe zomhlaba wonke azinawo. Emfanekisweni olandelayo, ngisebenzisa iziphambano ezibomvu, ngizama ukukhombisa ukuthi ukuqedwa kohlelo lwe-loader kuthinta kanjani isikhathi sokuphila sezinto esimweni sezingwegwe zendawo nezomhlaba jikelele.
Kungani kunomehluko phakathi kwamahhuku asekhaya nawomhlaba? Ukugijima ezinye izinhlobo zezinhlelo zenethiwekhi kunengqondo ngaphandle kwendawo yomsebenzisi, isibonelo, cabanga ukuvikelwa kwe-DDoS - i-bootloader ibhala imithetho futhi ixhuma uhlelo lwe-BPF ku-interface yenethiwekhi, emva kwalokho i-bootloader ingahamba futhi izibulale. Ngakolunye uhlangothi, cabanga ngohlelo lokulandelela lokususa iphutha olubhale emadolweni akho emizuzwini eyishumi - uma seluqediwe, ungathanda kungabikho udoti osele ohlelweni, futhi izingwegwe zendawo zizoqinisekisa lokho.
Ngakolunye uhlangothi, cabanga ukuthi ufuna ukuxhuma ku-tracepoint ku-kernel futhi uqoqe izibalo eminyakeni eminingi. Kulesi simo, ungafuna ukuqedela ingxenye yomsebenzisi bese ubuyela kuzibalo ngezikhathi ezithile. Isistimu yefayela ye-bpf inikeza leli thuba. Kuwuhlelo lwefayela mbumbulu lwenkumbulo kuphela oluvumela ukwakhiwa kwamafayela abhekisela ezintweni ze-BPF futhi ngaleyo ndlela ande. refcount
izinto. Ngemuva kwalokhu, isilayishi singaphuma, futhi izinto ezizidalile zizohlala ziphila.
Ukudala amafayela ngama-bpff abhekisela kuzinto ze-BPF kubizwa ngokuthi "ukuphina" (njengasemshweni olandelayo: "inqubo ingaphina uhlelo lwe-BPF noma imephu"). Ukudala izinto zefayela lezinto ze-BPF kunengqondo hhayi kuphela ekwandiseni impilo yezinto zasendaweni, kodwa futhi nokusebenziseka kwezinto zomhlaba - sibuyela esibonelweni ngohlelo lokuvikela lwe-DDoS lomhlaba wonke, sifuna ukukwazi ukuza futhi sibheke izibalo. ngezikhathi ezithile.
Isistimu yefayela ye-BPF ivamise ukufakwa phakathi /sys/fs/bpf
, kodwa ingafakwa futhi endaweni, isibonelo, kanje:
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint
Amagama esistimu yefayela akhiwa kusetshenziswa umyalo BPF_OBJ_PIN
Ucingo lwesistimu ye-BPF. Ukufanekisa, ake sithathe uhlelo, siluhlanganise, silulayishe, bese siphinela kulo bpffs
. Uhlelo lwethu alwenzi lutho oluwusizo, sethula kuphela ikhodi ukuze ukwazi ukukhiqiza kabusha isibonelo:
$ cat test.c
__attribute__((section("xdp"), used))
int test(void *ctx)
{
return 0;
}
char _license[] __attribute__((section("license"), used)) = "GPL";
Masihlanganise lolu hlelo futhi sakhe ikhophi yendawo yesistimu yefayela bpffs
:
$ clang -target bpf -c test.c -o test.o
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint
Manje ake silande uhlelo lwethu sisebenzisa insiza bpftool
bese ubheka izingcingo zesistimu ezihambisana naso bpf(2)
(eminye imigqa engabalulekile isusiwe ekuphumeni kwe-strace):
$ sudo strace -e bpf bpftool prog load ./test.o bpf-mountpoint/test
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="test", ...}, 120) = 3
bpf(BPF_OBJ_PIN, {pathname="bpf-mountpoint/test", bpf_fd=3}, 120) = 0
Lapha silayishe uhlelo sisebenzisa BPF_PROG_LOAD
, ithole isichazi sefayela ku-kernel 3
nokusebenzisa umyalo BPF_OBJ_PIN
uphine lesi sichazi sefayela njengefayela "bpf-mountpoint/test"
. Ngemva kwalokhu uhlelo lwe-bootloader bpftool
siqedile ukusebenza, kodwa uhlelo lwethu lwahlala ku-kernel, nakuba singazange silinamathisele kunoma iyiphi i-interface yenethiwekhi:
$ sudo bpftool prog | tail -3
783: xdp name test tag 5c8ba0cf164cb46c gpl
loaded_at 2020-05-05T13:27:08+0000 uid 0
xlated 24B jited 41B memlock 4096B
Singasusa into yefayela ngokujwayelekile unlink(2)
futhi ngemva kwalokho uhlelo oluhambisanayo luzosuswa:
$ sudo rm ./bpf-mountpoint/test
$ sudo bpftool prog show id 783
Error: get by id (783): No such file or directory
Isusa izinto
Ekhuluma ngokususa izinto, kuyadingeka ukucacisa ukuthi ngemuva kokunqamula uhlelo ku-hook (ijeneretha yomcimbi), akukho nowodwa umcimbi omusha ozoqala ukwethulwa, noma kunjalo, zonke izimo zamanje zohlelo zizoqedwa ngendlela evamile. .
Ezinye izinhlobo zezinhlelo ze-BPF zikuvumela ukuthi umiselele uhlelo ngokuphazima kweso, i.e. hlinzeka nge-athomu yokulandelana replace = detach old program, attach new program
. Kulokhu, zonke izenzakalo ezisebenzayo zenguqulo endala yohlelo zizoqeda umsebenzi wazo, futhi izibambi zomcimbi ezintsha zizodalwa kusukela kuhlelo olusha, futhi βi-atomicityβ lapha isho ukuthi awukho umcimbi ozogejwa.
Ukunamathisela izinhlelo emithonjeni yomcimbi
Kulesi sihloko, ngeke sichaze ngokuhlukana izinhlelo zokuxhuma emithonjeni yomcimbi, ngoba kunengqondo ukufunda lokhu kumongo wohlobo oluthile lohlelo. Cm.
Ukuphatha Izinto Ngokusebenzisa Ucingo Lwesistimu lwe-bpf
Izinhlelo ze-BPF
Zonke izinto ze-BPF zidalwa futhi ziphathwa endaweni yomsebenzisi kusetshenziswa ikholi yesistimu bpf
, enale prototype elandelayo:
#include <linux/bpf.h>
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
Nali iqembu cmd
ingenye yamanani ohlobo enum bpf_cmd
attr
β i-pointer kumapharamitha wohlelo oluthile kanye size
- usayizi wento ngokwesikhombi, i.e. ngokuvamile lokhu sizeof(*attr)
. Ku-kernel 5.8 ikholi yesistimu bpf
isekela imiyalo ehlukene engama-34, futhi union bpf_attr
ithatha imigqa engama-200. Kodwa akufanele kusatshiswe yilokhu, ngoba sizobe sizijwayeza imiyalo nemingcele phakathi nezihloko ezimbalwa.
Ake siqale ngeqembu BPF_PROG_LOAD
, edala izinhlelo ze-BPF - ithatha isethi yemiyalelo ye-BPF futhi ilayishe ku-kernel. Ngesikhathi sokulayisha, isiqinisekisi siyethulwa, bese kuba i-JIT compiler futhi, ngemva kokwenza ngempumelelo, isichazi sefayela lohlelo sibuyiselwa kumsebenzisi. Sibonile ukuthi kwenzekani kuye ngokulandelayo esigabeni esandulele
Manje sizobhala uhlelo lwangokwezifiso oluzolayisha uhlelo olulula lwe-BPF, kodwa okokuqala sidinga ukunquma ukuthi hlobo luni lohlelo esifuna ukululayisha - kuzodingeka sikhethe BPF_PROG_TYPE_XDP
, okuzobuyisela inani XDP_PASS
(yeqa wonke amaphakheji). Ku-assembler ye-BPF kubonakala kulula kakhulu:
r0 = 2
exit
Ngemva kokuba sesinqumile lokho sizolayisha, singakutshela ukuthi sizokwenza kanjani:
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
}
int main(void)
{
struct bpf_insn insns[] = {
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_0,
.imm = XDP_PASS
},
{
.code = BPF_JMP | BPF_EXIT
},
};
union bpf_attr attr = {
.prog_type = BPF_PROG_TYPE_XDP,
.insns = ptr_to_u64(insns),
.insn_cnt = sizeof(insns)/sizeof(insns[0]),
.license = ptr_to_u64("GPL"),
};
strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
for ( ;; )
pause();
}
Imicimbi ethokozisayo kuhlelo iqala ngencazelo yamalungu afanayo insns
- Uhlelo lwethu lwe-BPF ngekhodi yomshini. Kulokhu, imiyalelo ngayinye yohlelo lwe-BPF igcwele esakhiweni bpf_insn
insns
ihambisana nemiyalelo r0 = 2
, owesibili - exit
.
Hlehla. I-kernel ichaza ama-macros alula kakhulu wokubhala amakhodi omshini, nokusebenzisa ifayela lesihloko se-kernel tools/include/linux/filter.h
singabhala
struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
BPF_EXIT_INSN()
};
Kodwa njengoba ukubhala izinhlelo ze-BPF ngekhodi yomdabu kudingekile kuphela ekubhaleni izivivinyo ku-kernel nezindatshana ezimayelana ne-BPF, ukungabi khona kwala ma-macro akubeki inkimbinkimbi ngempela impilo yonjiniyela.
Ngemva kokuchaza uhlelo lwe-BPF, siqhubekela phambili ekulayisheni ku-kernel. Isethi yethu yamapharamitha amancane attr
ihlanganisa uhlobo lohlelo, isethi nenombolo yemiyalelo, ilayisense edingekayo, negama "woo"
, esiyisebenzisayo ukuthola uhlelo lwethu ohlelweni ngemva kokulanda. Uhlelo, njengoba luthenjisiwe, lulayishwa ohlelweni kusetshenziswa ikholi yesistimu bpf
.
Ekupheleni kohlelo sigcina sesiku-loop engapheli elingisa ukukhokhelwa. Ngaphandle kwalo, uhlelo luzobulawa i-kernel lapho isichazi sefayela esibuyiselwe ucingo lwesistimu kithi sivaliwe. bpf
, futhi ngeke sikubone ohlelweni.
Hhayi-ke, sesilungele ukuhlolwa. Asihlanganise futhi siqhube uhlelo ngaphansi strace
ukuhlola ukuthi yonke into isebenza ngendlela okufanele:
$ clang -g -O2 simple-prog.c -o simple-prog
$ sudo strace ./simple-prog
execve("./simple-prog", ["./simple-prog"], 0x7ffc7b553480 /* 13 vars */) = 0
...
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0x7ffe03c4ed50, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_V
ERSION(0, 0, 0), prog_flags=0, prog_name="woo", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS}, 72) = 3
pause(
Konke kuhamba kahle, bpf(2)
wabuyisela isibambo esingu-3 kithi futhi sangena ku-loop engapheli nge pause()
. Ake sizame ukuthola uhlelo lwethu ohlelweni. Ukuze senze lokhu sizoya kwenye i-terminal futhi sisebenzise insiza bpftool
:
# bpftool prog | grep -A3 woo
390: xdp name woo tag 3b185187f1855c4c gpl
loaded_at 2020-08-31T24:66:44+0000 uid 0
xlated 16B jited 40B memlock 4096B
pids simple-prog(10381)
Siyabona ukuthi kunohlelo olulayishiwe ohlelweni woo
I-ID yakhe yomhlaba wonke ingu-390 futhi isaqhubeka simple-prog
kukhona isichazi sefayela esivulekile esikhomba uhlelo (futhi uma simple-prog
uzoqeda umsebenzi ke woo
izonyamalala). Njengoba bekulindelekile, uhlelo woo
ithatha amabhayithi angu-16 - imiyalelo emibili - yamakhodi kanambambili ekwakhiweni kwe-BPF, kodwa ngesimo sayo somdabu (x86_64) isivele ingamabhayithi angu-40. Ake sibheke uhlelo lwethu ngendlela yalo yokuqala:
# bpftool prog dump xlated id 390
0: (b7) r0 = 2
1: (95) exit
akukho okumangalisayo. Manje ake sibheke ikhodi ekhiqizwe i-JIT compiler:
# bpftool prog dump jited id 390
bpf_prog_3b185187f1855c4c_woo:
0: nopl 0x0(%rax,%rax,1)
5: push %rbp
6: mov %rsp,%rbp
9: sub $0x0,%rsp
10: push %rbx
11: push %r13
13: push %r14
15: push %r15
17: pushq $0x0
19: mov $0x2,%eax
1e: pop %rbx
1f: pop %r15
21: pop %r14
23: pop %r13
25: pop %rbx
26: leaveq
27: retq
ayisebenzi kahle kakhulu exit(2)
, kodwa ngokungenzeleli, uhlelo lwethu lulula kakhulu, futhi ezinhlelweni ezingasho lutho isandulela kanye nesiqephu esengezwe umdidiyeli we-JIT, yiqiniso, siyadingeka.
Amamephu
Izinhlelo ze-BPF zingasebenzisa izindawo zememori ezihlelekile ezifinyeleleka kokubili kwezinye izinhlelo ze-BPF nasezinhlelweni ezisesikhaleni somsebenzisi. Lezi zinto zibizwa ngokuthi amamephu futhi kulesi sigaba sizobonisa ukuthi ziphathwa kanjani kusetshenziswa ucingo lwesistimu bpf
.
Masithi khona manjalo amandla wamamephu awagcini nje ngokufinyelela kumemori eyabiwe. Kunamamephu anezinjongo ezikhethekile aqukethe, ngokwesibonelo, izikhombi zezinhlelo ze-BPF noma izikhombi eziya endaweni yokusebenzelana yenethiwekhi, amamephu okusebenza ngemicimbi ye-perf, njll. Ngeke sikhulume ngazo lapha, ukuze singaphambanisi umfundi. Ngaphandle kwalokhu, siziba izindaba zokuvumelanisa, njengoba lokhu akubalulekile ezibonelweni zethu. Uhlu oluphelele lwezinhlobo zemephu ezitholakalayo lungatholakala ku <linux/bpf.h>
BPF_MAP_TYPE_HASH
.
Uma udala ithebula le-hash phakathi, ithi, C++, ungasho unordered_map<int,long> woo
, okusho ukuthi ngesiRashiya βNgidinga itafula woo
usayizi ongenamkhawulo, okhiye bayo bawuhlobo int
, futhi amanani awuhlobo long
" Ukuze sakhe ithebula le-BPF hashi, sidinga ukwenza into efanayo, ngaphandle kokuthi kufanele sicacise usayizi omkhulu wetafula, futhi esikhundleni sokucacisa izinhlobo zokhiye namanani, sidinga ukucacisa osayizi babo ngamabhayithi. . Ukwakha amamephu sebenzisa umyalo BPF_MAP_CREATE
ikholi yesistimu bpf
. Ake sibheke uhlelo oluncane kakhulu noma oluncane olwakha imephu. Ngemuva kohlelo lwangaphambilini olulayisha izinhlelo ze-BPF, lolu kufanele lubonakale lulula kuwe:
$ cat simple-map.c
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/bpf.h>
int main(void)
{
union bpf_attr attr = {
.map_type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 4,
};
strncpy(attr.map_name, "woo", sizeof(attr.map_name));
syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
for ( ;; )
pause();
}
Lapha sichaza isethi yamapharamitha attr
, lapho sithi βNgidinga ithebula le-hashi elinokhiye namanani kasayizi sizeof(int)
, lapho ngingafaka khona ubuningi bezakhi ezine." Lapho udala amamephu e-BPF, ungacacisa amanye amapharamitha, ngokwesibonelo, ngendlela efanayo naleyo esibonelweni sohlelo, sicacise igama lento njenge "woo"
.
Masihlanganise futhi siqalise uhlelo:
$ clang -g -O2 simple-map.c -o simple-map
$ sudo strace ./simple-map
execve("./simple-map", ["./simple-map"], 0x7ffd40a27070 /* 14 vars */) = 0
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_HASH, key_size=4, value_size=4, max_entries=4, map_name="woo", ...}, 72) = 3
pause(
Nansi ikholi yesistimu bpf(2)
usibuyisele inombolo yemephu esichazayo 3
bese uhlelo, njengoba kulindelekile, lulinde eminye imiyalelo ocingweni lwesistimu pause(2)
.
Manje ake sithumele uhlelo lwethu ngemuva noma sivule enye i-terminal futhi sibheke into yethu sisebenzisa insiza bpftool
(singahlukanisa imephu yethu kwamanye ngamagama ayo):
$ sudo bpftool map
...
114: hash name woo flags 0x0
key 4B value 4B max_entries 4 memlock 4096B
...
Inombolo 114 iyi-ID yomhlaba wonke yento yethu. Noma yiluphi uhlelo kusistimu lungasebenzisa le ID ukuvula imephu ekhona kusetshenziswa umyalo BPF_MAP_GET_FD_BY_ID
ikholi yesistimu bpf
.
Manje singadlala ngetafula lethu le-hash. Ake sibheke ekuqukethe:
$ sudo bpftool map dump id 114
Found 0 elements
Akunalutho. Ake sibeke inani kuyo hash[1] = 1
:
$ sudo bpftool map update id 114 key 1 0 0 0 value 1 0 0 0
Ake sibheke itafula futhi:
$ sudo bpftool map dump id 114
key: 01 00 00 00 value: 01 00 00 00
Found 1 element
Hooray! Sikwazile ukwengeza isici esisodwa. Qaphela ukuthi kufanele sisebenze ku-byte level ukwenza lokhu, kusukela bptftool
awazi ukuthi hlobo luni amanani kuthebula le-hashi. (Lolu lwazi lungadluliselwa kuye kusetshenziswa i-BTF, kodwa okwengeziwe ngalokho manje.)
Ngabe i-bpftool ifunda futhi yengeza kanjani izakhi? Ake sibheke ngaphansi kwe-hood:
$ sudo strace -e bpf bpftool map dump id 114
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=NULL, next_key=0x55856ab65280}, 120) = 0
bpf(BPF_MAP_LOOKUP_ELEM, {map_fd=3, key=0x55856ab65280, value=0x55856ab652a0}, 120) = 0
key: 01 00 00 00 value: 01 00 00 00
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=0x55856ab65280, next_key=0x55856ab65280}, 120) = -1 ENOENT
Okokuqala sivule imephu nge-ID yayo yomhlaba wonke sisebenzisa umyalo BPF_MAP_GET_FD_BY_ID
ΠΈ bpf(2)
ibuyisele isichazi 3 kithi. Ngokuqhubeka nokusebenzisa umyalo BPF_MAP_GET_NEXT_KEY
sithole ukhiye wokuqala etafuleni ngokudlula NULL
njengesikhombi kukhiye "odlule". Uma sinokhiye singenza BPF_MAP_LOOKUP_ELEM
okubuyisela inani kusikhombisi value
. Isinyathelo esilandelayo sizama ukuthola isici esilandelayo ngokudlulisela i-pointer kukhiye wamanje, kodwa ithebula lethu liqukethe into eyodwa kanye nomyalo. BPF_MAP_GET_NEXT_KEY
izimbuyiselo ENOENT
.
Kulungile, asiguqule inani ngokhiye 1, ake sithi ukucabanga kwebhizinisi lethu kudinga ukubhaliswa hash[1] = 2
:
$ sudo strace -e bpf bpftool map update id 114 key 1 0 0 0 value 2 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x55dcd72be260, value=0x55dcd72be280, flags=BPF_ANY}, 120) = 0
Njengoba kulindelekile, kulula kakhulu: umyalo BPF_MAP_GET_FD_BY_ID
ivula imephu yethu nge-ID, kanye nomyalo BPF_MAP_UPDATE_ELEM
ibhala phezu kwesici.
Ngakho-ke, ngemva kokudala ithebula le-hash ohlelweni olulodwa, singakwazi ukufunda nokubhala okuqukethwe kulo kusuka kwenye. Qaphela ukuthi uma sikwazile ukwenza lokhu kusukela kulayini womyalo, khona-ke noma yiluphi olunye uhlelo ohlelweni lungakwenza. Ngaphezu kwemiyalelo echazwe ngenhla, yokusebenza ngamamephu avela endaweni yomsebenzisi,
BPF_MAP_LOOKUP_ELEM
: thola inani ngokhiyeBPF_MAP_UPDATE_ELEM
: buyekeza/dala inaniBPF_MAP_DELETE_ELEM
: khipha ukhiyeBPF_MAP_GET_NEXT_KEY
: thola ukhiye olandelayo (noma wokuqala).BPF_MAP_GET_NEXT_ID
: ikuvumela ukuthi udlule kuwo wonke amamephu akhona, yindlela esebenza ngayobpftool map
BPF_MAP_GET_FD_BY_ID
: vula imephu ekhona nge-ID yayo yomhlaba wonkeBPF_MAP_LOOKUP_AND_DELETE_ELEM
: buyekeza nge-athomu inani lento bese ubuyisela endalaBPF_MAP_FREEZE
: yenza imephu ingaguquleki endaweni yomsebenzisi (lo msebenzi awukwazi ukuhlehliswa)BPF_MAP_LOOKUP_BATCH
,BPF_MAP_LOOKUP_AND_DELETE_BATCH
,BPF_MAP_UPDATE_BATCH
,BPF_MAP_DELETE_BATCH
: imisebenzi eminingi. Ngokwesibonelo,BPF_MAP_LOOKUP_AND_DELETE_BATCH
- lena ukuphela kwendlela ethembekile yokufunda nokusetha kabusha wonke amanani asuka kumephu
Akuyona yonke le miyalo esebenza kuzo zonke izinhlobo zamamephu, kodwa ngokuvamile ukusebenza nezinye izinhlobo zamamephu avela endaweni yomsebenzisi kubukeka kufana ncamashi nokusebenza ngamathebula e-hashi.
Ngenxa yokuhleleka, masiqedele ukuhlolwa kwethu kwetafula le-hashi. Khumbula ukuthi sidale ithebula elingaqukatha okhiye abane? Ake sengeze ezinye izici ezimbalwa:
$ sudo bpftool map update id 114 key 2 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 3 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 4 0 0 0 value 1 0 0 0
Kuze kube manje kuhle kakhulu:
$ sudo bpftool map dump id 114
key: 01 00 00 00 value: 01 00 00 00
key: 02 00 00 00 value: 01 00 00 00
key: 04 00 00 00 value: 01 00 00 00
key: 03 00 00 00 value: 01 00 00 00
Found 4 elements
Ake sizame ukwengeza eyodwa futhi:
$ sudo bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
Error: update failed: Argument list too long
Njengoba bekulindelekile, asiphumelelanga. Ake sibheke iphutha ngokuningiliziwe:
$ sudo strace -e bpf bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_OBJ_GET_INFO_BY_FD, {info={bpf_fd=3, info_len=80, info=0x7ffe6c626da0}}, 120) = 0
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x56049ded5260, value=0x56049ded5280, flags=BPF_ANY}, 120) = -1 E2BIG (Argument list too long)
Error: update failed: Argument list too long
+++ exited with 255 +++
Konke kuhamba kahle: njengoba bekulindelekile, iqembu BPF_MAP_UPDATE_ELEM
izama ukudala ukhiye omusha, wesihlanu, kodwa iyaphahlazeka E2BIG
.
Ngakho-ke, singakha futhi silayishe izinhlelo ze-BPF, futhi sidale futhi siphathe amamephu sisendaweni yomsebenzisi. Manje kunengqondo ukubheka ukuthi singasebenzisa kanjani amamephu avela ezinhlelweni ze-BPF ngokwazo. Singakhuluma ngalokhu ngolimi lwezinhlelo okunzima ukuzifunda kumakhodi amakhulu emishini, kodwa empeleni isikhathi sesifikile sokukhombisa ukuthi izinhlelo ze-BPF zibhalwa futhi zigcinwe kanjani - kusetshenziswa. libbpf
.
(Kubafundi abanganelisekile ngokuntuleka kwesibonelo esisezingeni eliphansi: sizohlaziya ngokuningiliziwe izinhlelo ezisebenzisa amamephu nemisebenzi yomsizi edalwe kusetshenziswa. libbpf
futhi ngikutshele ukuthi kwenzekani ezingeni lemiyalo. Kubafundi abanganelisekile kakhulu, sengeze
Ukubhala izinhlelo ze-BPF usebenzisa i-libbpf
Ukubhala izinhlelo ze-BPF usebenzisa amakhodi omshini kungathakazelisa okokuqala ngqa, bese kungena ukusutha. Kulo mzuzu udinga ukunaka kwakho llvm
, ene-backend yokukhiqiza ikhodi yezakhiwo ze-BPF, kanye nomtapo wolwazi libbpf
, okukuvumela ukuthi ubhale uhlangothi lomsebenzisi lwezinhlelo zokusebenza ze-BPF futhi ulayishe ikhodi yezinhlelo ze-BPF ezikhiqizwe kusetshenziswa llvm
/clang
.
Eqinisweni, njengoba sizobona kulesi sihloko nakwelandelayo, libbpf
yenza umsebenzi omningi ngaphandle kwayo (noma amathuluzi afanayo - iproute2
, libbcc
, libbpf-go
, njll.) akunakwenzeka ukuphila. Esinye sezici ezibulalayo zephrojekthi libbpf
yi-BPF CO-RE (Compile Kanye, Run Everywhere) - iphrojekthi ekuvumela ukuthi ubhale izinhlelo ze-BPF eziphathwayo kusuka ku-kernel eyodwa kuya kwenye, ezinekhono lokusebenzisa ama-API ahlukene (isibonelo, lapho ukwakheka kwe-kernel kushintsha kusuka enguqulweni kuya kunguqulo). Ukuze ukwazi ukusebenza ne-CO-RE, i-kernel yakho kufanele ihlanganiswe nokusekelwa kwe-BTF (sichaza ukuthi ungakwenza kanjani lokhu esigabeni
$ ls -lh /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 2.6M Jul 29 15:30 /sys/kernel/btf/vmlinux
Leli fayela ligcina ulwazi olumayelana nazo zonke izinhlobo zedatha ezisetshenziswa ku-kernel futhi lisetshenziswa kuzo zonke izibonelo zethu kusetshenziswa libbpf
. Sizokhuluma ngokuningiliziwe mayelana ne-CO-RE esihlokweni esilandelayo, kodwa kulesi - vele uzakhele i-kernel CONFIG_DEBUG_INFO_BTF
.
umtapo libbpf
uhlala ngqo ohlwini lwemibhalo tools/lib/bpf
I-kernel kanye nokuthuthukiswa kwayo kwenziwa ngohlu lwamakheli [email protected]
. Kodwa-ke, indawo yokugcina ehlukile iyagcinwa ngezidingo zezicelo ezihlala ngaphandle kwe-kernel
Kulesi sigaba sizobheka ukuthi ungakha kanjani iphrojekthi esebenzisa libbpf
, ake sibhale izinhlelo zokuhlola ezimbalwa (eziningi noma ezingasho lutho) futhi sihlaziye ngokuningiliziwe ukuthi konke kusebenza kanjani. Lokhu kuzosivumela ukuthi sichaze kalula ezigabeni ezilandelayo ukuthi izinhlelo ze-BPF zisebenzisana kanjani namamephu, abasizi be-kernel, i-BTF, njll.
Ngokujwayelekile amaphrojekthi asetshenziswa libbpf
engeza inqolobane ye-GitHub njenge-submodule ye-git, sizokwenza okufanayo:
$ mkdir /tmp/libbpf-example
$ cd /tmp/libbpf-example/
$ git init-db
Initialized empty Git repository in /tmp/libbpf-example/.git/
$ git submodule add https://github.com/libbpf/libbpf.git
Cloning into '/tmp/libbpf-example/libbpf'...
remote: Enumerating objects: 200, done.
remote: Counting objects: 100% (200/200), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 3354 (delta 101), reused 118 (delta 79), pack-reused 3154
Receiving objects: 100% (3354/3354), 2.05 MiB | 10.22 MiB/s, done.
Resolving deltas: 100% (2176/2176), done.
Iya ku libbpf
silula:
$ cd libbpf/src
$ mkdir build
$ OBJDIR=build DESTDIR=root make -s install
$ find root
root
root/usr
root/usr/include
root/usr/include/bpf
root/usr/include/bpf/bpf_tracing.h
root/usr/include/bpf/xsk.h
root/usr/include/bpf/libbpf_common.h
root/usr/include/bpf/bpf_endian.h
root/usr/include/bpf/bpf_helpers.h
root/usr/include/bpf/btf.h
root/usr/include/bpf/bpf_helper_defs.h
root/usr/include/bpf/bpf.h
root/usr/include/bpf/libbpf_util.h
root/usr/include/bpf/libbpf.h
root/usr/include/bpf/bpf_core_read.h
root/usr/lib64
root/usr/lib64/libbpf.so.0.1.0
root/usr/lib64/libbpf.so.0
root/usr/lib64/libbpf.a
root/usr/lib64/libbpf.so
root/usr/lib64/pkgconfig
root/usr/lib64/pkgconfig/libbpf.pc
Uhlelo lwethu olulandelayo kulesi sigaba lumi kanje: sizobhala uhlelo lwe-BPF njenge BPF_PROG_TYPE_XDP
, okufanayo nakwesibonelo sangaphambilini, kodwa ku-C, siyihlanganisa sisebenzisa clang
, bese ubhala uhlelo lomsizi oluzoyilayisha ku-kernel. Ezigabeni ezilandelayo sizonweba amandla akho kokubili uhlelo lwe-BPF kanye nohlelo lomsizi.
Isibonelo: ukudala uhlelo lokusebenza olugcwele usebenzisa i-libbpf
Ukuqala, sisebenzisa ifayela /sys/kernel/btf/vmlinux
, okukhulunywe ngakho ngenhla, futhi adale okulingana nayo ngendlela yefayela elingasekhanda:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
Leli fayela lizogcina zonke izakhiwo zedatha ezitholakala ku-kernel yethu, isibonelo, le yindlela unhlokweni we-IPv4 ochazwa ngayo ku-kernel:
$ grep -A 12 'struct iphdr {' vmlinux.h
struct iphdr {
__u8 ihl: 4;
__u8 version: 4;
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
};
Manje sizobhala uhlelo lwethu lwe-BPF ngo-C:
$ cat xdp-simple.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("xdp/simple")
int simple(void *ctx)
{
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Nakuba uhlelo lwethu lube lula kakhulu, sisadinga ukunaka imininingwane eminingi. Okokuqala, ifayela lokuqala likanhlokweni esilifakayo lithi vmlinux.h
, esisanda kuyenza sisebenzisa bpftool btf dump
- manje asidingi ukufaka iphakheji ye-kernel-headers ukuze sithole ukuthi izakhiwo ze-kernel zibukeka kanjani. Ifayela elingunhlokweni elilandelayo liza kithi livela kulabhulali libbpf
. Manje siyidinga kuphela ukuchaza i-macro SEC
, okuthumela uhlamvu esigabeni esifanele sefayela lento ye-ELF. Uhlelo lwethu luqukethwe esigabeni xdp/simple
, lapho ngaphambi kwe-slash sichaza uhlobo lohlelo lwe-BPF - lena yingqungquthela esetshenziswa ku libbpf
, ngokusekelwe egameni lesigaba izoshintsha uhlobo olulungile ekuqaleni bpf(2)
. Uhlelo lwe-BPF ngokwalo C
- ilula kakhulu futhi iqukethe umugqa owodwa return XDP_PASS
. Ekugcineni, isigaba esihlukile "license"
iqukethe igama lelayisensi.
Singahlanganisa uhlelo lwethu sisebenzisa i-llvm/clang, inguqulo >= 10.0.0, noma okungcono nokho, okukhulu nakakhulu (bona isigaba
$ clang --version
clang version 11.0.0 (https://github.com/llvm/llvm-project.git afc287e0abec710398465ee1f86237513f2b5091)
...
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
Phakathi kwezici ezithakazelisayo: sibonisa isakhiwo esihlosiwe -target bpf
kanye nendlela eya kumaheda libbpf
, esisanda kuyifaka. Futhi, ungakhohlwa mayelana -O2
, ngaphandle kwale nketho ungase ube nezimanga esikhathini esizayo. Ake sibheke ikhodi yethu, sikwazile ukubhala uhlelo ebesilufuna?
$ llvm-objdump --section=xdp/simple --no-show-raw-insn -D xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: r0 = 2
1: exit
Yebo, kwasebenza! Manje, sinefayela kanambambili nohlelo, futhi sifuna ukwakha uhlelo lokusebenza oluzoyilayisha ku-kernel. Ngale njongo umtapo wolwazi libbpf
isinika izinketho ezimbili - sebenzisa i-API yezinga eliphansi noma i-API yezinga eliphezulu. Sizohamba ngendlela yesibili, njengoba sifuna ukufunda ukubhala, ukulayisha nokuxhuma izinhlelo ze-BPF ngomzamo omncane wokufunda kwazo okwalandela.
Okokuqala, sidinga ukukhiqiza "uhlaka lwamathambo" lohlelo lwethu kusuka kanambambili walo sisebenzisa insiza efanayo bpftool
- ummese waseSwitzerland womhlaba we-BPF (ongathathwa njengoba unjalo, njengoba uDaniel Borkman, omunye wabadali nabanakekeli be-BPF, engumSwitzerland):
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
Kufayela xdp-simple.skel.h
iqukethe ikhodi kanambambili yohlelo lwethu nemisebenzi yokuphatha - ukulayisha, ukunamathisela, ukususa into yethu. Esimeni sethu esilula lokhu kubukeka njengokweqile, kodwa futhi kusebenza esimweni lapho ifayela lezinto liqukethe izinhlelo eziningi ze-BPF namamephu futhi ukulayisha le ELF enkulu sidinga nje ukukhiqiza uhlaka lwamathambo futhi sibize umsebenzi owodwa noma emibili kusuka kuhlelo lokusebenza lwangokwezifiso esinalo. bayabhala Asiqhubeke manje.
Uma sikhuluma nje, uhlelo lwethu lokulayishwa luyinto encane:
#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"
int main(int argc, char **argv)
{
struct xdp_simple_bpf *obj;
obj = xdp_simple_bpf__open_and_load();
if (!obj)
err(1, "failed to open and/or load BPF objectn");
pause();
xdp_simple_bpf__destroy(obj);
}
kuyinto struct xdp_simple_bpf
kuchazwe kufayela xdp-simple.skel.h
futhi ichaza ifayela lethu lezinto:
struct xdp_simple_bpf {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_program *simple;
} progs;
struct {
struct bpf_link *simple;
} links;
};
Singabona ukulandelana kwe-API yezinga eliphansi lapha: isakhiwo struct bpf_program *simple
ΠΈ struct bpf_link *simple
. Isakhiwo sokuqala sichaza ngokuqondile uhlelo lwethu, olubhalwe esigabeni xdp/simple
, bese eyesibili ichaza ukuthi uhlelo luxhumeka kanjani kumthombo womcimbi.
Umsebenzi xdp_simple_bpf__open_and_load
, ivula into ye-ELF, iyihlukanise, idale zonke izakhiwo nezigatshana (ngaphandle kohlelo, i-ELF iqukethe nezinye izigaba - idatha, idatha yokufunda kuphela, ulwazi lokulungisa iphutha, ilayisensi, njll.), bese ilayisha ku-kernel usebenzisa uhlelo. shayela bpf
, esingakuhlola ngokuhlanganisa nokusebenzisa uhlelo:
$ clang -O2 -I ./libbpf/src/root/usr/include/ xdp-simple.c -o xdp-simple ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_BTF_LOAD, 0x7ffdb8fd9670, 120) = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0xdfd580, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 8, 0), prog_flags=0, prog_name="simple", prog_ifindex=0, expected_attach_type=0x25 /* BPF_??? */, ...}, 120) = 4
Manje ake sibheke uhlelo lwethu sisebenzisa bpftool
. Masithole i-ID yakhe:
# bpftool p | grep -A4 simple
463: xdp name simple tag 3b185187f1855c4c gpl
loaded_at 2020-08-01T01:59:49+0000 uid 0
xlated 16B jited 40B memlock 4096B
btf_id 185
pids xdp-simple(16498)
futhi silahle (sisebenzisa ifomu elifushanisiwe lomyalo bpftool prog dump xlated
):
# bpftool p d x id 463
int simple(void *ctx):
; return XDP_PASS;
0: (b7) r0 = 2
1: (95) exit
Okuthile okusha! Uhlelo luphrinte izingcezu zefayela lethu elingumthombo C. Lokhu kwenziwa umtapo wolwazi libbpf
, ethole isigaba sokususa iphutha kukhombambili, yasihlanganisa entweni ye-BTF, yasilayisha ku-kernel isebenzisa BPF_BTF_LOAD
, bese icacisa isichazi sefayela esiwumphumela lapho silayisha uhlelo ngomyalo BPG_PROG_LOAD
.
Abasizi beKernel
Izinhlelo ze-BPF zingasebenzisa imisebenzi βyangaphandleβ - abasizi be-kernel. Le misebenzi yomsizi ivumela izinhlelo ze-BPF ukuthi zifinyelele izakhiwo ze-kernel, zilawule amamephu, futhi zixhumane "nomhlaba wangempela" - dala imicimbi ye-perf, lawula izingxenyekazi zekhompuyutha (isibonelo, ukuqondisa kabusha amaphakethe), njll.
Isibonelo: bpf_get_smp_processor_id
Ngaphakathi kohlaka lwepharadigm "yokufunda ngesibonelo", ake sicabangele omunye wemisebenzi yomsizi, bpf_get_smp_processor_id()
, kernel/bpf/helpers.c
. Ibuyisela inombolo yephrosesa lapho uhlelo lwe-BPF oluyibizile lusebenza khona. Kepha asinantshisekelo kuma-semantics ayo njengaseqinisweni lokuthi ukusetshenziswa kwayo kuthatha umugqa owodwa:
BPF_CALL_0(bpf_get_smp_processor_id)
{
return smp_processor_id();
}
Izincazelo zomsebenzi womsizi we-BPF ziyefana nezincazelo zekholi yesistimu ye-Linux. Lapha, isibonelo, umsebenzi uchazwa ongenakho ukuphikisana. (Umsebenzi othatha, uthi, izimpikiswano ezintathu zichazwa kusetshenziswa i-macro BPF_CALL_3
. Inombolo enkulu yezimpikiswano ezinhlanu.) Nokho, lena ingxenye yokuqala kuphela yencazelo. Ingxenye yesibili iwukuchaza uhlobo lwesakhiwo struct bpf_func_proto
, equkethe incazelo yomsebenzi womsizi oqondwa isiqinisekisi:
const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
.func = bpf_get_smp_processor_id,
.gpl_only = false,
.ret_type = RET_INTEGER,
};
Ukubhalisa Imisebenzi Yomsizi
Ukuze izinhlelo ze-BPF zohlobo oluthile zisebenzise lo msebenzi, kufanele ziwubhalise, isibonelo ngohlobo BPF_PROG_TYPE_XDP
umsebenzi uchazwa ku-kernel xdp_func_proto
, enquma kusukela ku-ID yomsebenzi womsizi ukuthi i-XDP iyawusekela yini lo msebenzi noma cha. Umsebenzi wethu
static const struct bpf_func_proto *
xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
...
case BPF_FUNC_get_smp_processor_id:
return &bpf_get_smp_processor_id_proto;
...
}
}
Izinhlobo zohlelo olusha lwe-BPF "ziyachazwa" kufayela include/linux/bpf_types.h
BPF_PROG_TYPE
. Ichazwa ngezingcaphuno ngenxa yokuthi iyincazelo enengqondo, futhi ngolimi C incazelo yeqoqo lonke lezakhiwo eziphathekayo yenzeka kwezinye izindawo. Ikakhulukazi, kufayela kernel/bpf/verifier.c
zonke izincazelo ezivela kufayela bpf_types.h
zisetshenziselwa ukwakha uxhaxha lwezakhiwo bpf_verifier_ops[]
:
static const struct bpf_verifier_ops *const bpf_verifier_ops[] = {
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
[_id] = & _name ## _verifier_ops,
#include <linux/bpf_types.h>
#undef BPF_PROG_TYPE
};
Okusho ukuthi, kuhlobo ngalunye lohlelo lwe-BPF, kuchazwa isikhombi esakhiweni sedatha sohlobo struct bpf_verifier_ops
, eqaliswa ngevelu _name ## _verifier_ops
, i.e., xdp_verifier_ops
ngoba xdp
. Isakhiwo xdp_verifier_ops
net/core/filter.c
kanje:
const struct bpf_verifier_ops xdp_verifier_ops = {
.get_func_proto = xdp_func_proto,
.is_valid_access = xdp_is_valid_access,
.convert_ctx_access = xdp_convert_ctx_access,
.gen_prologue = bpf_noop_prologue,
};
Lapha sibona umsebenzi wethu esiwujwayele xdp_func_proto
, ezosebenzisa isiqinisekisi njalo lapho ihlangabezana nenselelo abanye isebenza ngaphakathi kohlelo lwe-BPF, bheka verifier.c
Ake sibheke ukuthi uhlelo lwe-BPF lokucabanga lusebenzisa kanjani umsebenzi bpf_get_smp_processor_id
. Ukwenza lokhu, sibhala kabusha uhlelo kusuka esigabeni sethu sangaphambilini ngendlela elandelayo:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("xdp/simple")
int simple(void *ctx)
{
if (bpf_get_smp_processor_id() != 0)
return XDP_DROP;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Π‘ΠΈΠΌΠ²ΠΎΠ» bpf_get_smp_processor_id
<bpf/bpf_helper_defs.h>
imitapo yolwazi libbpf
kanjani
static u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;
leyo, bpf_get_smp_processor_id
iyisikhombi somsebenzi esinani laso lingu-8, lapho u-8 eyinani BPF_FUNC_get_smp_processor_id
thayipha enum bpf_fun_id
, esichazelwe thina kufayela vmlinux.h
(ifayela bpf_helper_defs.h
ku-kernel kukhiqizwa umbhalo, ngakho izinombolo "zomlingo" zilungile). Lo msebenzi awuzithathi izimpikiswano futhi ubuyisela inani lohlobo __u32
. Uma siyiqhuba ohlelweni lwethu, clang
yakha umyalelo BPF_CALL
"uhlobo olulungile" Ake sihlanganise uhlelo futhi sibheke ingxenye xdp/simple
:
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ llvm-objdump -D --section=xdp/simple xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: bf 01 00 00 00 00 00 00 r1 = r0
2: 67 01 00 00 20 00 00 00 r1 <<= 32
3: 77 01 00 00 20 00 00 00 r1 >>= 32
4: b7 00 00 00 02 00 00 00 r0 = 2
5: 15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_2>
6: b7 00 00 00 01 00 00 00 r0 = 1
0000000000000038 <LBB0_2>:
7: 95 00 00 00 00 00 00 00 exit
Emgqeni wokuqala sibona imiyalelo call
, ipharamitha IMM
elilingana no-8, futhi SRC_REG
-ziro. Ngokwesivumelwano se-ABI esisetshenziswa isiqinisekisi, lolu wucingo oluya kunombolo yesishiyagalombili yomsebenzi womsizi. Uma seyethuliwe, i-logic ilula. Buyisela inani elivela kurejista r0
ikopishelwe ku r1
futhi emigqeni 2,3 iguqulwa ibe uhlobo u32
- amabhithi aphezulu angama-32 asuliwe. Elayini 4,5,6,7 sibuyisela 2 (XDP_PASS
) noma 1 (XDP_DROP
) kuye ngokuthi umsebenzi womsizi osuka kulayini 0 ubuyise inani elinguziro noma elingelona uziro.
Ake sizihlole: layisha uhlelo futhi sibheke okukhiphayo bpftool prog dump xlated
:
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple &
[2] 10914
$ sudo bpftool p | grep simple
523: xdp name simple tag 44c38a10c657e1b0 gpl
pids xdp-simple(10915)
$ sudo bpftool p d x id 523
int simple(void *ctx):
; if (bpf_get_smp_processor_id() != 0)
0: (85) call bpf_get_smp_processor_id#114128
1: (bf) r1 = r0
2: (67) r1 <<= 32
3: (77) r1 >>= 32
4: (b7) r0 = 2
; }
5: (15) if r1 == 0x0 goto pc+1
6: (b7) r0 = 1
7: (95) exit
Kulungile, isiqinisekisi sithole umsizi we-kernel olungile.
Isibonelo: ukudlulisa izimpikiswano futhi ekugcineni uqhuba uhlelo!
Yonke imisebenzi yomsizi weleveli yokuqalisa ine-prototype
u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
Amapharamitha emisebenzi yomsizi adluliswa kumarejista r1
-r5
, futhi inani libuyiselwa kurejista r0
. Ayikho imisebenzi ethatha ama-agumenti angaphezu kwesihlanu, futhi ukusekelwa kwawo akulindelekile ukuthi kwengezwe ngokuzayo.
Ake sibheke umsizi omusha we-kernel nokuthi i-BPF idlula kanjani amapharamitha. Masibhale kabusha xdp-simple.bpf.c
kanje (eminye imigqa ayishintshile):
SEC("xdp/simple")
int simple(void *ctx)
{
bpf_printk("running on CPU%un", bpf_get_smp_processor_id());
return XDP_PASS;
}
Uhlelo lwethu luphrinta inombolo ye-CPU olusebenza kuyo. Masiyihlanganise futhi sibheke ikhodi:
$ llvm-objdump -D --section=xdp/simple --no-show-raw-insn xdp-simple.bpf.o
0000000000000000 <simple>:
0: r1 = 10
1: *(u16 *)(r10 - 8) = r1
2: r1 = 8441246879787806319 ll
4: *(u64 *)(r10 - 16) = r1
5: r1 = 2334956330918245746 ll
7: *(u64 *)(r10 - 24) = r1
8: call 8
9: r1 = r10
10: r1 += -24
11: r2 = 18
12: r3 = r0
13: call 6
14: r0 = 2
15: exit
Emgqeni 0-7 sibhala intambo running on CPU%un
, bese kuthi emgqeni wesi-8 sisebenzise esijwayelekile bpf_get_smp_processor_id
. Emgqeni 9-12 silungiselela izimpikiswano zomsizi bpf_printk
- amarejista r1
, r2
, r3
. Kungani bebathathu hhayi ababili? Ngoba bpf_printk
- bpf_trace_printk
, okudingeka kudlule usayizi weyunithi yezinhlamvu yefomethi.
Manje ake sengeze imigqa embalwa kuyo xdp-simple.c
ukuze uhlelo lwethu luxhume esibonakalayo lo
futhi ngempela waqala!
$ cat xdp-simple.c
#include <linux/if_link.h>
#include <err.h>
#include <unistd.h>
#include "xdp-simple.skel.h"
int main(int argc, char **argv)
{
__u32 flags = XDP_FLAGS_SKB_MODE;
struct xdp_simple_bpf *obj;
obj = xdp_simple_bpf__open_and_load();
if (!obj)
err(1, "failed to open and/or load BPF objectn");
bpf_set_link_xdp_fd(1, -1, flags);
bpf_set_link_xdp_fd(1, bpf_program__fd(obj->progs.simple), flags);
cleanup:
xdp_simple_bpf__destroy(obj);
}
Lapha sisebenzisa umsebenzi bpf_set_link_xdp_fd
, exhuma izinhlelo ze-BPF zohlobo lwe-XDP kuzixhumi zenethiwekhi. Sifake ikhodi yesixhumi esibonakalayo lo
, okuhlala kungu-1. Sisebenzisa umsebenzi kabili ukuze siqale sikhiphe uhlelo oludala uma lunamathiselwe. Qaphela ukuthi manje asiyidingi inselelo pause
noma iluphu engapheli: uhlelo lwethu lokulayishwa luzophuma, kodwa uhlelo lwe-BPF ngeke lubulawe njengoba luxhunywe kumthombo womcimbi. Ngemuva kokulanda ngempumelelo nokuxhumeka, uhlelo luzokwethulwa kuphakethe lenethiwekhi ngalinye elifika kulo lo
.
Ake silande uhlelo futhi sibheke esibonakalayo lo
:
$ sudo ./xdp-simple
$ sudo bpftool p | grep simple
669: xdp name simple tag 4fca62e77ccb43d6 gpl
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 669
Uhlelo esiludawunilodayo luno-ID 669 futhi sibona i-ID efanayo kusixhumi esibonakalayo lo
. Sizothumela amaphakheji ambalwa kuwo 127.0.0.1
(cela + phendula):
$ ping -c1 localhost
futhi manje ake sibheke okuqukethwe kwefayela elibonakalayo lokususa iphutha /sys/kernel/debug/tracing/trace_pipe
, lapho bpf_printk
ubhala imiyalezo yakhe:
# cat /sys/kernel/debug/tracing/trace_pipe
ping-13937 [000] d.s1 442015.377014: bpf_trace_printk: running on CPU0
ping-13937 [000] d.s1 442015.377027: bpf_trace_printk: running on CPU0
Amaphakheji amabili abonwe lo
futhi yacutshungulwa ku-CPU0 - uhlelo lwethu lokuqala olugcwele oluphelele lwe-BPF lusebenzile!
Kumele uqaphele lokho bpf_printk
Akuyona ize ukuthi ibhalela ifayela lokususa iphutha: lokhu akuyena umsizi ophumelele kakhulu wokusetshenziswa ekukhiqizeni, kodwa umgomo wethu kwakuwukukhombisa into elula.
Ukufinyelela amamephu kusukela ezinhlelweni ze-BPF
Isibonelo: ukusebenzisa imephu yohlelo lwe-BPF
Ezigabeni ezedlule sifunde ukudala nokusebenzisa amamephu endaweni yomsebenzisi, futhi manje ake sibheke ingxenye ye-kernel. Ake siqale, njengenjwayelo, ngesibonelo. Asibhale kabusha uhlelo lwethu xdp-simple.bpf.c
kanje:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 8);
__type(key, u32);
__type(value, u64);
} woo SEC(".maps");
SEC("xdp/simple")
int simple(void *ctx)
{
u32 key = bpf_get_smp_processor_id();
u32 *val;
val = bpf_map_lookup_elem(&woo, &key);
if (!val)
return XDP_ABORTED;
*val += 1;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";
Ekuqaleni kohlelo sengeze incazelo yemephu woo
: Lona uhlu lwezinto ezingu-8 ezigcina amanani afana u64
(ku-C singachaza uhlu olunjalo ngokuthi u64 woo[8]
). Ohlelweni "xdp/simple"
sithola inombolo yephrosesa yamanje ibe okuguquguqukayo key
bese usebenzisa umsebenzi womsizi bpf_map_lookup_element
sithola i-pointer ekungeneni okuhambisanayo ohlwini, esikwandisa ngakunye. Kuhunyushelwe kusi-Russian: sibala izibalo lapho i-CPU icubungule amaphakethe angenayo. Ake sizame ukuqalisa uhlelo:
$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple
Ake sihlole ukuthi uxhumene naye lo
futhi uthumele amaphakethe athile:
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 108
$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
Manje ake sibheke okuqukethwe kwamalungu afanayo:
$ sudo bpftool map dump name woo
[
{ "key": 0, "value": 0 },
{ "key": 1, "value": 400 },
{ "key": 2, "value": 0 },
{ "key": 3, "value": 0 },
{ "key": 4, "value": 0 },
{ "key": 5, "value": 0 },
{ "key": 6, "value": 0 },
{ "key": 7, "value": 46400 }
]
Cishe zonke izinqubo zacutshungulwa ku-CPU7. Lokhu akubalulekile kithi, into esemqoka ukuthi uhlelo lusebenza futhi siyaqonda ukuthi ungafinyelela kanjani amamephu kusuka ezinhlelweni ze-BPF - usebenzisa Ρ
Π΅Π»ΠΏΠ΅ΡΠΎΠ² bpf_mp_*
Inkomba yemfihlakalo
Ngakho-ke, singakwazi ukufinyelela imephu kusuka kuhlelo lwe-BPF sisebenzisa izingcingo ezifana
val = bpf_map_lookup_elem(&woo, &key);
lapho umsebenzi womsizi ubukeka kanjani
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
kodwa sidlula inkomba &woo
esakhiweni esingashiwongo struct { ... }
...
Uma sibheka umhlanganisi wohlelo, sibona ukuthi inani &woo
empeleni akuchazwanga (umugqa wesi-4):
llvm-objdump -D --section xdp/simple xdp-simple.bpf.o
xdp-simple.bpf.o: file format elf64-bpf
Disassembly of section xdp/simple:
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
2: bf a2 00 00 00 00 00 00 r2 = r10
3: 07 02 00 00 fc ff ff ff r2 += -4
4: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
6: 85 00 00 00 01 00 00 00 call 1
...
futhi iqukethwe ekuthuthweni:
$ llvm-readelf -r xdp-simple.bpf.o | head -4
Relocation section '.relxdp/simple' at offset 0xe18 contains 1 entries:
Offset Info Type Symbol's Value Symbol's Name
0000000000000020 0000002700000001 R_BPF_64_64 0000000000000000 woo
Kodwa uma sibheka uhlelo oseluvele lulayishiwe, sibona isikhombi semephu efanele (umugqa wesi-4):
$ sudo bpftool prog dump x name simple
int simple(void *ctx):
0: (85) call bpf_get_smp_processor_id#114128
1: (63) *(u32 *)(r10 -4) = r0
2: (bf) r2 = r10
3: (07) r2 += -4
4: (18) r1 = map[id:64]
...
Ngakho-ke, singaphetha ngokuthi ngesikhathi sethula uhlelo lwethu lokulayisha, isixhumanisi esiya &woo
kwathathelwa indawo okuthile ngomtapo wolwazi libbpf
. Okokuqala sizobheka okukhiphayo strace
:
$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, key_size=4, value_size=8, max_entries=8, map_name="woo", ...}, 120) = 4
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="simple", ...}, 120) = 5
Siyakubona lokho libbpf
udale imephu woo
bese ulanda uhlelo lwethu simple
. Ake sibhekisise ukuthi silayisha kanjani uhlelo:
- shayela
xdp_simple_bpf__open_and_load
kusuka kufayelaxdp-simple.skel.h
- okuyimbangela
xdp_simple_bpf__load
kusuka kufayelaxdp-simple.skel.h
- okuyimbangela
bpf_object__load_skeleton
kusuka kufayelalibbpf/src/libbpf.c
- okuyimbangela
bpf_object__load_xattr
kusuka kulibbpf/src/libbpf.c
Umsebenzi wokugcina, phakathi kwezinye izinto, uzobiza bpf_object__create_maps
, edala noma evula amamephu akhona, iwaguqule abe izichazi zefayela. (Kulapho sibona khona BPF_MAP_CREATE
kokuphumayo strace
.) Okulandelayo umsebenzi ubizwa bpf_object__relocate
futhi nguye osithakazelayo, njengoba sikhumbula esikubonile woo
etafuleni lokuthutha. Ukuyihlola, ekugcineni sizithola sisemsebenzini bpf_program__relocate
, okuthi
case RELO_LD64:
insn[0].src_reg = BPF_PSEUDO_MAP_FD;
insn[0].imm = obj->maps[relo->map_idx].fd;
break;
Ngakho sithatha imiyalelo yethu
18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
bese ufaka esikhundleni serejista yomthombo kuyo BPF_PSEUDO_MAP_FD
, kanye ne-IMM yokuqala kusichazi sefayela lemephu yethu futhi, uma ilingana, ngokwesibonelo, 0xdeadbeef
, khona-ke ngenxa yalokho siyothola isiyalo
18 11 00 00 ef eb ad de 00 00 00 00 00 00 00 00 r1 = 0 ll
Lena yindlela ulwazi lwemephu oludluliselwa ngayo kuhlelo oluthile olulayishiwe lwe-BPF. Kulokhu, imephu ingadalwa kusetshenziswa BPF_MAP_CREATE
, futhi ivulwe nge-ID kusetshenziswa BPF_MAP_GET_FD_BY_ID
.
Ingqikithi, uma usebenzisa libbpf
i-algorithm imi kanje:
- ngesikhathi sokuhlanganiswa, amarekhodi adalwa kuthebula lokuthutha ukuze axhumane namamephu
libbpf
ivula incwadi yento ye-ELF, ithola wonke amamephu asetshenzisiwe bese iwadalela izichazi zefayela- izichazi zefayela zilayishwa ku-kernel njengengxenye yomyalelo
LD64
Njengoba ungacabanga, kuningi okuzayo futhi kuzodingeka sibheke umnyombo. Ngenhlanhla, sinawo umkhondo - siyibhale phansi incazelo BPF_PSEUDO_MAP_FD
kurejista yomthombo futhi singayingcwaba, ezosiholela engcweleni yabo bonke abangcwele - kernel/bpf/verifier.c
, lapho umsebenzi onegama elihlukile uthatha indawo yesichazi sefayela ngekheli lesakhiwo sohlobo struct bpf_map
:
static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) {
...
f = fdget(insn[0].imm);
map = __bpf_map_get(f);
if (insn->src_reg == BPF_PSEUDO_MAP_FD) {
addr = (unsigned long)map;
}
insn[0].imm = (u32)addr;
insn[1].imm = addr >> 32;
(ikhodi egcwele ingatholakala
- ngenkathi ilayisha uhlelo, isiqinisekisi sihlola ukusetshenziswa okufanele kwemephu futhi sibhale ikheli lesakhiwo esihambisanayo
struct bpf_map
Uma ulanda kanambambili we-ELF usebenzisa libbpf
Kuningi okuqhubekayo, kodwa sizoxoxa ngakho kwezinye izihloko.
Ilayisha izinhlelo namamephu ngaphandle kwe-libbpf
Njengoba kuthenjisiwe, nasi isibonelo sabafundi abafuna ukwazi ukuthi bangakha futhi balayishe kanjani uhlelo olusebenzisa amamephu, ngaphandle kosizo. libbpf
. Lokhu kungaba wusizo uma usebenza endaweni ongakwazi ukwakha kuyo ukuncika, noma ukonga yonke ingxenye, noma ubhala uhlelo olufana nelo. ply
Ukwenza kube lula ukulandela ingqondo, sizobhala kabusha isibonelo sethu ngalezi zinhloso xdp-simple
. Ikhodi ephelele nenwetshiwe kancane yohlelo okuxoxwe ngalo kulesi sibonelo ingatholakala kulokhu
I-logic yesicelo sethu imi kanje:
- dala uhlobo lwemephu
BPF_MAP_TYPE_ARRAY
usebenzisa umyaloBPF_MAP_CREATE
, - dala uhlelo olusebenzisa le mephu,
- xhuma uhlelo kusixhumi esibonakalayo
lo
,
elihumusha kumuntu ngokuthi
int main(void)
{
int map_fd, prog_fd;
map_fd = map_create();
if (map_fd < 0)
err(1, "bpf: BPF_MAP_CREATE");
prog_fd = prog_load(map_fd);
if (prog_fd < 0)
err(1, "bpf: BPF_PROG_LOAD");
xdp_attach(1, prog_fd);
}
kuyinto map_create
idala imephu ngendlela efanayo naleyo esenzile esibonelweni sokuqala mayelana nekholi yesistimu bpf
- βkernel, ngicela ungenzele imephu entsha ngendlela yohlu lwama-elementi angu-8 afana __u64
bese ungibuyisela isichazi sefayela":
static int map_create()
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.map_type = BPF_MAP_TYPE_ARRAY,
attr.key_size = sizeof(__u32),
attr.value_size = sizeof(__u64),
attr.max_entries = 8,
strncpy(attr.map_name, "woo", sizeof(attr.map_name));
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}
Uhlelo futhi kulula ukulayisha:
static int prog_load(int map_fd)
{
union bpf_attr attr;
struct bpf_insn insns[] = {
...
};
memset(&attr, 0, sizeof(attr));
attr.prog_type = BPF_PROG_TYPE_XDP;
attr.insns = ptr_to_u64(insns);
attr.insn_cnt = sizeof(insns)/sizeof(insns[0]);
attr.license = ptr_to_u64("GPL");
strncpy(attr.prog_name, "woo", sizeof(attr.prog_name));
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
Ingxenye ekhohlisayo prog_load
incazelo yohlelo lwethu lwe-BPF njengoxhaxha lwezakhiwo struct bpf_insn insns[]
. Kodwa njengoba sisebenzisa uhlelo esinalo ku-C, singakopela kancane:
$ llvm-objdump -D --section xdp/simple xdp-simple.bpf.o
0000000000000000 <simple>:
0: 85 00 00 00 08 00 00 00 call 8
1: 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
2: bf a2 00 00 00 00 00 00 r2 = r10
3: 07 02 00 00 fc ff ff ff r2 += -4
4: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
6: 85 00 00 00 01 00 00 00 call 1
7: b7 01 00 00 00 00 00 00 r1 = 0
8: 15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2>
9: 61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0)
10: 07 01 00 00 01 00 00 00 r1 += 1
11: 63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1
12: b7 01 00 00 02 00 00 00 r1 = 2
0000000000000068 <LBB0_2>:
13: bf 10 00 00 00 00 00 00 r0 = r1
14: 95 00 00 00 00 00 00 00 exit
Sekukonke, sidinga ukubhala imiyalelo eyi-14 ngendlela yezakhiwo ezifana struct bpf_insn
(iseluleko: thatha indawo yokulahla phezulu, phinda ufunde isigaba semiyalelo, vula linux/bpf.h
linux/bpf_common.h
struct bpf_insn insns[]
yedwa):
struct bpf_insn insns[] = {
/* 85 00 00 00 08 00 00 00 call 8 */
{
.code = BPF_JMP | BPF_CALL,
.imm = 8,
},
/* 63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0 */
{
.code = BPF_MEM | BPF_STX,
.off = -4,
.src_reg = BPF_REG_0,
.dst_reg = BPF_REG_10,
},
/* bf a2 00 00 00 00 00 00 r2 = r10 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_X,
.src_reg = BPF_REG_10,
.dst_reg = BPF_REG_2,
},
/* 07 02 00 00 fc ff ff ff r2 += -4 */
{
.code = BPF_ALU64 | BPF_ADD | BPF_K,
.dst_reg = BPF_REG_2,
.imm = -4,
},
/* 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll */
{
.code = BPF_LD | BPF_DW | BPF_IMM,
.src_reg = BPF_PSEUDO_MAP_FD,
.dst_reg = BPF_REG_1,
.imm = map_fd,
},
{ }, /* placeholder */
/* 85 00 00 00 01 00 00 00 call 1 */
{
.code = BPF_JMP | BPF_CALL,
.imm = 1,
},
/* b7 01 00 00 00 00 00 00 r1 = 0 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 0,
},
/* 15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2> */
{
.code = BPF_JMP | BPF_JEQ | BPF_K,
.off = 4,
.src_reg = BPF_REG_0,
.imm = 0,
},
/* 61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0) */
{
.code = BPF_MEM | BPF_LDX,
.off = 0,
.src_reg = BPF_REG_0,
.dst_reg = BPF_REG_1,
},
/* 07 01 00 00 01 00 00 00 r1 += 1 */
{
.code = BPF_ALU64 | BPF_ADD | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 1,
},
/* 63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1 */
{
.code = BPF_MEM | BPF_STX,
.src_reg = BPF_REG_1,
.dst_reg = BPF_REG_0,
},
/* b7 01 00 00 02 00 00 00 r1 = 2 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_1,
.imm = 2,
},
/* <LBB0_2>: bf 10 00 00 00 00 00 00 r0 = r1 */
{
.code = BPF_ALU64 | BPF_MOV | BPF_X,
.src_reg = BPF_REG_1,
.dst_reg = BPF_REG_0,
},
/* 95 00 00 00 00 00 00 00 exit */
{
.code = BPF_JMP | BPF_EXIT
},
};
Ukuzivocavoca kulabo abangazange babhale lokhu ngokwabo - thola map_fd
.
Kunengxenye eyodwa engadalulwanga esele ohlelweni lwethu - xdp_attach
. Ngeshwa, izinhlelo ezifana ne-XDP azikwazi ukuxhunywa kusetshenziswa ikholi yesistimu bpf
. Abantu abadale i-BPF ne-XDP babevela emphakathini we-Linux eku-inthanethi, okusho ukuthi basebenzise lowo abazijwayele kakhulu (kodwa hhayi evamile people) isikhombimsebenzisi sokusebenzelana ne-kernel: xdp_attach
ikopisha ikhodi ku libbpf
, okungukuthi, kusukela kufayela netlink.c
Siyakwamukela emhlabeni wamasokhethi e-netlink
Vula uhlobo lwesokhethi ye-netlink NETLINK_ROUTE
:
int netlink_open(__u32 *nl_pid)
{
struct sockaddr_nl sa;
socklen_t addrlen;
int one = 1, ret;
int sock;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0)
err(1, "socket");
if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one)) < 0)
warnx("netlink error reporting not supported");
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
err(1, "bind");
addrlen = sizeof(sa);
if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0)
err(1, "getsockname");
*nl_pid = sa.nl_pid;
return sock;
}
Sifunda kulesi sokhethi:
static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq)
{
bool multipart = true;
struct nlmsgerr *errm;
struct nlmsghdr *nh;
char buf[4096];
int len, ret;
while (multipart) {
multipart = false;
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0)
err(1, "recv");
if (len == 0)
break;
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_pid != nl_pid)
errx(1, "wrong pid");
if (nh->nlmsg_seq != seq)
errx(1, "INVSEQ");
if (nh->nlmsg_flags & NLM_F_MULTI)
multipart = true;
switch (nh->nlmsg_type) {
case NLMSG_ERROR:
errm = (struct nlmsgerr *)NLMSG_DATA(nh);
if (!errm->error)
continue;
ret = errm->error;
// libbpf_nla_dump_errormsg(nh); too many code to copy...
goto done;
case NLMSG_DONE:
return 0;
default:
break;
}
}
}
ret = 0;
done:
return ret;
}
Ekugcineni, nansi umsebenzi wethu ovula isokhethi bese uthumela umlayezo okhethekile kuyo oqukethe isichazi sefayela:
static int xdp_attach(int ifindex, int prog_fd)
{
int sock, seq = 0, ret;
struct nlattr *nla, *nla_xdp;
struct {
struct nlmsghdr nh;
struct ifinfomsg ifinfo;
char attrbuf[64];
} req;
__u32 nl_pid = 0;
sock = netlink_open(&nl_pid);
if (sock < 0)
return sock;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.nh.nlmsg_type = RTM_SETLINK;
req.nh.nlmsg_pid = 0;
req.nh.nlmsg_seq = ++seq;
req.ifinfo.ifi_family = AF_UNSPEC;
req.ifinfo.ifi_index = ifindex;
/* started nested attribute for XDP */
nla = (struct nlattr *)(((char *)&req)
+ NLMSG_ALIGN(req.nh.nlmsg_len));
nla->nla_type = NLA_F_NESTED | IFLA_XDP;
nla->nla_len = NLA_HDRLEN;
/* add XDP fd */
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FD;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
memcpy((char *)nla_xdp + NLA_HDRLEN, &prog_fd, sizeof(prog_fd));
nla->nla_len += nla_xdp->nla_len;
/* if user passed in any flags, add those too */
__u32 flags = XDP_FLAGS_SKB_MODE;
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FLAGS;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
nla->nla_len += nla_xdp->nla_len;
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0)
err(1, "send");
ret = bpf_netlink_recv(sock, nl_pid, seq);
cleanup:
close(sock);
return ret;
}
Ngakho, yonke into isilungele ukuhlolwa:
$ cc nolibbpf.c -o nolibbpf
$ sudo strace -e bpf ./nolibbpf
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, map_name="woo", ...}, 72) = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=15, prog_name="woo", ...}, 72) = 4
+++ exited with 0 +++
Ake sibone ukuthi uhlelo lwethu luxhumekile yini lo
:
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
prog/xdp id 160
Masithumele ama-pings futhi sibheke imephu:
$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
$ sudo bpftool m dump name woo
key: 00 00 00 00 value: 90 01 00 00 00 00 00 00
key: 01 00 00 00 value: 00 00 00 00 00 00 00 00
key: 02 00 00 00 value: 00 00 00 00 00 00 00 00
key: 03 00 00 00 value: 00 00 00 00 00 00 00 00
key: 04 00 00 00 value: 00 00 00 00 00 00 00 00
key: 05 00 00 00 value: 00 00 00 00 00 00 00 00
key: 06 00 00 00 value: 40 b5 00 00 00 00 00 00
key: 07 00 00 00 value: 00 00 00 00 00 00 00 00
Found 8 elements
Hurray, konke kuyasebenza. Qaphela, ngendlela, ukuthi imephu yethu iphinda ikhonjiswe ngendlela yamabhayithi. Lokhu kungenxa yokuthi, ngokungafani libbpf
asizange silayishe ulwazi lohlobo (BTF). Kodwa sizokhuluma kabanzi ngalokhu ngokuzayo.
Amathuluzi Okuthuthukisa
Kulesi sigaba, sizobheka ikhithi yamathuluzi kanjiniyela ye-BPF encane.
Ngokuvamile, awudingi lutho olukhethekile ukuze uthuthukise izinhlelo ze-BPF - i-BPF isebenza kunoma iyiphi i-kernel yokusabalalisa ehloniphekile, futhi izinhlelo zakhiwa kusetshenziswa. clang
, enganikezwa kusukela kuphakheji. Kodwa-ke, ngenxa yokuthi i-BPF ingaphansi kokuthuthukiswa, i-kernel namathuluzi ahlala eshintsha, uma ungafuni ukubhala izinhlelo ze-BPF usebenzisa izindlela zakudala kusukela ngo-2019, kuzodingeka uhlanganise.
llvm
/clang
pahole
- umgogodla wayo
bpftool
(Ukuze uthole ireferensi, lesi sigaba kanye nazo zonke izibonelo eziku-athikili zisetshenziswe ku-Debian 10.)
llvm/clang
I-BPF inobungani ne-LLVM futhi, nakuba izinhlelo zakamuva ze-BPF zingahlanganiswa kusetshenziswa i-gcc, konke ukuthuthukiswa kwamanje kwenzelwa i-LLVM. Ngakho-ke, okokuqala, sizokwakha inguqulo yamanje clang
kusuka ku-git:
$ sudo apt install ninja-build
$ git clone --depth 1 https://github.com/llvm/llvm-project.git
$ mkdir -p llvm-project/llvm/build/install
$ cd llvm-project/llvm/build
$ cmake .. -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86"
-DLLVM_ENABLE_PROJECTS="clang"
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_BUILD_TYPE=Release
-DLLVM_BUILD_RUNTIME=OFF
$ time ninja
... ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΡΠΏΡΡΡΡ
$
Manje singakwazi ukuhlola ukuthi konke kuhlangane kahle yini:
$ ./bin/llc --version
LLVM (http://llvm.org/):
LLVM version 11.0.0git
Optimized build.
Default target: x86_64-unknown-linux-gnu
Host CPU: znver1
Registered Targets:
bpf - BPF (host endian)
bpfeb - BPF (big endian)
bpfel - BPF (little endian)
x86 - 32-bit X86: Pentium-Pro and above
x86-64 - 64-bit X86: EM64T and AMD64
(Iziqondiso zomhlangano clang
othathwe yimi kusuka
Ngeke sizifake izinhlelo esisanda kuzakha, kodwa esikhundleni salokho sizozengeza kuzo PATH
isibonelo:
export PATH="`pwd`/bin:$PATH"
(Lokhu kungangezwa ku .bashrc
noma kufayela elihlukile. Ngokwami, ngengeza izinto ezinjengalezi ~/bin/activate-llvm.sh
futhi uma kunesidingo ngiyakwenza . activate-llvm.sh
.)
I-Pahole ne-BTF
Okusetshenziswayo pahole
esetshenziswa lapho kwakhiwa i-kernel ukudala ulwazi lokususa iphutha ngefomethi ye-BTF. Ngeke singene ngokuningiliziwe kulesi sihloko mayelana nemininingwane yobuchwepheshe be-BTF, ngaphandle kokuthi ilula futhi sifuna ukuyisebenzisa. Ngakho-ke uma uzokwakha i-kernel yakho, yakha kuqala pahole
(ngaphandle kwe- pahole
ngeke ukwazi ukwakha i-kernel ngenketho CONFIG_DEBUG_INFO_BTF
:
$ git clone https://git.kernel.org/pub/scm/devel/pahole/pahole.git
$ cd pahole/
$ sudo apt install cmake
$ mkdir build
$ cd build/
$ cmake -D__LIB=lib ..
$ make
$ sudo make install
$ which pahole
/usr/local/bin/pahole
Izinhlamvu zokuhlola i-BPF
Lapho ngihlola amathuba e-BPF, ngifuna ukuhlanganisa owami umongo. Lokhu, ngokujwayelekile, akudingekile, ngoba uzokwazi ukuhlanganisa nokulayisha izinhlelo ze-BPF ku-kernel yokusabalalisa, noma kunjalo, ukuba ne-kernel yakho kukuvumela ukuthi usebenzise izici zakamuva ze-BPF, ezizovela ekusabalaliseni kwakho ezinyangeni ezingcono kakhulu. , noma, njengasendabeni yamanye amathuluzi okulungisa amaphutha ngeke apakishwe nhlobo esikhathini esizayo esibonakalayo. Futhi, ingqikithi yayo ikwenza kuzizwe kubalulekile ukuzama ikhodi.
Ukuze wakhe i-kernel udinga, okokuqala, i-kernel ngokwayo, futhi okwesibili, ifayela lokumisa i-kernel. Ukuhlola i-BPF singasebenzisa okujwayelekile net
net-next
bpf
bpf-next
*-next
ama-kernels yiwona angazinzi kakhulu kulawo asohlwini).
Kungaphezu kobubanzi balesi sihloko ukukhuluma ngendlela yokuphatha amafayela wokumisa i-kernel - kucatshangwa ukuthi kungenzeka ukuthi usuvele wazi ukuthi ungakwenza kanjani lokhu, noma
Landa enye yalezi zinhlamvu ezingenhla:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
$ cd bpf-next
Yakha ukucushwa kwe-kernel encane yokusebenza:
$ cp /boot/config-`uname -r` .config
$ make localmodconfig
Nika amandla izinketho ze-BPF kufayela .config
ngokukhetha kwakho (okungenzeka CONFIG_BPF
izovele inikwe amandla njengoba i-systemd iyisebenzisa). Nalu uhlu lwezinketho ezivela ku-kernel esetshenziselwa lesi sihloko:
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_LSM=y
CONFIG_BPF_SYSCALL=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_IPV6_SEG6_BPF=y
# CONFIG_NETFILTER_XT_MATCH_BPF is not set
# CONFIG_BPFILTER is not set
CONFIG_NET_CLS_BPF=y
CONFIG_NET_ACT_BPF=y
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_DEBUG_INFO_BTF=y
Ngemuva kwalokho singakwazi ukuhlanganisa kalula futhi sifake amamojula kanye ne-kernel (ngendlela, ungakwazi ukuhlanganisa i-kernel usebenzisa okusanda kuhlanganiswa. clang
ngokungeza CC=clang
):
$ make -s -j $(getconf _NPROCESSORS_ONLN)
$ sudo make modules_install
$ sudo make install
bese uqalisa kabusha nge-kernel entsha (ngiyisebenzisela lokhu kexec
kusuka kuphakheji kexec-tools
):
v=5.8.0-rc6+ # Π΅ΡΠ»ΠΈ Π²Ρ ΠΏΠ΅ΡΠ΅ΡΠΎΠ±ΠΈΡΠ°Π΅ΡΠ΅ ΡΠ΅ΠΊΡΡΠ΅Π΅ ΡΠ΄ΡΠΎ, ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π΄Π΅Π»Π°ΡΡ v=`uname -r`
sudo kexec -l -t bzImage /boot/vmlinuz-$v --initrd=/boot/initrd.img-$v --reuse-cmdline &&
sudo kexec -e
bpftool
Isisetshenziswa esisetshenziswa kakhulu esihlokweni kuzoba insiza bpftool
, ehlinzekwa njengengxenye ye-Linux kernel. Ibhalwa futhi inakekelwe abathuthukisi be-BPF yonjiniyela be-BPF futhi ingasetshenziswa ukuphatha zonke izinhlobo zezinto ze-BPF - layisha izinhlelo, udale futhi uhlele amamephu, uhlole impilo ye-ecosystem ye-BPF, njll. Amadokhumenti asesimweni samakhodi omthombo wamakhasi omuntu angatholakala
Ngesikhathi sokuloba bpftool
ifika isilungele kuphela i-RHEL, i-Fedora ne-Ubuntu (bheka, isibonelo, bpftool
ngeDebian). Kodwa uma usuyakhile i-kernel yakho, bese yakha bpftool
lula njengophaya:
$ cd ${linux}/tools/bpf/bpftool
# ... ΠΏΡΠΎΠΏΠΈΡΠΈΡΠ΅ ΠΏΡΡΠΈ ΠΊ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅ΠΌΡ clang, ΠΊΠ°ΠΊ ΡΠ°ΡΡΠΊΠ°Π·Π°Π½ΠΎ Π²ΡΡΠ΅
$ make -s
Auto-detecting system features:
... libbfd: [ on ]
... disassembler-four-args: [ on ]
... zlib: [ on ]
... libcap: [ on ]
... clang-bpf-co-re: [ on ]
Auto-detecting system features:
... libelf: [ on ]
... zlib: [ on ]
... bpf: [ on ]
$
(lapha ${linux}
- lena yinkomba yakho ye-kernel.) Ngemva kokwenza le miyalo bpftool
izoqoqwa ohlwini lwemibhalo ${linux}/tools/bpf/bpftool
futhi ingangezwa endleleni (okokuqala kumsebenzisi root
) noma vele ukopishe ku /usr/local/sbin
.
Qoqa bpftool
kungcono ukusebenzisa yokugcina clang
, ihlanganiswe njengoba kuchazwe ngenhla, futhi uhlole ukuthi ihlanganiswe kahle - usebenzisa, isibonelo, umyalo
$ sudo bpftool feature probe kernel
Scanning system configuration...
bpf() syscall for unprivileged users is enabled
JIT compiler is enabled
JIT compiler hardening is disabled
JIT compiler kallsyms exports are enabled for root
...
ezobonisa ukuthi yiziphi izici ze-BPF ezinikwe amandla ku-kernel yakho.
Ngendlela, umyalo wangaphambilini ungaqhutshwa njenge
# bpftool f p k
Lokhu kwenziwa ngokufanisa nezinsiza ezivela kuphakheji iproute2
, lapho esingasho khona, ngokwesibonelo, ukusho ip a s eth0
esikhundleni salokho ip addr show dev eth0
.
isiphetho
I-BPF ikuvumela ukuthi ufake isicathulo inzeze ukuze ulinganise kahle futhi lapho undiza ushintshe ukusebenza komgogodla. Uhlelo luphumelele kakhulu, emasikweni angcono kakhulu e-UNIX: indlela elula evumela ukuthi (uphinde) uhlele i-kernel ivumele inani elikhulu labantu nezinhlangano ukuthi zivivinye. Futhi, nakuba ukuhlolwa, kanye nokuthuthukiswa kwengqalasizinda ye-BPF ngokwayo, kusekude nokuqedwa, uhlelo seluvele lunayo i-ABI ezinzile evumela ukuthi wakhe i-logic yebhizinisi ethembekile, futhi okubaluleke kakhulu, ephumelelayo.
Ngithanda ukuqaphela ukuthi, ngokubona kwami, ubuchwepheshe buye buthandwa kakhulu ngoba, ngakolunye uhlangothi, bungakwazi ukudlala (isakhiwo somshini singaqondwa kancane noma ngaphansi ngobusuku obubodwa), futhi ngakolunye uhlangothi, ukuxazulula izinkinga ezingenakuxazululwa (kakuhle) ngaphambi kokubonakala kwayo. Lezi zingxenye ezimbili zihlangene ziphoqa abantu ukuthi bazame futhi baphuphe, okuholela ekuveleni kwezixazululo eziningi ezintsha.
Lesi sihloko, nakuba singesifushane kakhulu, siyisingeniso kuphela emhlabeni we-BPF futhi asichazi izici "ezithuthukisiwe" nezingxenye ezibalulekile zezakhiwo. Uhlelo oluya phambili lungokufana nalokhu: isihloko esilandelayo sizoba isifinyezo sezinhlobo zohlelo lwe-BPF (kunezinhlobo zezinhlelo ezingu-5.8 ezisekelwa ku-30 kernel), bese ekugcineni sibheka ukuthi zibhalwa kanjani izinhlelo zokusebenza ze-BPF zangempela zisebenzisa izinhlelo zokulandelela i-kernel. njengesibonelo, sekuyisikhathi sesifundo esijulile mayelana nezakhiwo ze-BPF, okulandelwa izibonelo zezinhlelo zokusebenza zenethiwekhi nezokuphepha ze-BPF.
Izihloko ezedlule kulolu chungechunge
Izixhumanisi
-
I-BPF kanye ne-XDP Reference Guide β imibhalo ye-BPF evela ku-cilium, noma ngokuqondile evela ku-Daniel Borkman, omunye wabadali nabanakekeli be-BPF. Lena enye yezincazelo zokuqala ezingathi sΓna, ehlukile kwezinye ngoba uDaniyeli wazi kahle ukuthi ubhala ngani futhi awekho amaphutha lapho. Ikakhulukazi, lo mbhalo uchaza indlela yokusebenza nezinhlelo ze-BPF zezinhlobo ze-XDP ne-TC kusetshenziswa insiza eyaziwayo.ip
kusuka kuphakhejiiproute2
. -
Documentation/networking/filter.txt β ifayela langempela elinamadokhumenti e-BPF yakudala bese eyandiswa. Ukufundwa okuhle uma ufuna ukujula olimini lomhlangano kanye nemininingwane yezakhiwo zobuchwepheshe. -
Ibhulogi nge-BPF evela ku-facebook . Ibuyekezwa kuyaqabukela, kodwa ngokufanelekile, njengoba u-Alexei Starovoitov (umbhali we-eBPF) no-Andrii Nakryiko - (umlondolozi) bebhala lapho.libbpf
). -
Izimfihlo ze-bpftool . Intambo ye-twitter ejabulisayo evela ku-Quentin Monnet enezibonelo nezimfihlo zokusebenzisa i-bpftool. -
Ngena ku-BPF: uhlu lwezinto zokufunda . Uhlu olukhulu (futhi olusagciniwe) lwezixhumanisi zemibhalo ye-BPF oluvela ku-Quentin Monnet.
Source: www.habr.com