BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Bilowgii waxaa jiray tignoolajiyada waxaana la odhan jiray BPF. Waanu eegnay iyada hore, Maqaalka Axdiga Hore ee taxanahan. 2013, iyada oo loo marayo dadaalka Alexei Starovoitov iyo Daniel Borkman, version la hagaajiyay, oo loogu talagalay mashiinnada casriga ah ee 64-bit, ayaa la sameeyay oo lagu daray kernel Linux. Tignoolajiyadan cusub ayaa si kooban loogu magacaabay BPF gudaha, ka dibna loo beddelay BPF Extended, oo hadda, dhowr sano ka dib, qof kastaa wuxuu si fudud ugu yeeraa BPF.

Ku dhawaad ​​​​hadalka, BPF wuxuu kuu oggolaanayaa inaad ku socodsiiso koodka isticmaale-bixiye-ku-sheegga ah ee booska Linux, iyo qaab dhismeedka cusub wuxuu noqday mid aad u guulaystay oo aan u baahan doono daraasiin maqaallo dheeraad ah si aan u qeexno dhammaan codsiyadiisa. (Waxa kaliya ee horumariyayaashu aysan si fiican u qaban, sida aad ku arki karto xeerka waxqabadka ee hoose, waxay abuureen calaamad wanaagsan.)

Maqaalkani waxa uu qeexayaa qaab-dhismeedka mashiinka farsamada ee BPF, isdhexgalka kernel ee la shaqeynta BPF, qalabka horumarinta, iyo sidoo kale dulmar kooban, aad u kooban oo ku saabsan awoodaha jira, i.e. Wax kasta oo aan u baahan doono mustaqbalka si aan u baarno qoto dheer ee codsiyada la taaban karo ee BPF.
BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Soo koobida maqaalka

Hordhaca qaab dhismeedka BPF. Marka hore, waxaanu qaadan doonaa aragtida shimbiraha ee qaab dhismeedka BPF waxaanan qeexi doonaa qaybaha ugu muhiimsan.

Diiwangeliyaha iyo nidaamka amarka mashiinka farsamada ee BPF. Markii horeba fikradda dhismaha guud ahaan, waxaan ku tilmaami doonaa qaab dhismeedka mashiinka farsamada ee BPF.

Meertada nolosha walxaha BPF, nidaamka faylka bpffs. Qaybtan, waxaan si dhow u eegi doonaa wareegga nolosha ee walxaha BPF - barnaamijyada iyo maabyada.

Maareynta walxaha iyadoo la isticmaalayo nidaamka bpf call. Iyada oo la fahmi karo nidaamka horeba u jiray, waxaanu ugu dambeyntii eegi doonaa sida loo abuuro oo looga maareeyo walxaha meel bannaan oo adeegsadaha annagoo adeegsanayna nidaam gaar ah oo wac - bpf(2).

Пишем программы BPF с помощью libbpf. Dabcan, waxaad ku qori kartaa barnaamijyada adigoo isticmaalaya nidaamka wicitaanka. Laakiin way adagtahay. Si loo helo xaalad waaqici ah, barnaamijyada nukliyeerka ayaa sameeyay maktabad libbpf. Waxaan abuuri doonaa qalfoofka codsiga BPF ee aasaasiga ah kaas oo aan u isticmaali doono tusaalooyinka xiga.

Caawinta Kernel. Halkan waxaan ku baran doonaa sida barnaamijyada BPF ay u heli karaan hawlaha caawiye kernel - qalab kaas oo ay weheliyaan khariidado, aasaas ahaan balaadhinaya awoodaha BPF cusub marka loo eego kan caadiga ah.

Helitaanka maabyada barnaamijyada BPF. Halkaa marka ay marayso, waxaan ogaan doonaa in ku filan si aan u fahanno sida saxda ah ee aan u abuuri karno barnaamijyo isticmaalaya maab. Oo aan xitaa si degdeg ah u eegno xaqiijiyaha weyn ee xoogga leh.

Qalabka horumarinta. Qaybta caawinta ee ku saabsan sida loo ururiyo tamarta loo baahan yahay iyo kernel ee tijaabooyinka.

Gabagabada. Gabagabada maqaalka, dadka ilaa hadda akhriya waxay ka heli doonaan ereyo dhiirigelin ah iyo sharraxaad kooban oo ku saabsan waxa dhici doona maqaallada soo socda. Waxaan sidoo kale liis gareyn doonaa dhowr xiriiriye oo loogu talagalay is-barashada kuwa aan haysan rabitaan ama awood ay ku sugaan sii wadida.

Hordhac Dhismaha BPF

Kahor intaanan bilaabin inaan tixgelinno qaab-dhismeedka BPF, waxaan tixraaci doonaa markii ugu dambeysay (oh) caadiga ah BPF, kaas oo loo sameeyay sidii looga jawaabi lahaa imaatinka mashiinnada RISC oo xalliyey dhibaatada shaandhaynta baakadaha hufan. Nashqadani waxa ay noqotay mid aad loogu guulaystay oo markii uu ku dhashay sagaashamaadkii ee Berkeley UNIX, waxa la geeyay inta badan nidaamyada hawlgalka ee jira, waxa ay ka badbaaday labaatankii waalan oo wali waxa ay helaysaa codsiyo cusub.

BPF-da cusub waxaa loo sameeyay iyadoo looga jawaabayo joogitaanka mashiinnada 64-bit, adeegyada daruuraha iyo baahida kordhaysa ee aaladaha abuurista SDN (Sqalab-dcadeeyay neworking). Waxaa soo saaray injineerada shabakada kernel si ay u noqdaan beddelka BPF-ga caadiga ah, BPF cusub macno ahaan lix bilood ka dib waxay heleen codsiyo ku jira hawsha adag ee raadinta nidaamyada Linux, iyo hadda, lix sano ka dib muuqaalkeeda, waxaan u baahan doonaa maqaal dhan oo soo socda kaliya qor noocyada kala duwan ee barnaamijyada.

Sawiro qosol badan

Asal ahaan, BPF waa mashiinka farsamada gacanta kaas oo kuu ogolaanaya inaad ku socodsiiso koodka "aan macnayn" meel kernel ah adigoon wax u dhimayn amniga. Barnaamijyada BPF waxaa lagu abuuray booska isticmaalaha, waxaa lagu shubaa kernel-ka, waxaana lagu xiraa ilaha dhacdada qaarkood. Dhacdo waxay noqon kartaa, tusaale ahaan, gaarsiinta baakidh is-dhexgal shabakadeed, bilaabida hawl kernel qaarkeed, iwm. Xaaladda xirmo, barnaamijka BPF wuxuu heli doonaa xogta iyo xogta badan ee xirmada (akhrinta iyo, suurtogalnimada, qorista, iyadoo ku xiran nooca barnaamijka); Xaaladda socodsiinta shaqada kernel, doodaha shaqada, oo ay ku jiraan tilmaamayaasha xusuusta kernel, iwm.

Aynu si qoto dheer u eegno habkan. Si aan u bilowno, aan ka hadalno farqiga ugu horreeya ee BPF-ga caadiga ah, barnaamijyada kuwaas oo lagu qoray isu-ururinta. Nooca cusub, qaab dhismeedka ayaa la balaariyay si barnaamijyada lagu qoro luqado heer sare ah, ugu horrayn, dabcan, C. Taas awgeed, dhabarka dambe ee llvm ayaa la sameeyay, kaas oo kuu ogolaanaya inaad soo saarto bytecode ee naqshadaha BPF.

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Nashqada BPF waxaa loo nashqadeeyay, qayb ahaan, si ay si hufan ugu shaqeeyaan mishiinada casriga ah. Si tan looga dhigo ficil ahaan, BPF bytecode, mar lagu shubo kernel-ka, ayaa loo turjumay koodka asalka ah iyadoo la adeegsanayo qayb la yiraahdo kombuyuutar JIT (Just In Time). Marka xigta, haddii aad xasuusato, BPF-ga caadiga ah barnaamijka waxaa lagu shubay kernel-ka waxaana lagu lifaaqay isha dhacdada atomically - marka la eego hal wicitaan oo nidaam ah. Dhismaha cusub, tani waxay ku dhacdaa laba marxaladood - marka hore, koodhka ayaa lagu shubaa kernel-ka iyadoo la adeegsanayo wicitaanka nidaamka bpf(2)ka dibna, ka dib, iyada oo loo marayo habab kale oo kala duwan oo ku xiran nooca barnaamijka, barnaamijku wuxuu ku xiran yahay isha dhacdada.

Halkan waxaa laga yaabaa in akhristuhu uu su'aal ku weydiiyo: suurtagal ma ahayd? Sidee loo dammaanad qaaday badbaadada fulinta xeerkan? Badbaadada fulinta waxaa noo dammaanad qaadaya heerka rarida barnaamijyada BPF ee loo yaqaan verifier (Ingiriisiga marxaladan waxaa loo yaqaan verifier waxaanan sii wadi doonaa isticmaalka ereyga Ingiriisiga):

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Xaqiijiye waa falanqeeye taagan oo hubiya in barnaamijku aanu carqaladayn shaqada caadiga ah ee kernel-ka. Tani, habka, macnaheedu maaha in barnaamijku aanu faragelin karin hawlgalka nidaamka - Barnaamijyada BPF, iyadoo ku xiran nooca, waxay akhrin karaan oo dib u qori karaan qaybaha xusuusta kernel, soo celinta qiyamka hawlaha, jar, dheji, dib u qori iyo xitaa baakadaha shabakada u sii gudbi. Xaqiijiye wuxuu dammaanad qaadayaa in socodsiinta barnaamijka BPF uusan burburi doonin kernel-ka iyo in barnaamijka, sida sharcigu qabo, uu leeyahay gelitaan qoris, tusaale ahaan, xogta xirmo baxaysa, ma awoodi doono inuu ku beddelo xusuusta kernel-ka ka baxsan xirmada. Waxaan si faahfaahsan u eegi doonaa xaqiijiyaha qaybta u dhiganta, ka dib marka aan barano dhammaan qaybaha kale ee BPF.

Haddaba maxaan ka baranay ilaa hadda? Isticmaaluhu wuxuu ku qoraa barnaamijka C, wuxuu ku shubayaa kernel-ka isagoo isticmaalaya nidaamka wicida bpf(2), halkaasoo uu hubiyay hubiyaha oo loo turjumay bytecode hooyo. Kadib isticmaale isku mid ah ama mid kale ayaa ku xiraya barnaamijka isha dhacdada wuxuuna bilaabayaa inuu fuliyo. Kala saarida kabaha iyo isku xirka ayaa lagama maarmaan u ah dhowr sababood. Marka hore, socodsiinta hubinta waa mid aad qaali u ah marka la soo dejiyo isla barnaamijka dhowr jeer waxaan luminaa waqtiga kombayutarka. Marka labaad, sida saxda ah ee barnaamijku isugu xidhan yahay waxay ku xidhan tahay nooca uu yahay, iyo hal “universal” interface oo la sameeyay sannad ka hor waxa laga yaabaa inaanay ku habboonayn noocyada cusub ee barnaamijyada. (Inkasta oo hadda qaab-dhismeedku uu noqonayo mid qaan-gaar ah, waxaa jira fikrad lagu mideynayo interface-ka heerka libbpf.)

Akhristaha fiiro gaar ah ayaa laga yaabaa inuu ogaado inaanan weli dhammayn sawirada. Runtii, dhammaan kuwan kor ku xusan ma sharraxayaan sababta BPF ay u beddesho sawirka marka la barbar dhigo BPF-ga caadiga ah. Laba hal-abuurnimo oo si weyn u balaadhinaya baaxadda ku habboonaanta ayaa ah awoodda isticmaalka xusuusta la wadaago iyo hawlaha caawiyaha kernel-ka. Gudaha BPF, xusuusta la wadaago waxaa lagu fuliyaa iyadoo la adeegsanayo waxa loogu yeero khariidado - qaabdhismeedka xogta la wadaago oo leh API gaar ah. Waxay u badan tahay inay heleen magacan sababtoo ah nooca ugu horreeya ee khariidad ee soo muuqda wuxuu ahaa miiska xashiishka. Kadibna waxaa soo baxay arraysyo, miisaska xashiishka ee maxalli ah (per-CPU) iyo arrays maxalli ah, geedo raadin, maab ay ku jiraan tilmaamayaasha barnaamijyada BPF iyo wax ka badan. Waxa hadda xiiso noo leh ayaa ah in barnaamijyada BPF ay hadda awood u leeyihiin in ay sii wadaan xaaladda inta u dhaxaysa wicitaannada oo ay la wadaagaan barnaamijyada kale iyo booska isticmaalaha.

Khariidadaha waxaa laga galaa hababka isticmaalaha iyadoo la isticmaalayo nidaamka wicitaanka bpf(2), iyo barnaamijyada BPF ee ku dhex jira kernel-ka iyadoo la isticmaalayo hawlaha caawiyaha. Intaa waxaa dheer, caawiyayaasha ma jiraan oo kaliya inay ku shaqeeyaan maab, laakiin sidoo kale si ay u helaan awoodaha kale ee kernel. Tusaale ahaan, barnaamijyada BPF waxay isticmaali karaan hawlaha caawiye si ay ugu gudbiyaan baakadaha is-dhexgalka kale, abuuraan dhacdooyinka perf, helitaanka qaababka kernel, iyo wixii la mid ah.

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Marka la soo koobo, BPF waxa ay bixisa awoodda ay ku shuban karto sabab la'aan, tusaale ahaan, xaqiijiye-la tijaabiyay, koodka isticmaale ee booska kernel-ka. Koodhkani waxa uu kaydin karaa xaalada u dhaxaysa wicitaanada iyo xogta isdhaafsiga booska isticmaalaha, sidoo kale waxa uu marin u leeyahay nidaamka hoosaadka kernel-ka ee uu ogolyahay barnaamijka noocan ah.

Tani waxay mar hore la mid tahay awoodaha ay bixiyaan modules kernel, marka la barbardhigo BPF waxay leedahay faa'iidooyin qaar (dabcan, waxaad kaliya barbar dhigi kartaa codsiyada la midka ah, tusaale ahaan, nidaamka raadinta - ma qori kartid darawal aan sabab lahayn oo leh BPF). Waxaad ogaan kartaa heerka gelitaanka hoose (qaar ka mid ah yutiilitida adeegsata BPF uma baahna adeegsadaha inuu yeesho xirfadaha barnaamijka kernel, ama xirfadaha barnaamijka guud ahaan), badqabka runtime (gacanta u taag faallooyinka kuwa aan jebin nidaamka marka wax la qorayo ama modules tijaabinta), atomicity - waxaa jira waqti hoos u dhac marka dib loo soo dajiyo modules, iyo nidaamka hoose ee BPF hubisaa in aan dhacdooyinka la seegay (si cadaalad ah, tani run maaha dhammaan noocyada barnaamijyada BPF).

Joogitaanka awoodaha noocan oo kale ah waxay ka dhigaysaa BPF qalab caalami ah oo loogu talagalay ballaarinta kernel-ka, taas oo lagu xaqiijiyay ficil ahaan: noocyo badan oo cusub oo barnaamijyo ah ayaa lagu daraa BPF, shirkado badan oo waaweyn ayaa isticmaala BPF ee server-yada dagaalka 24 × 7, in ka badan iyo in ka badan. bilaabayaashu waxay ganacsigooda ku dhistaan ​​xalal ku salaysan BPF. BPF waxaa loo isticmaalaa meel kasta: ka ilaalinta weerarrada DDoS, abuurista SDN (tusaale ahaan, hirgelinta shabakadaha kubernetes), sida qalabka ugu muhiimsan ee nidaamka raadinta iyo ururiyaha tirakoobka, hababka ogaanshaha soo gelitaanka iyo nidaamyada sanduuqyada, iwm.

Aynu halkan ku dhamayno dulmarka qaybta maqaalka oo aynu si faahfaahsan u eegno mishiinka farsamada iyo nidaamka deegaanka BPF.

Digression: utilities

Si aad awood ugu yeelatid inaad ku socodsiiso tusaalooyinka qaybaha soo socda, waxaa laga yaabaa inaad u baahato tiro agab ah, ugu yaraan. llvm/clang oo leh taageero bpf iyo bpftool. Qeybta Qalabka Horumarinta Waxaad akhrin kartaa tilmaamaha loogu talagalay isu-ururinta yutiilitida, iyo sidoo kale kernel-kaaga. Qaybtan hoos ayaa la dhigayaa si aanay u carqaladayn is-waafajinta soo jeedinteena.

Nidaamyada Diiwaangelinta Mashiinka Farshaxanka ee BPF iyo Tilmaamaha

Qaab dhismeedka iyo nidaamka taliska ee BPF ayaa la sameeyay iyada oo la tixgelinayo in barnaamijyada lagu qori doono luqadda C iyo, ka dib markii la geliyo kernel-ka, loo turjumay koodka hooyo. Sidaa darteed, tirada diiwaanka iyo amarrada amarrada ayaa lagu doortay iyadoo isha lagu hayo isgoyska, macnaha xisaabta, awoodda mashiinnada casriga ah. Intaa waxaa dheer, xannibaadyo kala duwan ayaa lagu soo rogay barnaamijyada, tusaale ahaan, ilaa dhowaan ma suurtagalin in la qoro wareegyada iyo subroutines, tirada tilmaamuhuna waxay ku koobnaayeen 4096 (hadda barnaamijyada mudnaanta leh waxay ku shuban karaan ilaa hal milyan oo tilmaamo ah).

BPF waxay leedahay kow iyo toban isticmaale-heli kara diiwaan 64-bit ah r0-r10 iyo miiska barnaamijka. Is diwaangeli r10 ka kooban yahay tilmaame jir waana akhrin-kaliya. Barnaamijyadu waxay marin u helayaan xidhmooyin 512-byte ah wakhtiga runtime-ka iyo qadar aan xadidnayn oo xusuusta la wadaago oo qaab khariidado ah.

Barnaamijyada BPF waxaa loo oggol yahay inay socodsiiyaan qayb gaar ah oo ah caawiyayaasha kernel-ka nooca barnaamijka ah iyo, dhawaanahan, hawlo joogto ah. Hawl kasta oo loo yaqaan waxay qaadan kartaa ilaa shan doodood, oo lagu gudbiyo diiwaannada r1-r5, qiimaha soo laabashadana waa loo gudbiyaa r0. Waxaa la dammaanad qaaday in ka dib marka laga soo laabto shaqada, waxa ku jira diiwaanka r6-r9 Ma beddeli doono.

Si loo helo turjumaada barnaamijka hufan, diiwaan gelinta r0-r11 Dhammaan dhismayaasha la taageeray waxaa si gaar ah loogu habeeyey diiwaannada dhabta ah, iyadoo la tixgelinayo sifooyinka ABI ee dhismaha hadda jira. Tusaale ahaan, loogu talagalay x86_64 diiwaan gelinta r1-r5, loo isticmaalo in lagu gudbiyo xuduudaha shaqada, ayaa lagu soo bandhigay rdi, rsi, rdx, rcx, r8, kuwaas oo loo isticmaalo in lagu gudbiyo qiyaasaha si ay u shaqeeyaan x86_64. Tusaale ahaan, koodka bidix wuxuu u turjumayaa koodka dhanka midig sida tan:

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

Is diwaangeli r0 sidoo kale loo isticmaalo in lagu soo celiyo natiijada fulinta barnaamijka, iyo in diiwaanka r1 barnaamijka waxaa loo gudbiyaa tilmaame macnaha guud - iyadoo ku xiran nooca barnaamijka, tani waxay noqon kartaa, tusaale ahaan, qaab dhismeed struct xdp_md (XDP) ama qaab-dhismeedka struct __sk_buff (Barnaamijyada shabakadaha kala duwan) ama qaab-dhismeedka struct pt_regs (oo loogu talagalay noocyada kala duwan ee barnaamijyada raadinta), iwm.

Markaa, waxaanu haysanay tiro diiwaan-galin ah, caawiyayaasha kernel-ka, xidhmooyin, tilmaame guud iyo xusuusta la wadaago oo qaab khariidado ah. Ma aha in waxaas oo dhami ay lagama maarmaan u yihiin safarka, laakiin ...

Aynu sii wadno sharraxaadda oo aynu ka hadalno nidaamka taliska ee ku shaqeeya walxahan. Dhammaan (Ku dhawaad ​​dhammaanTilmaamaha BPF waxay leeyihiin cabbir go'an oo 64-bit ah. Haddii aad eegto hal tilmaamo mashiinka 64-bit Big Endian waad arki doontaa

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

waa Code - kani waa codaynta tilmaamaha, Dst/Src waa cod-bixinaha soo-dhoweeyaha iyo isha, siday u kala horreeyaan, Off - 16-bit soo gelid saxeexan, iyo Imm waa 32-bit isteeger saxeexan oo loo isticmaalo tilmaamaha qaar (oo la mid ah cBPF joogta ah K). Codaynta Code wuxuu leeyahay laba nooc midkood:

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Casharrada 0, 1, 2, 3 waxay qeexaan amarrada lagu shaqeynayo xusuusta. Iyaga ayaa loo yaqaan, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, siday u kala horreeyaan. Fasalada 4, 7 (BPF_ALU, BPF_ALU64) oo ka kooban tilmaamo ALU ah. Fasalada 5, 6 (BPF_JMP, BPF_JMP32) ka kooban tilmaanta boodada.

Qorshaha mustaqbalka ee lagu baranayo habka wax-barid ee BPF waa sida soo socota: Halkii si taxadar leh loo qori lahaa dhammaan tilmaamaha iyo cabbiradooda, waxaan eegi doonaa dhowr tusaale oo qaybtan ah waxaana iyaga ka muuqan doona sida habraaca dhab ahaantii u shaqeeyaan iyo sida loo sameeyo. gacanta u kala qaad fayl kasta oo laba-jibaaran ah oo loogu talagalay BPF. Si loo xoojiyo maaddada dambe ee maqaalka, waxaan sidoo kale la kulmi doonaa tilmaamo gaar ah oo ku saabsan qaybaha ku saabsan Verifier, compiler JIT, tarjumaadda BPF-ga caadiga ah, iyo sidoo kale marka la baranayo khariidadaha, wicitaanka hawlaha, iwm.

Marka aan ka hadalno tilmaamaha gaarka ah, waxaan tixraaci doonaa faylasha asaasiga ah bpf.h и bpf_common.h, kaas oo qeexaya nambarada tilmaamaha BPF. Markaad barato qaab-dhismeedka adiga keligaa iyo/ama kala saarayso binaries, waxaad ka heli kartaa semantics ilaha soo socda, oo loo kala soocay sida ay u adag yihiin: Faahfaahinta eBPF aan rasmi ahayn, Tilmaamaha Tixraaca BPF iyo XDP, Habraaca Tilmaamaha, Dukumeenti/shabakad/filter.txt iyo, dabcan, gudaha koodhka isha Linux - xaqiijiye, JIT, turjumaan BPF.

Tusaale: kala dirida BPF ee madaxaaga

Bal aynu eegno tusaale aynu barnaamij ku ururinayno readelf-example.c oo eeg binary-ga ka dhashay. Waxaan daaha ka qaadi doonaa nuxurka asalka ah readelf-example.c hoose, ka dib markii aan ka soo celinay caqligooda codes binary:

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

Tiirka koowaad ee soo saarista readelf waa soo gal oo barnaamijkeenu wuxuu ka kooban yahay afar amar:

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

Xeerarka amarku waa siman yihiin b7, 15, b7 и 95. Xusuusnow in saddexda qaybood ee ugu yar ay yihiin fasalka waxbarida. Xaaladeena, qaybta afraad ee dhammaan tilmaamuhu way madhan yihiin, markaa fasallada waxbarashadu waa 7, 5, 7, 5, siday u kala horreeyaan. BPF_ALU64iyo 5 waa BPF_JMP. Labada fasalba, qaabka wax-barashadu waa isku mid (eeg xagga sare) waxaana dib u qori karnaa barnaamijkeena sidan oo kale (isla markaasna waxaan dib u qori doonaa tiirarka soo haray ee qaabka aadanaha):

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

Hawlgalka b fasalka ALU64 Waa BPF_MOV. Waxay qiimeeyaa diiwaanka goobta. Haddii xoogaa la dhigo s (source), ka dibna qiimaha ayaa laga soo qaatay diiwaanka isha, iyo haddii, sida kiiskeena, aan la dejin, ka dibna qiimaha ayaa laga soo qaaday goobta. Imm. Markaa tilmaamaha koowaad iyo saddexaad waxaanu samaynaa qaliinka r0 = Imm. Intaa waxaa dheer, hawlgalka JMP fasalka 1 waa BPF_JEQ (bood hadday siman yihiin). Xaaladeena, tan iyo xoogaa S waa eber, waxay barbar dhigaysaa qiimaha diiwaanka isha iyo goobta Imm. Haddii qiyamku isku mid yihiin, markaa kala-guurka ayaa dhacaya PC + Offhalkaas oo PC, sida caadiga ah, ka kooban ciwaanka tilmaamaha soo socda. Ugu dambeyntii, JMP Class 9 Hawlgalku waa BPF_EXIT. Tilmaantaani waxay joojinaysaa barnaamijka, kuna soo laabanaysa kernel-ka r0. Aan ku darno tiir cusub miiskayaga:

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

Waxaan tan dib ugu qori karnaa qaab ka habboon:

     r0 = 1
     if (r1 == 0) goto END
     r0 = 2
END:
     exit

Haddii aan xasuusano waxa ku jira diiwaanka r1 barnaamijka waxaa loo gudbiyaa tilmaame macnaha guud laga soo bilaabo kernel, iyo diiwaanka r0 qiimihiisu waxa lagu soo celiyaa kernel-ka, markaas waxaan arki karnaa in haddii tilmaanta macnaha guud ay eber tahay, markaas waxaan ku celineynaa 1, haddii kale - 2. Aan hubinno inaan saxan nahay annaga oo eegayna isha:

$ cat readelf-example.c
int foo(void *ctx)
{
        return ctx ? 2 : 1;
}

Haa, waa barnaamij aan macno lahayn, laakiin waxa uu u turjumayaa afar tilmaamood oo fudud.

Tusaale ka baxsan: 16-byte tilmaamaha

Waxaan horay u soo sheegnay in tilmaamaha qaarkood ay qaataan in ka badan 64 bits. Tani waxay khusaysaa, tusaale ahaan, tilmaamaha lddw (Cod = 0x18 = BPF_LD | BPF_DW | BPF_IMM) - kelmad laba jibaaran ka soo rog goobaha diiwaanka Imm. Xaqiiqdu waxay tahay Imm cabbirkiisu waa 32, erey labanlaabana waa 64-bits, markaa ku shubida 64-bit qiimaha degdega ah ee diiwaanka hal 64-bit waxbarid ma shaqayn doonto. Si tan loo sameeyo, laba tilmaamood oo isku xiga ayaa loo isticmaalaa in lagu kaydiyo qaybta labaad ee qiimaha 64-bit ee goobta Imm. Tusaale:

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

Waxa jira laba tilmaamo oo keliya barnaamijka binary:

Binary                                 Disassm
18000000 ddccbbaa 00000000 44332211    r0 = Imm[0]|Imm[1]
95000000 00000000                      exit

Waxaan mar kale la kulmi doonaa tilmaamo lddw, marka aan ka hadalno raritaan iyo ku shaqaynta maab.

Tusaale: kala dirida BPF iyadoo la isticmaalayo qalabka caadiga ah

Marka, waxaan baranay inaan akhrino koodka binary code ee BPF waxaanan diyaar u nahay inaan kala saarno tilmaam kasta haddii loo baahdo. Si kastaba ha ahaatee, waxaa habboon in la sheego in ficil ahaan ay ku habboon tahay oo dhaqso badan tahay in la kala saaro barnaamijyada iyadoo la adeegsanayo qalabka caadiga ah, tusaale ahaan:

$ 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

Meertada nololeed ee walxaha BPF, nidaamka faylka bpffs

(Waxaan markii hore ka bartay qaar ka mid ah faahfaahinta lagu qeexay qaybtan hoose boostada Alexei Starovoitov BPF Blog.)

Walxaha BPF-barnaamijyada iyo khariidadaha-waxaa laga sameeyay meel bannaan oo adeegsadaha ah iyadoo la isticmaalayo amarro BPF_PROG_LOAD и BPF_MAP_CREATE nidaamka call bpf(2), waxaan uga hadli doonaa sida saxda ah ee tani u dhacdo qaybta xigta. Tani waxay abuurtaa qaababka xogta kernel iyo mid kasta oo iyaga ka mid ah refcount (Tirinta tixraaca) ayaa loo dejiyay mid, iyo sharraxaha faylka tilmaamaya shayga ayaa lagu soo celinayaa isticmaalaha. Ka dib marka gacanta la xiro refcount walaxda hal baa lagu dhimayaa, marka uu eber gaadho, shaygu waa baabba'aa.

Haddii barnaamijku isticmaalo maab, markaa refcount Khariidadahaan waxaa lagu kordhiyaa hal mar kadib marka la shubo barnaamijka, i.e. Sharaxayaashooda faylka waa laga xiri karaa nidaamka isticmaalaha iyo weli refcount ma noqon doonto eber:

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Ka dib marka si guul leh loo soo dejiyo barnaamijka, waxaan inta badan ku dhejinnaa nooc ka mid ah dhacdooyinka dhaliyaha. Tusaale ahaan, waxaan ku dhejin karnaa isku xirka shabakada si aan uga baaraandegno xirmooyinka soo socda ama aan ugu xirno qaar tracepoint xudunta u ah. Halkaa marka ay marayso, miiska tixraaca ayaa sidoo kale kor u qaadi doona mid waxaanan awood u yeelan doonaa in aan xidhno sharraxaadda faylka ee barnaamijka xamuulka.

Maxaa dhacaya haddii aan hadda xirno bootloader-ka? Waxay kuxirantahay nooca dhaliyaha dhacdada (hook). Dhammaan xirmooyinka shabakadu waxay jiri doonaan ka dib marka la dhammeeyo rarka, kuwani waa waxa loogu yeero xirmooyinka caalamiga ah. Iyo, tusaale ahaan, barnaamijyada raadraaca ayaa la sii dayn doonaa ka dib marka habka abuuray uu dhammaado (oo sidaas darteed waxaa loogu yeeraa mid gudaha, laga bilaabo "maxali ilaa habka"). Farsamo ahaan, jillaabyada maxalliga ah ayaa had iyo jeer leh sharraxa faylka u dhigma ee booska isticmaalaha oo sidaas darteed xira marka habka la xiro, laakiin jillaabyada caalamiga ah ma sameeyaan. Jaantuskan soo socda, adoo isticmaalaya iskutallaabyada cas, waxaan isku dayaa inaan muujiyo sida joojinta barnaamijka loader-ka ay u saameynayso nolosha walxaha ee kiiska gudaha iyo caalamiga ah.

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Waa maxay sababta ay u kala duwan yihiin qabsashada gudaha iyo kuwa caalamiga ah? Ku shaqeynta qaar ka mid ah noocyada barnaamijyada shabakada waxay macno samaynayaan iyada oo aan la isticmaalin, tusaale ahaan, qiyaas ilaalinta DDoS - bootloader ayaa qoraya sharciyada oo ku xiraya barnaamijka BPF ee shabakada shabakada, ka dib markaa bootloader-ku wuu tagi karaa oo is dili karaa. Dhanka kale, qiyaas barnaamijka raadraaca qaladka ah oo aad ku qortay jilbahaaga toban daqiiqo gudahood - marka ay dhamaato, waxaad jeclaan lahayd inaysan jirin wax qashin ah oo ku haray nidaamka, iyo qabsatooyinka maxalliga ah ayaa xaqiijin doona taas.

Dhanka kale, ka fikir inaad rabto inaad ku xidho barta raadraaca ee kernel-ka oo aad ururiso tirakoob sanado badan. Xaaladdan oo kale, waxaad rabi lahayd inaad buuxiso qaybta isticmaalaha oo aad ku soo celiso tirakoobka waqti ka waqti. Nidaamka faylka bpf wuxuu bixiyaa fursadan. Waa nidaamka faylka been abuurka ah ee xusuusta-kaliya kaas oo u oggolaanaya abuurista faylasha tixraacaya walxaha BPF oo markaa kordhiya refcount walxaha. Taas ka dib, xamuulku wuu bixi karaa, walxaha uu abuurayna way sii noolaan doonaan.

BPF ee kuwa yaryar, qaybta koowaad: BPF oo la dheereeyey

Abuuritaanka faylasha bpffs ee tixraacaya walxaha BPF waxaa loo yaqaan "pinning" (sida weedha soo socota: "Nidaamku wuxuu dhejin karaa barnaamijka BPF ama khariidad"). Abuuritaanka walxaha feylasha ee walxaha BPF macno ma aha oo kaliya kordhinta nolosha walxaha maxalliga ah, laakiin sidoo kale isticmaalka walxaha caalamiga ah - dib ugu noqoshada tusaalaha barnaamijka ilaalinta caalamiga ah ee DDoS, waxaan rabnaa inaan awoodno inaan imaado oo aan eegno tirakoobka. marba mar.

Nidaamka faylka BPF badanaa waa lagu rakibaa /sys/fs/bpf, laakiin sidoo kale waxaa lagu dhejin karaa gudaha, tusaale ahaan, sidan:

$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint

Magacyada nidaamka faylka ayaa la abuuray iyadoo la adeegsanayo amarka BPF_OBJ_PIN nidaamka BPF wac. Tusaale ahaan, aan soo qaadanno barnaamij, soo ururinno, dhejinno, oo ku dhejinno bpffs. Barnaamijkayagu ma sameeyo wax faa'iido leh, waxaan kaliya soo bandhigaynaa koodka si aad u soo saari karto tusaalaha:

$ cat test.c
__attribute__((section("xdp"), used))
int test(void *ctx)
{
        return 0;
}

char _license[] __attribute__((section("license"), used)) = "GPL";

Aynu soo ururinno barnaamijkan oo aynu abuurno nuqul maxalli ah oo nidaamka faylka ah bpffs:

$ clang -target bpf -c test.c -o test.o
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpoint

Hadda aan soo dejinno barnaamijkeena anagoo adeegsanayna utility bpftool oo fiiri wicitaanada nidaamka la socda bpf(2) (qaar xariiqo aan khusayn oo laga saaray wax soo saarka xargaha):

$ 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

Halkan waxaanu ku shubnay barnaamijka anagoo adeegsanayna BPF_PROG_LOAD, helay sharraxaadda faylka kernel-ka 3 iyo adeegsiga amarka BPF_OBJ_PIN ku dhejiyay sharraxaaha faylkan sidii fayl ahaan "bpf-mountpoint/test". Taas ka dib barnaamijka bootloader bpftool wuu dhammeeyay shaqada, laakiin barnaamijkayagu wuxuu ku hadhay kernel-ka, in kasta oo aynaan ku xidhin wax shabakad isku xidhan:

$ 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

Waxaan si caadi ah u tirtiri karnaa shayga faylka unlink(2) ka dibna barnaamijka u dhigma waa la tirtiri doonaa:

$ sudo rm ./bpf-mountpoint/test
$ sudo bpftool prog show id 783
Error: get by id (783): No such file or directory

Tirtirka walxaha

Marka laga hadlayo tirtiridda walxaha, waxaa lagama maarmaan ah in la caddeeyo in ka dib markii aan ka saarno barnaamijka jilbaha (dhaliyaha dhacdada), ma dhacdo dhacdo cusub oo kicinaysa bilawgeeda, si kastaba ha ahaatee, dhammaan dhacdooyinka hadda ee barnaamijka ayaa loo dhamaystiri doonaa sida caadiga ah. .

Qaar ka mid ah noocyada barnaamijyada BPF waxay kuu oggolaanayaan inaad bedesho barnaamijka duulista, i.e. siin atomiga isku xigxiga replace = detach old program, attach new program. Xaaladdan oo kale, dhammaan dhacdooyinka firfircoon ee noocii hore ee barnaamijka ayaa dhammayn doona shaqadooda, iyo maamulayaal cusub ayaa laga abuuri doonaa barnaamijka cusub, iyo "atomicity" halkan macnaheedu waa in aan hal dhacdo la seegi doonin.

Ku lifaaqida barnaamijyada ilaha dhacdada

Maqaalkan, si gooni ah uma qeexi doono ku xidhida barnaamijyada ilaha dhacdooyinka, maadaama ay macno samaynayso in tan lagu barto macnaha nooc gaar ah oo barnaamij ah. Cm. Tusaale hoos, kaas oo aan ku muujineyno sida barnaamijyada sida XDP ay isugu xiran yihiin.

Maaraynta Walxaha Isticmaalka Wicista Nidaamka bpf

Barnaamijyada BPF

Dhammaan walxaha BPF waxaa laga sameeyay oo laga maamulaa meel bannaan oo adeegsadaha iyadoo la adeegsanayo wicitaanka nidaamka bpf, isagoo leh tusaalaha soo socda:

#include <linux/bpf.h>

int bpf(int cmd, union bpf_attr *attr, unsigned int size);

Waa kan kooxda cmd waa mid ka mid ah qiyamka nooca enum bpf_cmd, attr - tilmaame tilmaamayaasha barnaamij gaar ah iyo size - cabbirka shayga marka loo eego tilmaamuhu, i.e. inta badan tan sizeof(*attr). Kernel 5.8 nidaamka wac bpf waxay taageertaa 34 amarro kala duwan, iyo qeexitaan union bpf_attr ku fadhiya 200 oo sadar. Laakiin ma aha in ay na cabsi geliyaan tan, maadaama aan baran doono amarrada iyo cabbirrada inta lagu guda jiro maqaallo dhowr ah.

Aan ku bilowno kooxda BPF_PROG_LOAD, kaas oo abuura barnaamijyada BPF - wuxuu qaataa tilmaamo BPF ah wuxuuna ku shubaa kernel-ka. Xilliga rarka, xaqiijiyaha ayaa la bilaabay, ka dibna isku-duwaha JIT iyo, ka dib fulinta guuleysiga, sharaxaadaha faylka barnaamijka ayaa lagu soo celiyaa isticmaalaha. Waxaan ku aragnay wixii isaga ku dhacay ee ku xiga qaybta hore ku saabsan wareegga nolosha ee walxaha BPF.

Hadda waxaan qori doonaa barnaamij gaar ah oo ku shubi doona barnaamij fudud oo BPF ah, laakiin marka hore waxaan u baahanahay inaan go'aansano nooca barnaamijka aan rabno inaan ku shubno - waa inaan dooranaa nooca oo gudaha qaabka noocaan ah, ku qor barnaamij ka gudbi doona imtixaanka xaqiijinta. Si kastaba ha noqotee, si aan loo adkeyn geeddi-socodka, halkan waa xal diyaar ah: waxaan qaadan doonaa barnaamij sida BPF_PROG_TYPE_XDP, kaas oo soo celin doona qiimaha XDP_PASS (ka bood dhammaan baakadaha). Isku-duwaha BPF waxay u egtahay mid aad u fudud:

r0 = 2
exit

Ka dib markii aan go'aansanay in waan soo gelin doonaa, waxaan kuu sheegi karnaa sida aan u sameyn doono:

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

Dhacdooyinka xiisaha leh ee barnaamijku waxay ku bilowdaan qeexida habayn insns - Barnaamijkeena BPF ee koodhka mashiinka. Xaaladdan oo kale, tilmaan kasta oo barnaamijka BPF ah ayaa lagu soo ururiyey qaab-dhismeedka bpf_insn. Cunsurka koowaad insns u hoggaansamo tilmaamaha r0 = 2, labaad - exit.

Dib u gurasho Kernelku wuxuu qeexayaa macros ku habboon qorista koodka mashiinka, iyo adeegsiga faylka madaxa kernel-ka tools/include/linux/filter.h waan qori karnaa

struct bpf_insn insns[] = {
    BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
    BPF_EXIT_INSN()
};

Laakiin maadaama ku qorista barnaamijyada BPF ee koodka asalka ah ay lagama maarmaan u tahay oo keliya qorista imtixaannada kernel-ka iyo maqaallada ku saabsan BPF, maqnaanshaha makrosyadani dhab ahaantii ma dhibayso nolosha horumariyaha.

Ka dib markii aan qeexno barnaamijka BPF, waxaan u dhaqaaqnaa si aan ugu shubno kernel-ka. Halbeegyadayada ugu yar attr waxaa ku jira nooca barnaamijka, habaynta iyo tirada tilmaamaha, shatiga loo baahan yahay, iyo magaca "woo", kaas oo aan isticmaalno si aan u helno barnaamijkayaga nidaamka ka dib marka la soo dejiyo. Barnaamijka, sidii ballanku ahaa, waxaa lagu shubaa nidaamka iyadoo la adeegsanayo wicitaanka nidaamka bpf.

Dhamaadka barnaamijka waxaan ku dhamaaneynaa wareeg aan xadidneyn oo u dhiganta culeyska. La'aanteed, barnaamijka waxaa dili doona kernel marka sharraxaadda feylka ee nidaamka wicitaanka nagu soo celiyay la xiro bpf, mana ku arki doono nidaamka.

Hagaag, waxaan diyaar u nahay tijaabinta. Aan isku soo uruurino oo aan barnaamijka hoos ka wadno stracesi loo hubiyo in wax walba ay u shaqeeyaan sidii la rabay:

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

Wax walba waa fiican yihiin, bpf(2) gacanta 3 ayaa nagu soo celiyay waxaanu galnay wareeg aan xad lahayn pause(). Aan isku dayno inaan ka helno barnaamijkayaga nidaamka. Si tan loo sameeyo waxaan aadi doonaa terminal kale oo aan isticmaalno utility 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)

Waxaan aragnaa in uu jiro barnaamij la raray nidaamka woo Kaas oo aqoonsi caalami ah uu yahay 390 oo hadda socda simple-prog waxaa jira tilmaame fayl furan oo tilmaamaya barnaamijka (iyo haddii simple-prog wuu dhamayn doonaa shaqada, markaa woo baaba'i doona). Sida la filayo, barnaamijka woo waxay qaadataa 16 bytes - laba tilmaamo - codes binary ee qaab dhismeedka BPF, laakiin qaabkeeda asalka ah (x86_64) waxay horey u tahay 40 bytes. Aan eegno barnaamijkeena qaabkiisii ​​​​ asalka ahaa:

# bpftool prog dump xlated id 390
   0: (b7) r0 = 2
   1: (95) exit

la yaab ma leh. Hadda aan eegno koodka uu sameeyay isu-duwaha JIT:

# 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

aan aad waxtar u leh exit(2), laakiin si caddaalad ah, barnaamijkeenu aad buu u fudud yahay, iyo barnaamijyada aan fudfudud ahayn hordhaca iyo hal-abuurka ay ku dartay ururiyaha JIT, dabcan, ayaa loo baahan yahay.

Maps

Barnaamijyada BPF waxay isticmaali karaan meelaha xusuusta habaysan ee laga heli karo barnaamijyada kale ee BPF iyo barnaamijyada goobta isticmaalaha. Walxahaan waxaa lagu magacaabaa maab, qeybtaan waxaan ku tusi doonaa sida loo maamulo iyaga oo isticmaalaya nidaamka wicitaanka bpf.

Aynu isla markiiba nidhaahno in awoodaha maabku aanay ku xaddidnayn oo keliya helitaanka xusuusta la wadaago. Waxa jira khariidado ujeedo gaar ah ka kooban, tusaale ahaan, tilmaamayaasha barnaamijyada BPF ama tilmaamayaasha isku xidhka shabakadaha, maabyada la shaqaynta dhacdooyinka perf, iwm. Ka hadli mayno halkan, si aan loo jahawareerin akhristaha. Marka laga reebo tan, waan iska indho-tiraa arrimaha isku-dhafka, maadaama aysan tani muhiim u ahayn tusaalayaashayada. Liis dhamaystiran oo ah noocyada khariidadaha la heli karo ayaa laga heli karaa <linux/bpf.h>, qaybtan waxaynu tusaale u soo qaadan doonaa nooca kowaad ee taariikh ahaan, miiska xashiishka BPF_MAP_TYPE_HASH.

Haddii aad ku dhex abuurto miiska xashiishka, dheh, C++, waxaad odhan lahayd unordered_map<int,long> woo, oo Ruushku macneheedu yahay "waxaan u baahanahay miis woo cabbir aan xadidnayn, kuwaas oo furayaashu ay yihiin nooca int, iyo qiyamku waa nooca long" Si loo abuuro miiska xashiishka BPF, waxaan u baahanahay inaan sameyno wax badan oo isku mid ah, marka laga reebo inay tahay inaan qeexno cabbirka ugu sarreeya ee miiska, iyo halkii aan caddayn lahayn noocyada furayaasha iyo qiimaha, waxaan u baahannahay inaan ku qeexno cabbirkooda bytes. . Si aad u abuurto maab isticmaal amarka BPF_MAP_CREATE nidaamka call bpf. Aynu eegno in ka badan ama ka yar barnaamijka ugu yar ee abuuraya khariidad. Ka dib barnaamijkii hore ee ku raran barnaamijyada BPF, kani waa inuu kuugula ekaado mid fudud:

$ 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();
}

Halkan waxaan ku qeexaynaa jaangooyooyin attr, kaas oo aanu ku nidhaahno "Waxaan u baahanahay miiska xashiishka leh furayaasha iyo qiimaha cabbirka sizeof(int), taas oo aan ku dhejin karo ugu badnaan afar curiye." Markaad abuureyso khariidado BPF, waxaad qeexi kartaa cabbirro kale, tusaale ahaan, si la mid ah tusaalaha barnaamijka, waxaan u qeexnay magaca shayga sida "woo".

Aynu soo ururino oo wadno barnaamijka:

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

Waa kan nidaamka wicitaanka bpf(2) ayaa noo soo celisay nambarkii khariidadda sharraxaha 3 ka dibna barnaamijku, sida la filayo, wuxuu sugayaa tilmaamo dheeraad ah oo ku saabsan wicitaanka nidaamka pause(2).

Hadda aan u dirno barnaamijkayaga xagga dambe ama fur terminal kale oo aan eegno shaygayaga adoo isticmaalaya utility bpftool (magaceeda waxaan ka sooci karnaa khariidadeena kuwa kale):

$ sudo bpftool map
...
114: hash  name woo  flags 0x0
        key 4B  value 4B  max_entries 4  memlock 4096B
...

Lambarka 114 waa aqoonsiga caalamiga ah ee shaygayaga. Barnaamij kasta oo nidaamka ku jira wuxuu isticmaali karaa aqoonsigan si uu u furo khariidad jira isagoo isticmaalaya amarka BPF_MAP_GET_FD_BY_ID nidaamka call bpf.

Hadda waxaan ku ciyaari karnaa miiska xashiishka. Aynu eegno waxa ku jira:

$ sudo bpftool map dump id 114
Found 0 elements

Madhan Aynu ku darno qiime hash[1] = 1:

$ sudo bpftool map update id 114 key 1 0 0 0 value 1 0 0 0

Aan markale eegno miiska:

$ sudo bpftool map dump id 114
key: 01 00 00 00  value: 01 00 00 00
Found 1 element

Hooray! Waxaan ku guuleysanay inaan ku darno hal shay. Ogsoonow in aan ka shaqeyno heerka byte si tan loo sameeyo, tan iyo bptftool ma garanayo nooca ay yihiin qiyamka miiska xashiishka. (Aqoontan waxaa lagu wareejin karaa iyada iyadoo la adeegsanayo BTF, laakiin in ka badan hadda.)

Sidee saxda ah ee bpftool u akhridaa oo ugu daraa walxaha? Bal aan eegno daboolka hoostiisa:

$ 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

Marka hore waxaan ku furnay khariidadda aqoonsigeeda caalamiga ah anagoo adeegsanayna amarka BPF_MAP_GET_FD_BY_ID и bpf(2) noo soo celi tilmaanta 3. Si dheeraad ah u isticmaalaya amarka BPF_MAP_GET_NEXT_KEY Waxaan ka helnay furihii ugu horreeyay miiska annaga oo dhaafnay NULL sida tilmaanta furaha "kii hore". Haddii aan furaha haysano waan sameyn karnaa BPF_MAP_LOOKUP_ELEMKaas oo qiimihii ku soo celinaya tilmaame value. Talaabada xigta waa in aan isku dayno in aan helno cunsurka ku xiga anagoo u gudbinayna tilmaame furaha hadda jira, laakiin miiskayagu waxa uu ka kooban yahay hal shay iyo amarka BPF_MAP_GET_NEXT_KEY soo noqda ENOENT.

Hagaag, aynu ku beddelno qiimaha furaha 1, aynu nidhaahno caqli-galkeena ganacsigu wuxuu u baahan yahay diiwaangelin 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

Sida la filayo, waa mid aad u fudud: amarka BPF_MAP_GET_FD_BY_ID wuxuu khariidadeena ku furaa aqoonsi, iyo amarka BPF_MAP_UPDATE_ELEM dib u qoraa curiyaha.

Markaa, ka dib markii aan samayno miiska xashiishka hal barnaamij, waxaan akhrin karnaa oo qori karnaa waxa ku jira mid kale. Ogsoonow in haddii aan awoodnay inaan tan ka sameyno khadka taliska, markaa barnaamij kasta oo kale oo nidaamka ayaa sameyn kara. Marka lagu daro amarada kor lagu sifeeyay, ee ku shaqaynta khariidadaha goobta isticmaalaha, soo socda:

  • BPF_MAP_LOOKUP_ELEM: qiimaha ku raadso furaha
  • BPF_MAP_UPDATE_ELEM: update/abuur qiimaha
  • BPF_MAP_DELETE_ELEM: furaha saar
  • BPF_MAP_GET_NEXT_KEYHel furaha ku xiga (ama kan ugu horreeya).
  • BPF_MAP_GET_NEXT_ID: waxay kuu ogolaanaysaa inaad dhex marto dhammaan khariidadaha jira, taasi waa sida ay u shaqeyso bpftool map
  • BPF_MAP_GET_FD_BY_ID: ku fur khariidad jira aqoonsigeeda caalamiga ah
  • BPF_MAP_LOOKUP_AND_DELETE_ELEM: atomically u cusboonaysii qiimaha shay oo soo celi kii hore
  • BPF_MAP_FREEZEKa dhig khariidadda mid aan laga beddeli karin goobta isticmaale (hawlgalkan lama celin karo)
  • BPF_MAP_LOOKUP_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_UPDATE_BATCH, BPF_MAP_DELETE_BATCH: hawlgallada tirada badan. Tusaale ahaan, BPF_MAP_LOOKUP_AND_DELETE_BATCH - kani waa habka kaliya ee lagu kalsoonaan karo ee lagu akhriyo oo dib loogu dejiyo dhammaan qiyamka khariidada

Dhammaan amarradan uma shaqeeyaan dhammaan noocyada khariidadaha, laakiin guud ahaan la shaqeynta noocyada kale ee khariidadaha ee booska isticmaalaha waxay si sax ah ula mid yihiin ku shaqeynta miisaska xashiishka.

Si loo kala danbeeyo, aan dhameyno tijaabooyinka miiska xashiishka. Xusuusnow inaan abuurnay miis ka koobnaan kara ilaa afar fure? Aynu ku darno dhawr waxyaalood oo kale:

$ 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

Ilaa hadda aad u wanaagsan:

$ 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

Aan isku dayno inaan mid kale ku darno:

$ sudo bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
Error: update failed: Argument list too long

Sidii la filayey, kuma aannu guulaysan. Aynu si faahfaahsan u eegno khaladka:

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

Wax walba waa fiican yihiin: sida la filayo, kooxda BPF_MAP_UPDATE_ELEM wuxuu isku dayaa inuu abuuro cusub, shanaad, fure, laakiin shil E2BIG.

Markaa, waxaanu samayn karnaa oo aanu ku shubi karnaa barnaamijyada BPF, sidoo kale waxaanu ka samayn karnaa oo aanu maamuli karnaa maabyada goobta isticmaalaha. Hadda waa macquul in la eego sida aan u isticmaali karno maabyada barnaamijyada BPF laftooda. Tan waxaan kaga hadli karnaa luqadda ay adag tahay in la akhriyo barnaamijyada mashiinka macro codes, laakiin dhab ahaantii waxaa la gaaray waqtigii lagu muujin lahaa sida barnaamijyada BPF dhab ahaan loo qoro loona ilaaliyo - iyadoo la adeegsanayo libbpf.

(Akhristayaasha aan ku qanacsanayn la'aanta tusaalaha hoose: waxaan si faahfaahsan u falanqeyn doonaa barnaamijyada adeegsada khariidadaha iyo hawlaha caawiye ee la abuuray iyadoo la isticmaalayo libbpf oo kuu sheego waxa ka dhacaya heerka tilmaamaha. Akhristayaasha aan ku qanacsanayn aad u badan, waxaanu ku daray Tusaale meesha ku haboon ee maqaalka.)

Qorista barnaamijyada BPF iyadoo la isticmaalayo libbpf

Qorista barnaamijyada BPF iyadoo la adeegsanayo koodka mishiinada waxay noqon kartaa mid xiiso leh marka ugu horeysa oo kaliya, ka dibna qanacsanaanta ayaa dejisa. Waqtigan xaadirka ah waxaad u baahan tahay inaad u jeedsato dareenkaaga llvm, kaas oo leh dhabarka soo saarista koodka qaab dhismeedka BPF, iyo sidoo kale maktabad libbpf, kaas oo kuu ogolaanaya inaad qorto dhinaca isticmaalaha ee codsiyada BPF oo aad ku shubto koodka barnaamijyada BPF ee la sameeyay llvm/clang.

Runtii sida aynu ku arki doonno qormadan iyo qormooyinka danbe ee soo socda. libbpf wuxuu qabtaa shaqo aad u badan la'aanteed (ama qalab la mid ah - iproute2, libbcc, libbpf-goiwm) waa wax aan macquul aheyn in la noolaado. Mid ka mid ah sifooyinka dilaaga ah ee mashruuca libbpf waa BPF CO-RE (Compile once, Run Everywhere) - mashruuc kuu ogolaanaya inaad qorto barnaamijyada BPF ee la qaadi karo hal kernel ilaa mid kale, awood u leh inuu ku shaqeeyo API-yo kala duwan (tusaale, marka qaabka kernel-ka uu isbeddelo nooca ilaa version). Si aad awood ugu yeelatid inaad la shaqeyso CO-RE, kernel-kaagu waa in lagu soo ururiyaa taageerada BTF (waxaan ku sharraxaynaa sida tan loo sameeyo qaybta Qalabka Horumarinta. Waxaad hubin kartaa in kernel-kaagu uu ku dhisan yahay BTF iyo in kale - iyadoo uu jiro faylka soo socda:

$ ls -lh /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 2.6M Jul 29 15:30 /sys/kernel/btf/vmlinux

Faylkan waxa uu kaydiyaa macluumaadka ku saabsan dhammaan noocyada xogta ee loo isticmaalo kernel-ka waxaana loo adeegsadaa dhammaan tusaalayaashayada iyadoo la adeegsanayo libbpf. Waxaan si faahfaahsan ugala hadli doonaa CO-RE maqaalka soo socda, laakiin kan - kaliya ku dhis naftaada kernel leh CONFIG_DEBUG_INFO_BTF.

Maktabadda libbpf si toos ah ugu nool tusaha tools/lib/bpf kernel iyo horumarintiisa waxaa lagu fuliyaa liiska boostada [email protected]. Si kastaba ha ahaatee, kayd gaar ah ayaa loo hayaa baahida codsiyada ku nool meel ka baxsan kernel-ka https://github.com/libbpf/libbpf taas oo maktabadda kernel ay u muuqato si loo akhriyo wax badan ama ka yar sida ay tahay.

Qaybtan waxaan ku eegi doonaa sida aad u abuuri karto mashruuc isticmaalaya libbpf, aynu qorno dhawr (badan ama kayar oo aan macno lahayn) barnaamijyo imtixaan oo si faahfaahsan u falanqeeyo sida ay u wada shaqeeyaan. Tani waxay noo ogolaan doontaa inaan si sahlan ugu sharaxno qaybaha soo socda sida saxda ah ee barnaamijyada BPF ay ula falgalaan khariidadaha, caawiyayaasha kernel-ka, BTF, iwm.

Caadi ahaan mashaariicda la isticmaalayo libbpf ku dar kaydka GitHub sida git submodule, waxaanu samayn doonaa si la mid ah:

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

U socdaa libbpf Aad u fudud:

$ 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

Qorshahayaga xiga ee qaybtan waa sida soo socota: waxaanu qori doonaa barnaamijka BPF sida BPF_PROG_TYPE_XDP, oo la mid ah tusaalihii hore, laakiin C, waxaanu ku soo ururinay annaga oo isticmaalaya clang, oo qor barnaamij caawiye kaas oo ku shubi doona kernel-ka. Qaybaha soo socda waxaan ku balaadhin doonaa awooda barnaamijka BPF iyo barnaamijka kaaliyaha.

Tusaale: abuurista codsi dhammaystiran iyadoo la isticmaalayo libbpf

Si aan ku bilowno, waxaan isticmaalnaa faylka /sys/kernel/btf/vmlinux, kaas oo kor lagu soo sheegay, oo u dhigma qaabka faylka madaxa:

$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

Faylkan waxa uu kaydin doonaa dhammaan qaab-dhismeedka xogta laga heli karo kernel-kayaga, tusaale ahaan, sidan waa sida madaxa IPv4 loogu qeexay kernel-ka:

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

Hadda waxaan ku qori doonaa barnaamijkayaga BPF gudaha 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";

Inkasta oo barnaamijkeenu uu noqday mid aad u fudud, haddana waxaan u baahanahay inaan fiiro gaar ah u yeelano faahfaahin badan. Marka hore, faylka madaxa ee ugu horreeya ee aan ku darno waa vmlinux.h, kaas oo aan hadda soo saarnay annagoo adeegsanayna bpftool btf dump - hadda uma baahnid in aan rakibno xirmada kernel-headers si aan u ogaano sida qaab-dhismeedka kernel-ku u eg yahay. Faylka madaxa ee soo socda ayaa nooga imaanaya maktabadda libbpf. Hadda waxaan u baahanahay oo kaliya si aan u qeexno makro SEC, kaas oo u diraya jilaha qaybta ku habboon ee faylka shayga ELF. Barnaamijkayagu waxa uu ka kooban yahay qaybta xdp/simple, halka ka hor inta aan la jarjarin aan ku qeexno nooca barnaamijka BPF - kani waa heshiiska lagu isticmaalo libbpf, iyadoo lagu saleynayo magaca qaybta waxay bedeli doontaa nooca saxda ah ee bilowga bpf(2). Barnaamijka BPF laftiisa ayaa ah C - aad u fudud oo ka kooban hal xariiq return XDP_PASS. Ugu dambeyntii, qayb gaar ah "license" waxa ku jira magaca shatiga.

Waxaan ku ururin karnaa barnaamijkayaga anagoo adeegsanayna llvm/clang, nooca>= 10.0.0, ama ka sii fiican, (eeg qaybta Qalabka Horumarinta):

$ 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

Waxaa ka mid ah sifooyinka xiisaha leh: waxaan tilmaamaynaa naqshadeynta bartilmaameedka -target bpf iyo dariiqa loo maro madaxyada libbpf, kaas oo aanu dhawaan rakibnay. Sidoo kale, ha ilaawin -O2, ikhtiyaarkan la'aanteed waxaa laga yaabaa inaad mustaqbalka ku jirto yaabab. Aan eegno code-keena, ma ku guuleysanay inaan qorno barnaamijkii aan rabnay?

$ 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

Haa, way shaqeysay! Hadda, waxaan haynaa faylka binary ee barnaamijka, waxaanan rabnaa inaan abuurno codsi ku dhejin doona kernel-ka. Ujeedadaas awgeed maktabadda libbpf waxay ina siinaysaa laba ikhtiyaar - isticmaal API heer hoose ah ama API heer sare ah. Waxaan mari doonaa dariiqa labaad, maadaama aan rabno inaan barano sida wax loo qoro, loo raro, looguna xiro barnaamijyada BPF dadaal yar oo loogu talagalay daraasaddooda xigta.

Marka hore, waxaan u baahanahay inaan ka soo saarno "qalfoofka" barnaamijkeena binary-ga iyadoo la isticmaalayo isla utility bpftool - mindida Swiss ee adduunka BPF (taas oo loo qaadan karo macno ahaan, tan iyo Daniel Borkman, mid ka mid ah abuurayaasha iyo ilaaliyayaasha BPF, waa Swiss):

$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h

Faylka ku jira xdp-simple.skel.h ka kooban yahay code binary ee barnaamijkeena iyo hawlaha maaraynta - rarista, lifaaqa, tirtirto shayga. Kiiskeena fudud tani waxay u egtahay mid xad dhaaf ah, laakiin sidoo kale waxay ka shaqeysaa kiiska halka faylka shayga uu ka kooban yahay barnaamijyo badan oo BPF ah iyo khariidado iyo si aan u xirno ELF weyn waxaan kaliya u baahanahay inaan abuurno qalfoofka oo aan wacno hal ama laba shaqo oo ka mid ah codsiga gaarka ah. ayaa qoraya Aynu hadda dhaqaaqno.

Haddaan si adag u hadlo, barnaamijkeenu waa wax fudud:

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

waa struct xdp_simple_bpf lagu qeexay faylka xdp-simple.skel.h oo qeexaya faylkayaga shayga:

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

Waxaan halkaan ku arki karnaa raadadka API-hooseeya: qaab-dhismeedka struct bpf_program *simple и struct bpf_link *simple. Qaab dhismeedka koowaad wuxuu si gaar ah u qeexayaa barnaamijkayaga, oo ku qoran qaybta xdp/simple, kan labaadna wuxuu qeexayaa sida barnaamijku ugu xidhmo isha dhacdada.

function xdp_simple_bpf__open_and_load, furaya shay ELF, kala jeexjeexay, abuuraa dhammaan qaab-dhismeedka iyo qaab-dhismeedyada (marka laga reebo barnaamijka, ELF sidoo kale waxay ka kooban tahay qaybo kale - xogta, xogta kaliya ee akhriska ah, macluumaadka debugging, shatiga, iwm.), ka dibna ku shuba kernelka iyadoo la adeegsanayo nidaam wac bpf, oo aan ku hubin karno innaga oo ururinayna barnaamijka:

$ 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

Aynu hadda eegno barnaamijkayaga annagoo isticmaalaya bpftool. Aan helno aqoonsigeeda:

# 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)

oo tuur (waxaan isticmaalnaa qaab gaaban oo amarka ah bpftool prog dump xlated):

# bpftool p d x id 463
int simple(void *ctx):
; return XDP_PASS;
   0: (b7) r0 = 2
   1: (95) exit

Wax cusub! Barnaamijku waxa uu daabacay qaybo ka mid ah faylka isha ee C. Tan waxa samaysay maktabadda libbpf, kaas oo ka helay qaybta debug-ka ee binary-ga, wuxuu ku soo ururiyay shay BTF, oo ku shubtay kernelka isagoo isticmaalaya BPF_BTF_LOAD, ka dibna cayimay sharraxaadda faylka natiijada marka aad barnaamijka ku shubayso amarka BPG_PROG_LOAD.

Caawinta Kernel

Barnaamijyada BPF waxay wadi karaan hawlaha "dibadda" - caawiyayaasha kernel-ka. Hawlahan caawiye waxay u oggolaanayaan barnaamijyada BPF inay galaan qaab-dhismeedka kernel, maareeyaan maab, iyo sidoo kale inay la xiriiraan "adduunka dhabta ah" - abuuraan dhacdooyinka perf, qalabka xakamaynta (tusaale ahaan, baakadaha dib u habeynta), iwm.

Tusaale: bpf_get_smp_processor_id

Gudaha qaab-dhismeedka "barashada tusaale ahaan" jaantuska, aan tixgelinno mid ka mid ah hawlaha caawiyaha, bpf_get_smp_processor_id(), la hubo faylka ku jira kernel/bpf/helpers.c. Waxay soo celinaysaa nambarka processor-ka uu ku shaqeynayo barnaamijka BPF ee u yeeray. Laakin uma xiisayneyno macnaheeda sida xaqiiqda ah in hirgelinteedu ay qaadato hal sadar:

BPF_CALL_0(bpf_get_smp_processor_id)
{
    return smp_processor_id();
}

Qeexitaannada shaqada caawiyaha BPF waxay la mid yihiin qeexitaannada wicitaanka nidaamka Linux. Halkan, tusaale ahaan, hawl ayaa lagu qeexaa oo aan lahayn wax dood ah. (Hawl qaadata, dheh, saddex dood ayaa lagu qeexaa iyadoo la adeegsanayo makro BPF_CALL_3. Tirada ugu badan ee dooduhu waa shan.) Si kastaba ha ahaatee, tani waa kaliya qaybta koowaad ee qeexida. Qaybta labaad waa in la qeexo qaabka nooca struct bpf_func_proto, oo ka kooban sharraxaadda shaqada caawiye ee uu xaqiijiyaha fahmayo:

const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
    .func     = bpf_get_smp_processor_id,
    .gpl_only = false,
    .ret_type = RET_INTEGER,
};

Diiwaangelinta Hawlaha Kaaliyaha

Si barnaamijyada BPF ee nooc gaar ah ay u isticmaalaan shaqadan, waa in ay ka diwaan galiyaan, tusaale ahaan nooca BPF_PROG_TYPE_XDP shaqo ayaa lagu qeexaa kernel-ka xdp_func_proto, kaas oo go'aaminaya aqoonsiga shaqada caawiye in XDP ay taageerto hawshan iyo in kale. Shaqadeenu waa taageerooyinka:

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

Noocyada cusub ee barnaamijka BPF ayaa lagu "qeexay" faylka include/linux/bpf_types.h iyadoo la isticmaalayo makro BPF_PROG_TYPE. Waxaa lagu qeexay xigashooyin sababtoo ah waa qeexitaan macquul ah, iyo ereyada luqadda C qeexida dhammaan qaab-dhismeedka la taaban karo ayaa ka dhaca meelo kale. Gaar ahaan, faylka kernel/bpf/verifier.c dhammaan qeexitaannada faylka bpf_types.h waxaa loo isticmaalaa in lagu abuuro qaabab kala duwan 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
};

Taasi waa, nooc kasta oo ka mid ah barnaamijka BPF, tilmaame qaab dhismeedka xogta nooca ayaa la qeexay struct bpf_verifier_ops, kaas oo lagu bilaabay qiimaha _name ## _verifier_ops, i.e. xdp_verifier_ops si ay u xdp... Qaab dhismeedka xdp_verifier_ops go'aamiyay faylka ku jira net/core/filter.c sida soo socota:

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

Halkan waxaan ku aragnaa shaqadayada la yaqaan xdp_func_proto, kaas oo wadi doona hubinta mar kasta oo ay la kulanto caqabad nooc ah hawlaha gudaha barnaamijka BPF, eeg verifier.c.

Aynu eegno sida barnaamijka mala-awaalka ah ee BPF uu u isticmaalo shaqada bpf_get_smp_processor_id. Si taas loo sameeyo, waxaan dib ugu qoreynaa barnaamijka qaybteena hore sida soo socota:

#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";

Calaamadda bpf_get_smp_processor_id go'aamiyay в <bpf/bpf_helper_defs.h> maktabadaha libbpf sida

static u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;

yacni, bpf_get_smp_processor_id waa tilmaame hawleed qiimihiisu yahay 8, halka 8 uu yahay qiimaha BPF_FUNC_get_smp_processor_id nooca enum bpf_fun_id, kaas oo anaga lagu qeexay faylka vmlinux.h (faylka bpf_helper_defs.h Kernel-ka waxaa soo saaray qoraal, markaa tirooyinka "sixirka" waa ok). Shaqadani ma qaadato wax dood ah waxayna soo celisaa qiimaha nooca __u32. Marka aan ku wadno barnaamijkeena, clang abuuraa tilmaamo BPF_CALL "nooca saxda ah" Aan soo ururiyo barnaamijka oo aan eegno qaybta 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

Safka hore waxaan ku aragnaa tilmaamo call, halbeegga IMM oo la mid ah 8, iyo SRC_REG - eber. Sida uu dhigayo heshiiska ABI ee uu isticmaalo hubiyaha, kani waa wicitaanka caawiye nambarka sideedaad. Marka la bilaabo, caqli-galku waa sahlan yahay. Ka soo celi qiimaha diiwaanka r0 laga guuriyay r1 iyo xariiqyada 2,3 waxa loo rogaa nooca u32 - 32-bit ee sare waa la nadiifiyay. Sadarka 4,5,6,7 waxaan ku celinaynaa 2 (XDP_PASS) ama 1 (XDP_DROP) iyadoo ku xiran in kaaliyaha uu ka shaqeeyo laynka 0 uu soo celiyay eber ama aan eber ahayn.

Aynu is tijaabinno: ku shubo barnaamijka oo aynu eegno wax soo saarka 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

Hagaag, xaqiijiyaha wuxuu helay caawiyaha kernel-ka saxda ah.

Tusaale: gudbinta doodaha iyo ugu dambeyntii socodsiinta barnaamijka!

Dhammaan hawlaha caawiyaha heerka orodku waxay leeyihiin tusaale

u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)

Halbeegyada hawlaha caawiye ayaa lagu gudbiyaa diiwaannada r1-r5, qiimahana waxaa lagu soo celiyaa diiwaanka r0. Ma jiraan wax hawlo ah oo qaata wax ka badan shan dood, taageero iyagana lama filayo in mustaqbalka lagu daro.

Aynu eegno caawiyaha kernel-ka cusub iyo sida BPF u gudbiso cabirrada. Aan dib u qorno xdp-simple.bpf.c sida soo socota (xadadka intiisa kale isma bedelin):

SEC("xdp/simple")
int simple(void *ctx)
{
    bpf_printk("running on CPU%un", bpf_get_smp_processor_id());
    return XDP_PASS;
}

Barnaamijkeenu waxa uu daabacaa tirada CPU-ga uu ku shaqaynayo. Aynu soo ururinno oo eegno koodka:

$ 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

Sadarka 0-7 waxaan ku qornaa xargaha running on CPU%un, ka dibna safka 8 waxaan ku wadnaa kii la yaqaanay bpf_get_smp_processor_id. Sadarka 9-12 waxaan ku diyaarineynaa doodaha caawiyaha bpf_printk - diiwangelinta r1, r2, r3. Waa maxay sababta ay saddex u yihiin oo aanay laba u noqon? Sababtoo ah bpf_printkkani waa makro duub agagaarka caawiyaha dhabta ah bpf_trace_printk, kaas oo u baahan inuu dhaafo xajmiga qaabka xargaha.

Aynu hadda ku darno dhawr sadar xdp-simple.csi uu barnaamijkeenu ugu xidhmo interface-ka lo oo runtii bilaabay!

$ 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);
}

Halkan waxaan isticmaalnaa shaqada bpf_set_link_xdp_fd, kaas oo ku xidha barnaamijyada BPF nooca XDP-ga iyo isku-xidhka shabakadaha. Waxaan hardcoded nambarka interface lo, taas oo had iyo jeer ah 1. Waxaan wadnaa shaqada laba jeer si aan marka hore ka saarno barnaamijkii hore haddii lagu dhejiyay. Ogow hadda uma baahnin caqabad pause ama wareeg aan xad lahayn: barnaamijka raridayagu wuu baxayaa, laakiin barnaamijka BPF lama dili doono maadaama uu ku xidhan yahay isha dhacdada. Ka dib markii si guul leh loo soo dejiyo iyo isku xirka, barnaamijka waxaa loo bilaabi doonaa xirmo kasta oo shabakad ah oo yimaada lo.

Aan soo dejino barnaamijka oo aan eegno interface-ka 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

Barnaamijka aan soo dejinay waxa uu leeyahay ID 669 waxaanan ku aragnaa isla aqoonsiga interface-ka lo. Waxaan u diri doonaa dhowr xirmo 127.0.0.1 (codsi + jawaab):

$ ping -c1 localhost

oo hadda aan eegno waxa ku jira faylka casriga ah ee khaladka ah /sys/kernel/debug/tracing/trace_pipe, kaas oo bpf_printk Farriimihiisa ayuu qoray:

# 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

Laba baako ayaa lagu arkay lo oo lagu farsameeyay CPU0 - barnaamijkeenii ugu horreeyay ee BPF oo aan macno lahayn ayaa shaqeeyay!

Waxaa xusid mudan taas bpf_printk Ma aha wax aan waxba ahayn in ay u qorto feylka cilladaha: tani ma aha kaaliyaha ugu guulaha badan ee loo isticmaalo wax soo saarka, laakiin hadafkeenu wuxuu ahaa inaan muujino wax fudud.

Helitaanka maabyada barnaamijyada BPF

Tusaale: adigoo isticmaalaya khariidad ka socota barnaamijka BPF

Qaybihii hore waxaan ku barannay sida loo sameeyo oo loo isticmaalo maab-ka-baxyada isticmaalaha, oo hadda aan eegno qaybta kernel-ka. Aan ku bilowno, sida caadada ah, tusaale. Aan dib u qorno barnaamijkeena xdp-simple.bpf.c sida soo socota:

#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";

Bilowgii barnaamijka waxaan ku darnay qeexida khariidad woo: Kani waa 8-element ah oo kaydiya qiyamka sida u64 (C waxaan ku qeexi karnaa qaabkan sida u64 woo[8]). In barnaamij "xdp/simple" Waxaan helnaa nambarka processor-ka hadda jira doorsoome key ka dibna isticmaal shaqada caawiye bpf_map_lookup_element waxaan helnaa tilmaame gelitaanka u dhigma ee shaxanka, kaas oo aanu ku kordhinay hal. Waxaa loo tarjumay Ruush: waxaan xisaabineynaa tirakoobyada CPU-gu ku farsameeyay xirmooyinka soo socda. Aan isku dayno inaan socodsiino barnaamijka:

$ 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

Aynu eegno inay ku xidhan tahay lo oo soo dir qaar ka mid ah baakado:

$ 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

Haddaba aan eegno nuxurka shaxda:

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

Ku dhawaad ​​dhammaan hababka waxaa lagu farsameeyay CPU7. Tani muhiim nooguma aha, waxa ugu muhiimsan waa in barnaamijku shaqeeyo oo aanu fahansanahay sida loo galo khariidadaha barnaamijyada BPF - anagoo adeegsanayna. хелперов bpf_mp_*.

Tilmaanta qarsoodiga ah

Marka, waxaan ka heli karnaa khariidada barnaamijka BPF anagoo adeegsanayna wicitaanada sida

val = bpf_map_lookup_elem(&woo, &key);

meesha uu u shaqeeyo caawiye u eg

void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)

laakiin waxaynu maraynaa tilmaame &woo dhisme aan la magacaabin struct { ... }...

Haddii aan eegno barnaamijka isku duwaha, waxaan aragnaa in qiimaha &woo dhab ahaantii lama qeexin (line 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
...

waxayna ku jirtaa dib udejin:

$ 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

Laakiin haddii aan eegno barnaamijkii hore loo raray, waxaan aragnaa tilmaame khariidadda saxda ah (line 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]
...

Sidaa darteed, waxaan ku soo gabagabeyn karnaa in waqtiga la bilaabayo barnaamijkeena loader, isku xirka &woo waxaa lagu beddelay wax maktabad leh libbpf. Marka hore waxaan eegi doonaa wax soo saarka 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

Waxaan aragnaa taas libbpf abuuray khariidad woo ka dibna la soo degsado barnaamijkeena simple. Aan si hoose u eegno sida aan u shubno barnaamijka:

  • wac xdp_simple_bpf__open_and_load ka faylka xdp-simple.skel.h
  • oo sababa xdp_simple_bpf__load ka faylka xdp-simple.skel.h
  • oo sababa bpf_object__load_skeleton ka faylka libbpf/src/libbpf.c
  • oo sababa bpf_object__load_xattr ka libbpf/src/libbpf.c

Shaqada ugu dambeysa, iyo waxyaabo kale, ayaa wici doona bpf_object__create_maps, kaas oo abuura ama furaya khariidado jira, iyaga oo u rogaya sharraxayaal faylal ah. (Tani waa meesha aan ku aragno BPF_MAP_CREATE wax soo saarka strace.) Marka xigta shaqada ayaa la yiraahdaa bpf_object__relocate oo waa tii inoo danaynaysa, mar haddaynu xusuusanayno wixii aannu aragnay woo miiska dib u dejinta. Baadhitaanka, waxaan ugu dambeyntii isku aragnaa shaqada bpf_program__relocate, kaas wax ka qabta raritaannada khariidadaha:

case RELO_LD64:
    insn[0].src_reg = BPF_PSEUDO_MAP_FD;
    insn[0].imm = obj->maps[relo->map_idx].fd;
    break;

Markaa waxaan qaadanaa tilmaamahayaga

18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll

kuna beddel diiwaanka isha ee ku jira BPF_PSEUDO_MAP_FD, iyo IMM-kii ugu horreeyay ee sharraxa faylka ee khariidadeena iyo, haddii ay la mid tahay, tusaale ahaan, 0xdeadbeef, ka dibna natiijada waxaan heli doonaa tilmaamaha

18 11 00 00 ef eb ad de 00 00 00 00 00 00 00 00 r1 = 0 ll

Tani waa sida macluumaadka khariidadda loogu wareejiyo barnaamij gaar ah oo la raray BPF. Xaaladdan oo kale, khariidadda waxaa la samayn karaa iyadoo la isticmaalayo BPF_MAP_CREATE, oo lagu furay aqoonsi isticmaalaya BPF_MAP_GET_FD_BY_ID.

Wadarta, marka la isticmaalayo libbpf Algorithm waa sida soo socota:

  • Inta lagu guda jiro ururinta, diiwaanada waxaa lagu abuuray miiska raritaan xiriirinta maabyada
  • libbpf furaya buuga shayga ELF, helaya dhamaan maabkii la isticmaalay oo u abuuraa sharraxayaal faylal ah
  • Sharaxayaasha faylka ayaa lagu shubaa kernel-ka iyada oo qayb ka ah tilmaamaha LD64

Sida aad qiyaasi karto, waxaa jira wax badan oo soo socda oo waa inaan eegno xudunta. Nasiib wanaag, waxaan haynaa tilmaan - waxaan qornay macnaha BPF_PSEUDO_MAP_FD geli diiwaanka isha oo aynu aasi karno, taas oo noo horseedi doonta quduusiinta oo dhan. kernel/bpf/verifier.c, halkaas oo shaqo leh magac gaar ah ay ku beddesho sharraxaadda faylka oo leh cinwaanka qaab-dhismeedka nooca 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;

(koodka buuxa waa la heli karaa link). Markaa waxaan ballaarin karnaa algorithm-kayaga:

  • marka la shubayo barnaamijka, xaqiijiyaha ayaa hubinaya isticmaalka saxda ah ee khariidadda wuxuuna qorayaa ciwaanka qaabka u dhigma struct bpf_map

Markaad soo dejisanayso binary ELF adoo isticmaalaya libbpf Wax badan ayaa jira, laakiin waxaan kaga hadli doonaa maqaallo kale.

Soodejinaya barnaamijyada iyo khariidado bilaa libbpf

Sidii ballanku ahaa, halkan waxaa tusaale u ah akhristayaasha doonaya in ay ogaadaan sida loo sameeyo loona raro barnaamij isticmaalaya maab, iyada oo aan caawin libbpf. Tani waxay noqon kartaa mid faa'iido leh marka aad ka shaqeyneyso deegaan aadan dhisi karin ku tiirsanaanta, ama aad badbaadin karto xoogaa, ama qorto barnaamij sida ply, kaas oo soo saara koodka binary BPF ee duulista.

Si loo fududeeyo raacitaanka macquulka ah, waxaan dib u qori doonaa tusaalaheena ujeedooyinkan xdp-simple. Koodhka barnaamijka oo dhammaystiran oo xoogaa la ballaariyey ayaa laga heli karaa tan gist.

Macnaha codsigayagu waa sida soo socota:

  • samee khariidad nooc ah BPF_MAP_TYPE_ARRAY iyadoo la isticmaalayo amarka BPF_MAP_CREATE,
  • samee barnaamij isticmaalaya khariidadan,
  • ku xidh barnaamijka interface-ka lo,

taas oo u tarjumeysa aadanaha sida

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

waa map_create wuxuu abuuraa khariidad si la mid ah sidii aan ku samaynay tusaalihii ugu horreeyay ee ku saabsan nidaamka wicitaanka bpf - "Kernel, fadlan ii samee khariidad cusub oo qaabaysan 8 walxood oo kala ah __u64 oo ii soo celi tusaha faylka":

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

Barnaamijka sidoo kale waa sahlan tahay in la soo geliyo:

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

Qaybta qallafsan prog_load waa qeexida barnaamijkeena BPF oo ah qaab dhismeedyo kala duwan struct bpf_insn insns[]. Laakiin maadaama aan isticmaaleyno barnaamij aan ku hayno C, waxaan wax yar khiyaami karnaa:

$ 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

Wadar ahaan, waxaan u baahanahay inaan qorno 14 tilmaamo qaab dhismeed sida struct bpf_insn (talo: Ka qaad qashinka kore, dib u akhri qaybta tilmaamaha, fur linux/bpf.h и linux/bpf_common.h iskuna day inaad go'aansato struct bpf_insn insns[] kaligiis):

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

Layli kuwa aan iyagu qorin tan laftooda - raadi map_fd.

Waxa barnaamijkayaga ku hadhay qayb kale oo aan la shaacin - xdp_attach. Nasiib darro, barnaamijyada sida XDP laguma xidhi karo iyada oo la isticmaalayo nidaamka wicitaanka bpf. Dadka abuuray BPF iyo XDP waxay ka yimaadeen bulshada Linux ee khadka tooska ah, taas oo macnaheedu yahay inay isticmaaleen midka ay yaqaaniin (laakiin maaha inay isticmaalaan. caadi dadka) interface ee la falgalka kernel-ka: saldhigyada netlinksidoo kale eeg RFC3549. Habka ugu fudud ee loo fuliyo xdp_attach ayaa koodhka ka soo koobiyaysa libbpf, oo ah, laga bilaabo faylka netlink.c, taasoo ah tii aanu samaynay, annagoo in yar soo gaabinay:

Ku soo dhawoow aduunka netlink sockets

Fur nooca godka 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;
}

Waxaan ka akhrinay godkan:

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

Ugu dambayntii, waa kan shaqadeenii oo furta godka oo u soo dirtaa farriin gaar ah oo ay ku jirto sharraxaha faylka:

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

Markaa, wax walbaa waxay diyaar u yihiin tijaabinta:

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

Aynu eegno in barnaamijkeenu ku xidhan yahay iyo in kale 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

Aan dirno pings oo aan eegno khariidada:

$ 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, wax walba waa shaqeeyaan. Ogow, jid ahaan, in khariidadeena ay mar kale soo bandhigtay qaab bytes ah. Tani waxay sabab u tahay xaqiiqda ah, ka duwan libbpf Ma aan shubin macluumaadka nooca (BTF). Laakiin wax badan ayaan ka hadli doonaa marka xigta.

Qalabka Horumarinta

Qaybtan, waxaan ku eegi doonaa qalabka wax soo saar ee BPF ugu yar.

Guud ahaan, uma baahnid wax gaar ah si aad u horumariso barnaamijyada BPF - BPF waxay ku shaqeysaa kernel qaybinta wanaagsan, barnaamijyadana waxaa la dhisay iyadoo la isticmaalayo clang, kaas oo laga keeni karo xirmada. Si kastaba ha noqotee, sababtoo ah xaqiiqda ah in BPF ay ku jirto horumarinta, kernel-ka iyo qalabka ayaa si joogto ah isbeddelaya, haddii aadan rabin inaad qorto barnaamijyada BPF adigoo isticmaalaya habab hore oo laga bilaabo 2019, markaa waxaad u baahan doontaa inaad ururiso.

  • llvm/clang
  • pahole
  • xudunta u ah
  • bpftool

(Tixraac ahaan, qaybtan iyo dhammaan tusaalooyinka maqaalka waxa lagu maamulay Debian 10.)

lvm/qabiil

BPF waxay saaxiib la tahay LLVM, in kasta oo barnaamijyada BPF ee dhowaanahan lagu soo ururin karo gcc, dhammaan horumarka hadda jira waxaa loo fuliyaa LLVM. Sidaa darteed, marka hore, waxaan dhisi doonaa version hadda clang ka 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
... много времени спустя
$

Hadda waxaan hubin karnaa in wax walba si sax ah isugu yimaadeen:

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

(Awaamiirta golaha clang aan ka qaatay bpf_devel_QA.)

Ma rakibi doono barnaamijyada aan hadda dhisnay, laakiin taa beddelkeeda ku darso PATH, tusaale ahaan:

export PATH="`pwd`/bin:$PATH"

(Tani waa lagu dari karaa .bashrc ama fayl gaar ah. Shakhsi ahaan, waxaan ku daraa waxyaalahan oo kale ~/bin/activate-llvm.sh markii loo baahdona waan sameeyaa . activate-llvm.sh.)

Pahole iyo BTF

Faa'iidada pahole loo isticmaalo marka la dhisayo kernel si loo abuuro macluumaadka cilladaha ee qaabka BTF. Ma geli doono si faahfaahsan maqaalkan ku saabsan faahfaahinta tignoolajiyada BTF, marka laga reebo xaqiiqda ah inay ku habboon tahay oo aan rabno inaan isticmaalno. Markaa haddii aad dhisayso kernel-kaaga, marka hore dhis pahole (la'aan pahole ma awoodi doontid inaad ku dhisto kernel ikhtiyaarka 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

Kernels loogu talagalay tijaabinta BPF

Marka aan sahaminayo fursadaha BPF, waxaan rabaa in aan ururiyo xuduntayda. Tani, guud ahaan marka loo eego, maahan lagama maarmaan, maadaama aad awoodi doonto inaad ururiso oo aad ku shubto barnaamijyada BPF kernel-ka qaybinta, si kastaba ha ahaatee, haysashada kernel-kaaga ayaa kuu ogolaanaysa inaad isticmaasho sifooyinka BPF ee ugu dambeeyay, kaas oo ka muuqan doona qaybintaada bilaha ugu fiican. , ama, sida kiiska qaar ka mid ah qalabyada wax-ka-hortagga ah gabi ahaanba lama daabici doono mustaqbalka la filayo. Sidoo kale, udub-dhexaadkeeda ayaa dareensan in ay muhiim tahay in lagu tijaabiyo koodka.

Si aad u dhisto kernel waxaad u baahan tahay, marka hore, kernel laftiisa, marka labaadna, faylka qaabeynta kernel. Si loo tijaabiyo BPF waxaan isticmaali karnaa kuwa caadiga ah vanilj kernel ama mid ka mid ah kernels horumarinta. Taariikh ahaan, horumarinta BPF waxay ka dhacdaa gudaha bulshada isku xidhka Linux, sidaas darteed dhammaan isbeddellada mar hore ama hadhow waxay maraan David Miller, ilaaliye isku xidhka Linux. Iyada oo ku xidhan dabeecadooda - wax ka beddelka ama astaamo cusub - isbeddellada shabakadu waxay u dhacaan mid ka mid ah laba qaybood - net ama net-next. Isbeddellada BPF waxaa loo qaybiyaa si la mid ah inta u dhaxaysa bpf и bpf-next, kuwaas oo markaa la isugu geeyey shabaq iyo shabaq-xiga, siday u kala horreeyaan. Faahfaahin dheeraad ah, arag bpf_devel_QA и netdev-FAQ. Markaa dooro kernel ku salaysan dhadhankaaga iyo baahiyaha xasiloonida nidaamka aad tijaabinayso (*-next kernels ayaa ah kuwa ugu xasilloon kuwa liiska ku jira).

Way ka baxsan tahay baaxadda maqaalkan in laga hadlo sida loo maareeyo faylasha qaabeynta kernel - waxaa loo maleynayaa inaad horeyba u taqaanay sida tan loo sameeyo, ama diyaar u ah inuu wax barto kaligiis. Si kastaba ha ahaatee, tilmaamaha soo socdaa waa inay ahaadaan kuwo badan ama ka yar oo ku filan si ay kuu siiyaan nidaam karti u leh BPF.

Soo deji mid ka mid ah kernels-ka sare:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
$ cd bpf-next

Dhis isku xidhka kernel-ka shaqeeya ee ugu yar:

$ cp /boot/config-`uname -r` .config
$ make localmodconfig

Daar doorashooyinka BPF ee faylka .config doorashadaada (badanaa CONFIG_BPF mar horeba waa la hawlgelin doonaa maadaama systemd uu isticmaalo). Waa kuwan liiska xulashooyinka kernel-ka loo isticmaalo maqaalkan:

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

Kadibna si fudud ayaan u ururin karnaa oo u rakibi karnaa modules-yada iyo kernel-ka (sida, waxaad ku ururin kartaa kernel adoo isticmaalaya kuwa cusub ee la soo ururiyey. clangisagoo ku daray CC=clang):

$ make -s -j $(getconf _NPROCESSORS_ONLN)
$ sudo make modules_install
$ sudo make install

oo dib ugu bilow kernel-ka cusub (waxaan u isticmaalayaa tan kexec ka xirmada 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

Utility inta badan la isticmaalo ee maqaalka waxay noqon doontaa utility bpftool, oo loo keenay qayb ka mid ah kernel Linux. Waxaa qoray oo ay ilaaliyeen horumariyeyaasha BPF ee horumarinta BPF waxaana loo isticmaali karaa in lagu maareeyo dhammaan noocyada walxaha BPF - barnaamijyada rarka, abuurista iyo tafatirka maabka, sahaminta nolosha nidaamka deegaanka BPF, iwm. Dukumentiyada qaabka koodhadhka isha ee bogagga man waa la heli karaa xudunta u ah ama, mar hore la soo ururiyey, shabaqa.

Waqtiga qoraalkan bpftool u soo diyaarsan oo kaliya RHEL, Fedora iyo Ubuntu (eeg, tusaale ahaan, duntan, kaas oo ka sheekeynaya sheekada aan dhammaan ee baakadaha bpftool ee Debian). Laakiin haddii aad hore u dhistay kernelkaaga, ka dibna dhis bpftool sida ugu fudud ee pie:

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

$

(Halkan ${linux} - kani waa hagahaaga kernel-ka.) Kadib fulinta amaradan bpftool waxaa lagu soo ururin doonaa buug-tusaha ${linux}/tools/bpf/bpftool waxaana lagu dari karaa wadada (marka ugu horeysa ee isticmaalaha root) ama ku koobbi /usr/local/sbin.

Ururi bpftool waxaa fiican in la isticmaalo kan dambe clang, la soo ururiyey sida kor lagu sharaxay, oo hubi in si sax ah loo ururiyey - adigoo isticmaalaya, tusaale ahaan, amarka

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

kaas oo tusi doona sifada BPF ee karti u leh kernelkaaga.

By habka, amarkii hore waxaa loo maamuli karaa sida

# bpftool f p k

Tan waxaa lagu sameeyaa isbarbardhig la leh yutiilitida xirmada iproute2, meesha aan ka, tusaale ahaan, odhan ip a s eth0 halkii ip addr show dev eth0.

gunaanad

BPF waxay kuu ogolaanaysaa inaad kabto boodada si aad si wax ku ool ah u cabbirto oo aad duulimaadka u beddesho shaqada xudunta u ah. Nidaamku wuxuu u soo baxay inuu noqdo mid aad u guuleysta, oo ku jira caadooyinka ugu wanaagsan ee UNIX: hab fudud oo kuu ogolaanaya inaad dib u dhigto barnaamijka kernel-ka ayaa loo oggolaaday tiro aad u badan oo dad ah iyo ururo ah inay tijaabiyaan. Iyo, inkasta oo tijaabooyinka, iyo sidoo kale horumarinta kaabayaasha BPF lafteeda, ay aad uga fog yihiin in la dhammeeyo, nidaamku wuxuu horeyba u leeyahay ABI xasilloon oo kuu ogolaanaya inaad dhisto la isku halleyn karo, iyo tan ugu muhiimsan, caqli-gal ganacsi oo waxtar leh.

Waxaan jeclaan lahaa in aan ogaado, fikradayda, tignoolajiyada ayaa noqotay mid caan ah sababtoo ah, dhinaca kale, waa la isticmaali karaa. inuu ciyaaro (nashqada mishiinka waxa la fahmi karaa in ka badan ama ka yar hal fiidkii), dhinaca kale, si loo xalliyo dhibaatooyinka aan la xalin karin (quruxda) ka hor muuqaalkiisa. Labadan qaybood oo wada jir ah ayaa dadka ku qasbaya inay tijaabiyaan oo ay ku riyoodaan, taas oo keenta in ay soo baxaan xalal cusub oo badan.

Maqaalkani, in kasta oo aan si gaar ah u gaabanayn, waa hordhac kaliya ee adduunka BPF mana qeexayo sifooyinka "horumarsan" iyo qaybaha muhiimka ah ee dhismaha. Qorshaha horay u socdaa waa sidan oo kale: maqaalka soo socdaa wuxuu noqon doonaa dulmar ku saabsan noocyada barnaamijka BPF (waxaa jira 5.8 nooc oo barnaamij ah oo lagu taageerayo kernel 30), ka dib waxaan ugu dambeyntii eegi doonaa sida loo qoro codsiyada BPF ee dhabta ah iyadoo la adeegsanayo barnaamijyada raadinta kernel Tusaale ahaan, ka dib waa waqtigii koorsada qoto dheer ee dhismaha BPF, oo ay ku xigto tusaalooyinka isku xirka BPF iyo codsiyada amniga.

Maqaaladii hore ee taxanahan

  1. BPF ee kuwa yaryar, qayb eber: BPF caadiga ah

Xiriirinta

  1. Tixraaca BPF iyo XDP - dukumeenti ku saabsan BPF ee cilium, ama si ka badan oo sax ah oo laga helay Daniel Borkman, mid ka mid ah abuurayaasha iyo ilaaliyayaasha BPF. Tani waa mid ka mid ah sharraxaadda ugu horreeya ee halista ah, taas oo ka duwan kuwa kale in Daanyeel uu si sax ah u yaqaan waxa uu qorayo oo aysan jirin wax khalad ah. Gaar ahaan, dukumeentigani wuxuu qeexayaa sida loola shaqeeyo barnaamijyada BPF ee noocyada XDP iyo TC iyadoo la adeegsanayo tamarta caanka ah ip ka xirmada iproute2.

  2. Dukumeenti/shabakad/filter.txt - faylka asalka ah oo wata dukumeenti loogu talagalay classic ka dibna BPF la dheereeyey. Akhris wanaagsan haddii aad rabto inaad u dhex gasho luqadda kulanka iyo faahfaahinta qaabdhismeedka farsamada.

  3. Blog ku saabsan BPF ka facebook. Si dhif ah ayaa loo cusbooneysiiyaa, laakiin si habboon, sida Alexei Starovoitov (qoraaga eBPF) iyo Andrii Nakryiko - (hayaha) ay halkaas ku qoraan. libbpf).

  4. Sirta bpftool. Dul twitter madadaalo leh oo ka socda Quentin Monnet oo wata tusaalooyin iyo siraha isticmaalka bpftool.

  5. U quusi ​​BPF: liiska agabka akhriska. Liis weyn (oo wali la hayo) ee xiriirinta dukumeentiyada BPF ee Quentin Monnet.

Source: www.habr.com

Add a comment