BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Anns an toiseach bha teicneòlas ann agus b 'e BPF a bh' air. Thug sinn sùil oirre roimhe seo, Artaigil an t-Seann Tiomnaidh den t-sreath seo. Ann an 2013, tro oidhirpean Alexei Starovoytov agus Daniel Borkman, chaidh dreach nas fheàrr dheth, air a bharrrachadh airson innealan 64-bit ùr-nodha, a leasachadh agus a thoirt a-steach don kernel Linux. Chaidh an teicneòlas ùr seo ainmeachadh gu h-aithghearr BPF a-staigh, an uairsin air ath-ainmeachadh Extended BPF, agus a-nis, às deidh grunn bhliadhnaichean, tha a h-uile duine dìreach ga ainmeachadh mar BPF.

Gu ìre mhòr, leigidh BPF leat còd neo-riaghailteach air a sholarachadh le neach-cleachdaidh a ruith ann an àite kernel Linux, agus thionndaidh an ailtireachd ùr a-mach gu bhith cho soirbheachail is gum feum sinn dusan artaigil a bharrachd gus cunntas a thoirt air na tagraidhean aige. (Is e an aon rud nach do rinn an luchd-leasachaidh gu math, mar a chì thu sa chòd coileanaidh gu h-ìosal, a bhith a’ cruthachadh suaicheantas iomchaidh.)

Tha an artaigil seo a’ toirt cunntas air structar inneal brìgheil BPF, eadar-aghaidh kernel airson a bhith ag obair le BPF, innealan leasachaidh, a bharrachd air tar-shealladh goirid, glè ghoirid air na comasan a th ’ann, i.e. a h-uile dad a dh’ fheumas sinn san àm ri teachd airson sgrùdadh nas doimhne air cleachdadh practaigeach BPF.
BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Geàrr-chunntas den artaigil

Ro-ràdh gu BPF ailtireachd. An toiseach, bheir sinn sealladh eun air ailtireachd BPF agus bheir sinn cunntas air na prìomh phàirtean.

Clàran agus siostam stiùiridh inneal brìgheil BPF. A-cheana le beachd air an ailtireachd gu h-iomlan, bheir sinn cunntas air structar inneal brìgheil BPF.

Cearcall beatha nithean BPF, siostam faidhle bpffs. Anns an earrainn seo, bheir sinn sùil nas mionaidiche air cearcall-beatha nithean BPF - prògraman agus mapaichean.

A’ riaghladh nithean a’ cleachdadh gairm siostam bpf. Le beagan tuigse air an t-siostam mar-thà na àite, seallaidh sinn mu dheireadh air mar a chruthaicheas agus a làimhsicheas sinn nithean bho àite luchd-cleachdaidh a’ cleachdadh gairm siostam sònraichte - bpf(2).

Пишем программы BPF с помощью libbpf. Gu dearbh, faodaidh tu prògraman a sgrìobhadh a 'cleachdadh gairm siostam. Ach tha e duilich. Airson suidheachadh nas reusanta, leasaich prògramadairean niùclasach leabharlann libbpf. Cruthaichidh sinn cnàimhneach tagraidh BPF bunaiteach a chleachdas sinn ann an eisimpleirean às deidh sin.

Luchd-cuideachaidh Kernel. An seo ionnsaichidh sinn mar a gheibh prògraman BPF cothrom air gnìomhan neach-cuideachaidh kernel - inneal a bhios, còmhla ri mapaichean, a’ leudachadh gu bunaiteach comasan an BPF ùr an taca ris an fhear clasaigeach.

Cothrom air mapaichean bho phrògraman BPF. Ron ìre seo, bidh fios againn gu leòr gus tuigsinn gu cinnteach mar as urrainn dhuinn prògraman a chruthachadh a bhios a’ cleachdadh mhapaichean. Agus bheir sinn eadhon sùil aithghearr air an neach-dearbhaidh mòr agus cumhachdach.

Innealan leasachaidh. Cuideachadh earrann air mar a chruinnicheas tu na goireasan riatanach agus kernel airson deuchainnean.

An co-dhùnadh. Aig deireadh an artaigil, gheibh an fheadhainn a leughas cho fada seo faclan brosnachail agus tuairisgeul goirid air na thachras anns na h-artaigilean a leanas. Bidh sinn cuideachd a’ liostadh grunn cheanglaichean airson fèin-ionnsachadh dhaibhsan aig nach eil miann no comas feitheamh airson an leantainn.

Ro-ràdh do BPF ailtireachd

Mus tòisich sinn a’ beachdachadh air ailtireachd BPF, bheir sinn iomradh air aon turas mu dheireadh (oh) gu BPF clasaigeach, a chaidh a leasachadh mar fhreagairt air teachd innealan RISC agus a dh’ fhuasgail duilgheadas sìoladh pacaid èifeachdach. Thionndaidh a-mach gu robh an ailtireachd cho soirbheachail is, às deidh dha a bhith air a bhreith anns na naochadan ruith ann am Berkeley UNIX, chaidh a ghiùlain chun mhòr-chuid de na siostaman obrachaidh a th ’ann, thàinig e beò gu na ficheadan seòlta agus tha e fhathast a’ lorg thagraidhean ùra.

Chaidh am BPF ùr a leasachadh mar fhreagairt do uile-làthaireach innealan 64-bit, seirbheisean sgòthan agus barrachd feum air innealan airson SDN a chruthachadh (Sbathar-dcriochnaichte network). Air a leasachadh le innleadairean lìonra kernel mar àite leasaichte airson am BPF clasaigeach, lorg am BPF ùr gu litearra sia mìosan às deidh sin tagraidhean anns an obair dhoirbh a bhith a’ lorg siostaman Linux, agus a-nis, sia bliadhna às deidh a choltas, bidh feum againn air an ath artaigil slàn dìreach airson liosta de na diofar sheòrsaichean de phrògraman.

Dealbhan èibhinn

Aig a chridhe, tha BPF na inneal brìgheil bogsa gainmhich a leigeas leat còd “mì-riaghailteach” a ruith ann an àite kernel gun a bhith a ’toirt buaidh air tèarainteachd. Tha prògraman BPF air an cruthachadh ann an àite luchd-cleachdaidh, air an luchdachadh a-steach don kernel, agus ceangailte ri stòr tachartais air choreigin. Dh’ fhaodadh tachartas a bhith, mar eisimpleir, lìbhrigeadh pacaid gu eadar-aghaidh lìonra, cur air bhog gnìomh kernel, msaa. A thaobh pasgan, bidh cothrom aig a’ phrògram BPF air dàta agus meata-dàta a’ phacaid (airson leughadh agus, is dòcha, sgrìobhadh, a rèir an t-seòrsa de phrògram); a thaobh a bhith a’ ruith gnìomh kernel, na h-argamaidean aig an gnìomh, a’ toirt a-steach comharran gu cuimhne kernel, msaa.

Bheir sinn sùil nas mionaidiche air a 'phròiseas seo. An toiseach, bruidhnidh sinn mun chiad eadar-dhealachadh bhon BPF clasaigeach, a chaidh prògraman airson a sgrìobhadh ann an assembler. Anns an dreach ùr, chaidh an ailtireachd a leudachadh gus an gabhadh prògraman a sgrìobhadh ann an cànanan àrd-ìre, gu sònraichte, gu dearbh, ann an C. Airson seo, chaidh backend airson llvm a leasachadh, a leigeas le bhith a’ gineadh bytecode airson ailtireachd BPF.

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Chaidh ailtireachd BPF a dhealbhadh, gu ìre, gus ruith gu h-èifeachdach air innealan an latha an-diugh. Gus an obraich seo ann an cleachdadh, tha còd byte BPF, aon uair ‘s gu bheil e air a luchdachadh a-steach don kernel, air eadar-theangachadh gu còd dùthchasach a’ cleachdadh co-phàirt ris an canar compiler JIT (Just In Time). An ath rud, ma chuimhnicheas tu, ann am BPF clasaigeach chaidh am prògram a luchdachadh a-steach don kernel agus a cheangal ri stòr an tachartais gu atamach - ann an co-theacsa aon ghairm siostam. Anns an ailtireachd ùr, bidh seo a ’tachairt ann an dà ìre - an toiseach, tha an còd air a luchdachadh a-steach don kernel a’ cleachdadh gairm siostam bpf(2)agus an uairsin, nas fhaide air adhart, tro dhòighean eile a tha ag atharrachadh a rèir an seòrsa prògram, bidh am prògram a’ ceangal ri stòr an tachartais.

An seo is dòcha gu bheil ceist aig an leughadair: an robh e comasach? Ciamar a tha sàbhailteachd cur an gnìomh a leithid de chòd cinnteach? Tha sàbhailteachd cur gu bàs air a ghealltainn dhuinn leis an ìre de bhith a’ luchdachadh phrògraman BPF ris an canar verifier (ann am Beurla canar verifier ris an ìre seo agus cumaidh mi a’ cleachdadh am facal Beurla):

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Is e anailisiche statach a th’ ann an dearbhaiche a nì cinnteach nach cuir prògram dragh air gnìomhachd àbhaisteach an kernel. Chan eil seo, leis an t-slighe, a 'ciallachadh nach urrainn don phrògram bacadh a chur air obrachadh an t-siostaim - faodaidh prògraman BPF, a rèir an t-seòrsa, earrannan de chuimhne kernel a leughadh agus ath-sgrìobhadh, luachan gnìomhan a thilleadh, trim, cuir ris, ath-sgrìobhadh. agus eadhon pacaidean lìonra air adhart. Tha an dearbhaiche a’ gealltainn nach tuit ruith prògram BPF an kernel agus nach bi prògram aig a bheil, a rèir nan riaghailtean, aig a bheil cothrom sgrìobhaidh, mar eisimpleir, dàta pacaid a tha a’ dol a-mach, comasach air a’ chuimhne kernel taobh a-muigh a’ phacaid ath-sgrìobhadh. Bheir sinn sùil nas mionaidiche air an neach-dearbhaidh anns an earrainn fhreagarrach, às deidh dhuinn eòlas fhaighinn air na pàirtean eile de BPF.

Mar sin dè a tha sinn air ionnsachadh gu ruige seo? Bidh an neach-cleachdaidh a’ sgrìobhadh prògram ann an C, ga luchdachadh a-steach don kernel a’ cleachdadh gairm siostaim bpf(2), far a bheil e air a sgrùdadh le neach-dearbhaidh agus air eadar-theangachadh gu bytecode dùthchasach. An uairsin bidh an aon neach-cleachdaidh no neach-cleachdaidh eile a 'ceangal a' phrògraim ri stòr an tachartais agus bidh e a 'tòiseachadh a' cur an gnìomh. Tha feum air bròg is ceangal a sgaradh airson grunn adhbharan. An toiseach, tha ruith dearbhaidh gu math daor agus le bhith a’ luchdachadh sìos an aon phrògram grunn thursan bidh sinn a’ caitheamh ùine coimpiutair. San dàrna h-àite, tha dìreach mar a tha prògram ceangailte an urra ris an t-seòrsa aige, agus is dòcha nach bi aon eadar-aghaidh “uile-choitcheann” a chaidh a leasachadh o chionn bliadhna freagarrach airson seòrsaichean ùra de phrògraman. (Ged a tha an ailtireachd a’ fàs nas aibidh a-nis, tha beachd ann an eadar-aghaidh seo aonachadh aig an ìre libbpf.)

Faodaidh an leughadair furachail mothachadh nach eil sinn deiseil leis na dealbhan fhathast. Gu dearbh, chan eil a h-uile rud gu h-àrd a’ mìneachadh carson a tha BPF gu bunaiteach ag atharrachadh an dealbh an taca ri BPF clasaigeach. Is e dà ùr-ghnàthachadh a tha a’ leudachadh gu mòr an raon iomchaidheachd an comas cuimhne co-roinnte agus gnìomhan cuideachaidh kernel a chleachdadh. Ann am BPF, tha cuimhne co-roinnte air a chur an gnìomh a 'cleachdadh mhapaichean ris an canar - structaran dàta co-roinnte le API sònraichte. Is dòcha gun d’ fhuair iad an t-ainm seo oir b’ e clàr hash a’ chiad seòrsa mapa a nochd. An uairsin nochd arrays, bùird hash ionadail (gach-CPU) agus arrays ionadail, craobhan sgrùdaidh, mapaichean anns an robh comharran gu prògraman BPF agus mòran a bharrachd. Is e an rud a tha inntinneach dhuinn a-nis gu bheil comas aig prògraman BPF a-nis cumail a’ dol eadar gairmean agus a roinn le prògraman eile agus le àite luchd-cleachdaidh.

Gheibhear gu mapaichean bho phròiseasan luchd-cleachdaidh a’ cleachdadh gairm siostaim bpf(2), agus bho phrògraman BPF a’ ruith san kernel a’ cleachdadh gnìomhan cuideachaidh. A bharrachd air an sin, tha luchd-cuideachaidh ann chan ann a-mhàin airson obrachadh le mapaichean, ach cuideachd gus faighinn gu comasan kernel eile. Mar eisimpleir, faodaidh prògraman BPF gnìomhan cuideachaidh a chleachdadh gus pacaidean a chuir air adhart gu eadar-aghaidh eile, tachartasan perf a ghineadh, faighinn gu structaran kernel, agus mar sin air adhart.

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Ann an geàrr-chunntas, tha BPF a’ toirt seachad comas còd cleachdaiche neo-riaghailteach, ie, air a dhearbhadh le dearbhadair, a luchdachadh a-steach gu àite kernel. Faodaidh an còd seo staid eadar gairmean agus dàta iomlaid a shàbhaladh le àite luchd-cleachdaidh, agus tha cothrom aige cuideachd air fo-shiostaman kernel a tha ceadaichte leis an t-seòrsa prògram seo.

Tha seo mar-thà coltach ris na comasan a tha air an toirt seachad le modalan kernel, an taca ris a bheil cuid de bhuannachdan aig BPF (gu dearbh, chan urrainn dhut ach coimeas a dhèanamh eadar tagraidhean coltach ris, mar eisimpleir, lorg siostam - chan urrainn dhut draibhear neo-riaghailteach a sgrìobhadh le BPF). Faodaidh tu ìre inntrigidh nas ìsle a thoirt fa-near (chan eil cuid de ghoireasan a chleachdas BPF ag iarraidh gum bi sgilean prògramadh kernel, no sgilean prògramadh san fharsaingeachd), sàbhailteachd ùine ruith (tog do làmh anns na beachdan dhaibhsan nach do bhris an siostam nuair a sgrìobhas iad no modalan deuchainn), atomicity - tha ùine downt ann nuair a thathar ag ath-luchdachadh mhodalan, agus tha fo-shiostam BPF a’ dèanamh cinnteach nach tèid tachartasan sam bith a chall (airson a bhith cothromach, chan eil seo fìor airson a h-uile seòrsa de phrògraman BPF).

Tha làthaireachd a leithid de chomasan a’ dèanamh BPF na inneal uile-choitcheann airson an kernel a leudachadh, a tha air a dhearbhadh ann an cleachdadh: tha barrachd is barrachd sheòrsan phrògraman ùra air an cur ri BPF, bidh barrachd is barrachd chompanaidhean mòra a’ cleachdadh BPF air frithealaichean sabaid 24 × 7, barrachd is barrachd. bidh luchd-tòiseachaidh a’ togail an gnìomhachas air fuasglaidhean a tha stèidhichte air BPF. Tha BPF air a chleachdadh anns a h-uile àite: ann a bhith a’ dìon an aghaidh ionnsaighean DDoS, a’ cruthachadh SDN (mar eisimpleir, a’ cur an gnìomh lìonraidhean airson kubernetes), mar phrìomh inneal lorg siostam agus neach-cruinneachaidh staitistig, ann an siostaman lorg sàrachaidh agus siostaman bogsa gainmhich, msaa.

Nach cuir sinn crìoch air a’ phàirt ath-shealladh den artaigil an seo agus thoir sùil nas mionaidiche air an inneal brìgheil agus eag-shiostam BPF.

Digression: goireasan

Airson a bhith comasach air na h-eisimpleirean a ruith anns na h-earrannan a leanas, is dòcha gu feum thu grunn ghoireasan, co-dhiù llvm/clang le taic bpf agus bpftool. Anns an earrainn Innealan Leasachaidh Faodaidh tu an stiùireadh airson na goireasan a cho-chruinneachadh, a bharrachd air an kernel agad a leughadh. Tha an earrann seo air a chuir gu h-ìosal gus nach cuir thu dragh air co-sheirm an taisbeanaidh againn.

Clàran inneal brìgheil BPF agus siostam stiùiridh

Chaidh ailtireachd agus siostam àithne BPF a leasachadh a’ toirt aire don fhìrinn gun tèid prògraman a sgrìobhadh ann an cànan C agus, às deidh an luchdachadh a-steach don kernel, eadar-theangachadh gu còd dùthchasach. Mar sin, chaidh an àireamh de chlàran agus an t-seata òrdughan a thaghadh le sùil ri eadar-ghearradh, ann an seagh matamataigeach, comasan innealan an latha an-diugh. A bharrachd air an sin, chaidh grunn chuingealachaidhean a chuir air prògraman, mar eisimpleir, gu o chionn ghoirid cha robh e comasach lùban agus fo-òrdughan a sgrìobhadh, agus bha an àireamh de stiùiridhean cuibhrichte gu 4096 (a-nis faodaidh prògraman sochair suas ri millean stiùireadh a luchdachadh).

Tha aon chlàr deug 64-bit aig BPF a tha ruigsinneach don neach-cleachdaidh r0-r10 agus cuntair prògram. Clàr r10 tha puing frèam ann agus tha e ri leughadh a-mhàin. Tha cothrom aig prògraman air stac 512-byte aig àm ruith agus àireamh neo-chuingealaichte de chuimhne co-roinnte ann an cruth mhapaichean.

Tha cead aig prògraman BPF seata sònraichte de luchd-cuideachaidh kernel seòrsa prògram a ruith agus, o chionn ghoirid, gnìomhan cunbhalach. Gabhaidh gach gnìomh ris an canar suas ri còig argamaidean, air an toirt seachad ann an clàran r1-r5, agus thèid an luach tilleadh gu r0. Tha e cinnteach gu bheil an dèidh tilleadh bhon ghnìomh, na th 'anns na clàran r6-r9 Cha tig atharrachadh.

Airson prògram èifeachdach eadar-theangachadh, clàran r0-r11 airson a h-uile ailtireachd le taic air a mhapadh gu sònraichte gu fìor chlàran, a’ toirt aire do fheartan ABI na h-ailtireachd gnàthach. Mar eisimpleir, airson x86_64 clàran r1-r5, air a chleachdadh gus paramadairean gnìomh a thoirt seachad, air an taisbeanadh air rdi, rsi, rdx, rcx, r8, a thathas a’ cleachdadh gus paramadairean a chuir air adhart gu gnìomhan x86_64. Mar eisimpleir, tha an còd air an taobh chlì ag eadar-theangachadh don chòd air an taobh cheart mar seo:

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

Clàr r0 cuideachd air a chleachdadh gus toradh coileanadh prògram a thilleadh, agus sa chlàr r1 tha am prògram air a thoirt seachad mar chomharra don cho-theacsa - a rèir an seòrsa prògram, dh’ fhaodadh seo a bhith, mar eisimpleir, structar struct xdp_md (airson XDP) no structar struct __sk_buff (airson diofar phrògraman lìonra) no structar struct pt_regs (airson diofar sheòrsaichean de phrògraman lorg), msaa.

Mar sin, bha seata chlàran againn, luchd-cuideachaidh kernel, stac, comharra co-theacsa agus cuimhne co-roinnte ann an cruth mhapaichean. Chan e gu bheil seo uile gu tur riatanach air an turas, ach ...

Leanaidh sinn leis an tuairisgeul agus bruidhnidh sinn mun t-siostam àithne airson a bhith ag obair leis na nithean sin. A h-uile (Cha mhòr a h-uile) Tha meud 64-bit stèidhichte aig stiùireadh BPF. Ma choimheadas tu air aon stiùireadh air inneal Big Endian 64-bit chì thu

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

tha e Code - is e seo còdachadh an stiùiridh, Dst/Src nan còdachaidhean aig a’ ghlacadair agus an tùs, fa leth, Off - cuir a-steach ainm-sgrìobhte 16-bit, agus Imm a tha na shlànaighear soidhnichte 32-bit air a chleachdadh ann an cuid de stiùiridhean (coltach ris an cBPF seasmhach K). Còdachadh Code tha aon de dhà sheòrsa ann:

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Bidh clasaichean stiùiridh 0, 1, 2, 3 a’ mìneachadh òrdughan airson obrachadh le cuimhne. iad canar, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, fa leth. Clasaichean 4, 7 (BPF_ALU, BPF_ALU64) a’ dèanamh suas seata de stiùiridhean ALU. Clasaichean 5, 6 (BPF_JMP, BPF_JMP32) anns a bheil stiùireadh leum.

Tha am plana san àm ri teachd airson sgrùdadh a dhèanamh air siostam stiùiridh BPF mar a leanas: an àite a bhith a’ toirt cunntas mionaideach air a h-uile stiùireadh agus na crìochan aca, seallaidh sinn ri eisimpleir no dhà san earrann seo agus bhuapa bidh e soilleir mar a tha an stiùireadh ag obair agus mar a nì thu. cuir às le làimh faidhle binary sam bith airson BPF. Gus an stuth a dhaingneachadh nas fhaide air adhart san artaigil, coinnichidh sinn cuideachd ri stiùireadh fa leth anns na h-earrannan mu Verifier, compiler JIT, eadar-theangachadh clasaigeach BPF, a bharrachd air nuair a bhios sinn a ’sgrùdadh mhapaichean, a’ gairm gnìomhan, msaa.

Nuair a bhios sinn a 'bruidhinn mu stiùireadh fa leth, bheir sinn iomradh air na prìomh fhaidhlichean bpf.h и bpf_common.h, a tha a’ mìneachadh còdan àireamhach stiùireadh BPF. Nuair a bhios tu a’ sgrùdadh ailtireachd leat fhèin agus/no a’ parsadh binaries, gheibh thu semantics anns na stòran a leanas, air an òrdachadh ann an òrdugh iom-fhillteachd: Sònrachadh EBPF neo-oifigeil, Stiùireadh Iomraidh BPF agus XDP, Seata Stiùiridh, Sgrìobhainnean/lìonradh/filter.txt agus, gu dearbh, ann an còd stòr Linux - dearbhaiche, JIT, eadar-theangair BPF.

Eisimpleir: cuir às do BPF nad cheann

Bheir sinn sùil air eisimpleir anns a bheil sinn a 'cur ri chèile prògram readelf-example.c agus thoir sùil air an dàna a thig às. Nochdaidh sinn an susbaint tùsail readelf-example.c gu h-ìosal, às deidh dhuinn a loidsig a thoirt air ais bho chòdan 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 ................

A 'chiad cholbh ann an toradh readelf na indentation agus mar sin tha ceithir òrduighean anns a’ phrògram againn:

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

Tha còdan àithne co-ionann b7, 15, b7 и 95. Cuimhnich gur e na trì pìosan as cudromaiche an clas stiùiridh. Anns a 'chùis againn, tha an ceathramh pìos den stiùireadh gu lèir falamh, agus mar sin tha na clasaichean stiùiridh 7, 5, 7, 5, fa leth. BPF_ALU64, agus tha 5 BPF_JMP. Airson an dà chlas, tha an cruth stiùiridh mar an ceudna (faic gu h-àrd) agus is urrainn dhuinn ar prògram ath-sgrìobhadh mar seo (aig an aon àm bidh sinn ag ath-sgrìobhadh na colbhan a tha air fhàgail ann an cruth daonna):

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

Obrachadh b clas ALU64 A bheil BPF_MOV. Bidh e a’ sònrachadh luach don chlàr cinn-uidhe. Ma tha am pìos air a shuidheachadh s (stòr), an uairsin thèid an luach a thoirt bhon chlàr stòr, agus mura h-eil e, mar a tha sa chùis againn, air a shuidheachadh, thèid an luach a thoirt bhon raon Imm. Mar sin anns a 'chiad agus an treas stiùireadh bidh sinn a' coileanadh an obrachaidh r0 = Imm. Nas fhaide, tha gnìomhachd clas 1 JMP BPF_JEQ (leum ma tha e co-ionann). Anns a 'chùis againn, bhon a' phìos S tha neoni, bidh e a’ dèanamh coimeas eadar luach a’ chlàr stòr leis an raon Imm. Ma tha na luachan aig an aon àm, bidh an gluasad a 'tachairt gu PC + Offcàite PC, mar as àbhaist, anns a bheil seòladh an ath stiùiridh. Mu dheireadh, tha JMP Clas 9 Operation BPF_EXIT. Bidh an stiùireadh seo a 'crìochnachadh a' phrògram, a 'tilleadh chun an kernel r0. Nach cuir sinn colbh ùr ris a’ bhòrd againn:

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

Faodaidh sinn seo ath-sgrìobhadh ann an cruth nas freagarraiche:

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

Ma chuimhnicheas sinn dè tha sa chlàr r1 tha am prògram air a thoirt seachad mar phuing don cho-theacsa bhon kernel, agus sa chlàr r0 tha an luach air a thilleadh dhan kernel, an uairsin chì sinn ma tha an comharra don cho-theacsa neoni, gun till sinn 1, agus air dhòigh eile - 2. Feuch an dèan sinn cinnteach gu bheil sinn ceart le bhith a’ coimhead air an stòr:

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

'S e, is e prògram gun bhrìgh a th' ann, ach tha e ag eadar-theangachadh gu dìreach ceithir stiùiridhean sìmplidh.

Eisimpleir sònraichte: stiùireadh 16-byte

Thug sinn iomradh na bu thràithe gu bheil cuid de stiùiridhean a’ gabhail barrachd air 64 pìosan. Tha seo a 'buntainn, mar eisimpleir, ri stiùireadh lddw (Còd = 0x18 = BPF_LD | BPF_DW | BPF_IMM) - luchdaich facal dùbailte bho na raointean a-steach don chlàr ImmS an Iar- Is e an fhìrinn sin Imm tha meud 32 ann, agus facal dùbailte 64 bit, agus mar sin chan obraich luach sa bhad 64-bit a-steach do chlàr ann an aon stiùireadh 64-bit. Gus seo a dhèanamh, thèid dà stiùireadh faisg air làimh a chleachdadh gus an dàrna pàirt den luach 64-bit a stòradh san raon Imm. Eisimpleir:

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

Chan eil ach dà stiùireadh ann am prògram binary:

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

Coinnichidh sinn a-rithist le stiùireadh lddw, nuair a bhios sinn a’ bruidhinn mu ghluasadan agus ag obair le mapaichean.

Eisimpleir: cuir às do BPF a’ cleachdadh innealan àbhaisteach

Mar sin, tha sinn air ionnsachadh còdan binary BPF a leughadh agus tha sinn deiseil airson stiùireadh sam bith a pharsadh ma tha sin riatanach. Ach, is fhiach a ràdh gu bheil e nas goireasaiche agus nas luaithe prògraman a thoirt air falbh le bhith a’ cleachdadh innealan àbhaisteach, mar eisimpleir:

$ 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

Cuairt-beatha nithean BPF, siostam faidhle bpffs

(Dh’ ionnsaich mi an toiseach cuid den fhiosrachadh a tha air a mhìneachadh san fho-earrann seo bho post Alexei Starovoytov saor an asgaidh Blog BPF.)

Bidh nithean BPF - prògraman agus mapaichean - air an cruthachadh bho àite luchd-cleachdaidh a’ cleachdadh òrdughan BPF_PROG_LOAD и BPF_MAP_CREATE call siostam bpf(2), bruidhnidh sinn mu dheidhinn dìreach mar a thachras seo anns an ath earrann. Bidh seo a 'cruthachadh structaran dàta kernel agus airson gach aon dhiubh refcount (cunntas iomraidh) air a shuidheachadh gu aon, agus tha tuairisgeul faidhle a’ comharrachadh an nì air a thilleadh chun neach-cleachdaidh. Às deidh an làmh a dhùnadh refcount tha an nì air a lughdachadh le aon, agus nuair a ruigeas e neoni, thèid an nì a sgrios.

Ma chleachdas am prògram mapaichean, an uairsin refcount tha na mapaichean sin air an àrdachadh le aon às deidh am prògram a luchdachadh, i.e. faodar na tuairisgeulan faidhle aca a dhùnadh bhon phròiseas neach-cleachdaidh agus fhathast refcount cha bhi e neoni :

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Às deidh dhuinn prògram a luchdachadh gu soirbheachail, mar as trice bidh sinn ga cheangal ri gineadair tachartais de sheòrsa air choreigin. Mar eisimpleir, is urrainn dhuinn a chuir air eadar-aghaidh lìonra gus pacaidean a thig a-steach a phròiseasadh no a cheangal ri cuid tracepoint anns a' chridhe. Aig an ìre seo, àrdaichidh a’ chunntair iomraidh le aon agus bidh e comasach dhuinn tuairisgeul an fhaidhle a dhùnadh sa phrògram luchdan.

Dè thachras ma dhùineas sinn a-nis am bootloader? Tha e an urra ris an t-seòrsa gineadair tachartais (dubhan). Bidh a h-uile dubhan lìonra ann às deidh don luchdan crìochnachadh, is iad sin na dubhan cruinne ris an canar. Agus, mar eisimpleir, thèid prògraman lorg a leigeil ma sgaoil às deidh don phròiseas a chruthaich iad a thighinn gu crìch (agus mar sin canar ionadail riutha, bho “ionadail chun phròiseas”). Gu teicnigeach, bidh tuairisgeul faidhle co-fhreagarrach aig dubhan ionadail an-còmhnaidh ann an àite luchd-cleachdaidh agus mar sin dùin iad nuair a bhios am pròiseas dùinte, ach chan eil dubhan cruinne ann. Anns an fhigear a leanas, a 'cleachdadh croisean dearga, bidh mi a' feuchainn ri sealltainn mar a tha crìoch a 'phrògraim luchdan a' toirt buaidh air beatha nithean ann an cùis dubhan ionadail agus cruinneil.

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Carson a tha eadar-dhealachadh eadar dubhan ionadail agus cruinne? Tha a bhith a 'ruith cuid de sheòrsachan de phrògraman lìonra a' dèanamh ciall às aonais àite luchd-cleachdaidh, mar eisimpleir, smaoinich air dìon DDoS - bidh an luchd-luidh a 'sgrìobhadh nan riaghailtean agus a' ceangal am prògram BPF ri eadar-aghaidh an lìonraidh, agus às dèidh sin faodaidh an luchd-luidh a dhol agus a mharbhadh fhèin. Air an làimh eile, smaoinich air prògram lorg debugging a sgrìobh thu air do ghlùinean ann an deich mionaidean - nuair a bhios e deiseil, bu mhath leat nach bi sgudal air fhàgail san t-siostam, agus nì dubhan ionadail cinnteach sin.

Air an làimh eile, smaoinich gu bheil thu airson ceangal ri tracepoint anns an kernel agus cruinneachadh staitistig thar grunn bhliadhnaichean. Anns a 'chùis seo, bhiodh tu airson am pàirt cleachdaiche a lìonadh agus tilleadh chun na staitistig bho àm gu àm. Bheir an siostam faidhle bpf an cothrom seo. Is e siostam faidhle meallta a tha ann an cuimhne a-mhàin a leigeas le faidhlichean a chruthachadh a bheir iomradh air nithean BPF agus mar sin àrdachadh refcount nithean. Às deidh seo, faodaidh an luchdan falbh, agus fuirichidh na stuthan a chruthaich e beò.

BPF airson an fheadhainn bheaga, pàirt a h-aon: BPF leudaichte

Canar “pinning” ri bhith a’ cruthachadh fhaidhlichean ann am bpffs a tha a’ toirt iomradh air nithean BPF (mar a tha san abairt a leanas: “faodaidh pròiseas prògram no mapa BPF a phronnadh”). Tha cruthachadh stuthan faidhle airson nithean BPF a’ dèanamh ciall chan ann a-mhàin airson a bhith a’ leudachadh beatha nithean ionadail, ach cuideachd airson cleachdadh stuthan cruinne - a’ dol air ais chun eisimpleir leis a’ phrògram dìon DDoS cruinneil, tha sinn airson a bhith comasach air tighinn a choimhead air staitistig. o àm gu àm.

Mar as trice bidh siostam faidhle BPF air a chuir a-steach /sys/fs/bpf, ach faodar a chuir suas gu h-ionadail cuideachd, mar eisimpleir, mar seo:

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

Tha ainmean siostam faidhle air an cruthachadh leis an àithne BPF_OBJ_PIN Call siostam BPF. Gus dealbh a dhèanamh, gabhamaid prògram, cuir ri chèile e, luchdaich suas e, agus cuir a-steach e bpffs. Chan eil am prògram againn a’ dèanamh dad feumail, chan eil sinn a’ taisbeanadh a’ chòd ach gus an urrainn dhut an eisimpleir ath-riochdachadh:

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

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

Nach cuir sinn ri chèile am prògram seo agus cruthaich sinn leth-bhreac ionadail den t-siostam faidhle bpffs:

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

A-nis leig dhuinn am prògram againn a luchdachadh sìos a ’cleachdadh a’ ghoireas bpftool agus coimhead air na gairmean siostam a tha na chois bpf(2) (cuid de loidhnichean neo-iomchaidh air an toirt a-mach à toradh strace):

$ sudo strace -e bpf bpftool prog load ./test.o bpf-mountpoint/test
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="test", ...}, 120) = 3
bpf(BPF_OBJ_PIN, {pathname="bpf-mountpoint/test", bpf_fd=3}, 120) = 0

An seo tha sinn air am prògram a luchdachadh le bhith a’ cleachdadh BPF_PROG_LOAD, fhuair e tuairisgeul faidhle bhon kernel 3 agus a 'cleachdadh an àithne BPF_OBJ_PIN pinned an tuairisgeul faidhle seo mar fhaidhle "bpf-mountpoint/test". Às dèidh seo am prògram bootloader bpftool deiseil ag obair, ach dh'fhuirich am prògram againn anns an kernel, ged nach do cheangail sinn e ri eadar-aghaidh lìonra sam bith:

$ 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

Is urrainn dhuinn an nì faidhle a sguabadh às gu h-àbhaisteach unlink(2) agus às deidh sin thèid am prògram co-fhreagarrach a dhubhadh às:

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

A' sguabadh às nithean

A ’bruidhinn mu bhith a’ cuir às do nithean, feumar a shoilleireachadh, às deidh dhuinn am prògram a dhì-cheangal bhon dubhan (gineadair tachartais), nach brosnaich aon tachartas ùr a chuir air bhog, ach, thèid a h-uile suidheachadh gnàthach den phrògram a chrìochnachadh san òrdugh àbhaisteach .

Tha cuid de sheòrsan de phrògraman BPF a’ toirt cothrom dhut am prògram a chuir an àite air an itealan, i.e. thoir seachad atomity sreath replace = detach old program, attach new program. Anns a 'chùis seo, cuiridh a h-uile suidheachadh gnìomhach den t-seann dreach den phrògram crìoch air an obair aca, agus thèid luchd-làimhseachaidh tachartais ùr a chruthachadh bhon phrògram ùr, agus tha "atomicity" an seo a' ciallachadh nach tèid aon tachartas a chall.

A’ ceangal phrògraman ri stòran tachartais

San artaigil seo, cha bhith sinn a’ toirt cunntas air leth air a bhith a ’ceangal phrògraman ri stòran tachartais, oir tha e ciallach seo a sgrùdadh ann an co-theacsa seòrsa sònraichte de phrògram. Cm. eisimpleir gu h-ìosal, anns a bheil sinn a 'sealltainn mar a tha prògraman mar XDP ceangailte.

A’ làimhseachadh nithean a’ cleachdadh gairm siostam bpf

Prògraman airson BPF

Tha a h-uile nì BPF air a chruthachadh agus air a riaghladh bho àite luchd-cleachdaidh a’ cleachdadh gairm siostaim bpf, aig a bheil na prototypes a leanas:

#include <linux/bpf.h>

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

Seo an sgioba cmd 's e aon de na luachan seòrsa enum bpf_cmd, attr - comharra air crìochan airson prògram sònraichte agus size - meud an nì a rèir a’ phuing, i.e. mar as trice seo sizeof(*attr). Ann an kernel 5.8 gairm an t-siostaim bpf a’ toirt taic do 34 òrdughan eadar-dhealaichte, agus определение union bpf_attr a’ gabhail thairis 200 loidhne. Ach cha bu chòir dhuinn a bhith fo eagal le seo, oir bidh sinn eòlach air na h-òrdughan agus na crìochan thairis air grunn artaigilean.

Feuch an tòisich sinn leis an sgioba BPF_PROG_LOAD, a bhios a’ cruthachadh phrògraman BPF - a’ gabhail seata de stiùiridhean BPF agus ga luchdachadh a-steach don kernel. Aig an àm a thèid a luchdachadh, thèid an dearbhaiche a chuir air bhog, agus an uairsin an inneal-cruinneachaidh JIT agus, às deidh a chuir gu bàs gu soirbheachail, thèid tuairisgeul faidhle a’ phrògraim a thilleadh chun neach-cleachdaidh. Chunnaic sinn an ath rud a thachras dha anns an earrainn mu dheireadh mu chuairt-beatha nithean BPF.

Sgrìobhaidh sinn a-nis prògram àbhaisteach a luchdaicheas prògram BPF sìmplidh, ach an toiseach feumaidh sinn co-dhùnadh dè an seòrsa prògram a tha sinn airson a luchdachadh - feumaidh sinn taghadh seòrsa de agus taobh a-staigh frèam den t-seòrsa seo, sgrìobh prògram a thèid seachad air an deuchainn dearbhaidh. Ach, gus nach bi am pròiseas iom-fhillte, seo fuasgladh deiseil: gabhaidh sinn prògram mar BPF_PROG_TYPE_XDP, a bheir air ais an luach XDP_PASS (sgioblaich a h-uile pasgan). Ann an assembler BPF tha e a’ coimhead gu math sìmplidh:

r0 = 2
exit

Às deidh dhuinn co-dhùnadh a dhèanamh a luchdaichidh sinn suas, is urrainn dhuinn innse dhut mar a nì sinn e:

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

Bidh tachartasan inntinneach ann am prògram a’ tòiseachadh leis a’ mhìneachadh air sreath insns - am prògram BPF againn ann an còd inneal. Anns a 'chùis seo, tha gach stiùireadh den phrògram BPF air a phacadh a-steach don structar bpf_insn. A’ chiad eileamaid insns a 'gèilleadh ris an stiùireadh r0 = 2, an dàrna - exit.

Tilleadh. Bidh an kernel a’ mìneachadh macros nas freagarraiche airson còdan inneal a sgrìobhadh, agus am faidhle cinn kernel a chleachdadh tools/include/linux/filter.h b’ urrainn dhuinn sgrìobhadh

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

Ach leis nach eil feum air a bhith a’ sgrìobhadh phrògraman BPF ann an còd dùthchasach ach airson deuchainnean a sgrìobhadh anns an kernel agus artaigilean mu BPF, chan eil dìth nam macros sin a’ dèanamh duilgheadas mòr do bheatha an leasaiche.

Às deidh dhuinn am prògram BPF a mhìneachadh, gluaisidh sinn air adhart gu bhith ga luchdachadh a-steach don kernel. An seata paramadairean as ìsle againn attr a’ toirt a-steach an seòrsa prògram, seata agus àireamh de stiùiridhean, cead riatanach, agus ainm "woo", a bhios sinn a 'cleachdadh gus am prògram againn a lorg air an t-siostam an dèidh a luchdachadh sìos. Tha am prògram, mar a chaidh a ghealltainn, air a luchdachadh a-steach don t-siostam a’ cleachdadh gairm siostam bpf.

Aig deireadh a’ phrògraim thig sinn gu crìch ann an lùb gun chrìoch a bhios coltach ris an uallach pàighidh. Às aonais, thèid am prògram a mharbhadh leis an kernel nuair a bhios an tuairisgeul faidhle a thill an gairm siostam thugainn dùinte bpf, agus chan fhaic sinn e san t-siostam.

Uill, tha sinn deiseil airson deuchainn. Nach cruinnich sinn agus ruith sinn am prògram fo stracegus dèanamh cinnteach gu bheil a h-uile càil ag obair mar a bu chòir:

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

Tha a h-uile dad gu math, bpf(2) thill sinn làmh 3 thugainn agus chaidh sinn a-steach do lùb neo-chrìochnach le pause(). Feuchaidh sinn ri ar prògram a lorg san t-siostam. Gus seo a dhèanamh thèid sinn gu ceann-uidhe eile agus cleachdaidh sinn an goireas 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)

Chì sinn gu bheil prògram luchdaichte air an t-siostam woo aig a bheil an ID cruinneil 390 agus a tha a’ dol air adhart an-dràsta simple-prog tha tuairisgeul faidhle fosgailte a’ comharrachadh a’ phrògram (agus ma tha simple-prog crìochnaichidh e an obair, ma ta woo falbhaidh). Mar a bhiodh dùil, am prògram woo a 'toirt 16 bytes - dà stiùireadh - de chòdan dà-chànanach ann an ailtireachd BPF, ach anns a' chruth dhùthchasach aige (x86_64) tha e mu thràth 40 bytes. Bheir sinn sùil air a’ phrògram againn anns a’ chruth thùsail aige:

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

gun iongnadh sam bith. A-nis leig dhuinn sùil a thoirt air a’ chòd a chruthaich an neach-cruinneachaidh 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

chan eil e gu math èifeachdach airson exit(2), ach ann an cothromachd, tha am prògram againn ro shìmplidh, agus airson prògraman nach eil cho beag, tha feum air a’ phròlogue agus an epilogue a chuir compiler JIT ris, gu dearbh.

Mapaichean

Faodaidh prògraman BPF raointean cuimhne structaraichte a chleachdadh a tha ruigsinneach an dà chuid do phrògraman BPF eile agus do phrògraman ann an àite luchd-cleachdaidh. Canar mapaichean ris na nithean sin agus anns an earrainn seo seallaidh sinn mar a làimhsicheas tu iad le bhith a’ cleachdadh siostam gairm bpf.

Canaidh sinn sa bhad nach eil comasan mhapaichean cuingealaichte a-mhàin ri ruigsinneachd air cuimhne co-roinnte. Tha mapaichean adhbhar sònraichte ann anns a bheil, mar eisimpleir, comharran gu prògraman BPF no comharran gu eadar-aghaidh lìonra, mapaichean airson a bhith ag obair le tachartasan perf, msaa. Cha bhith sinn a 'bruidhinn mun deidhinn an seo, gus nach cuir sinn dragh air an leughadair. A bharrachd air an seo, bidh sinn a’ seachnadh cùisean sioncronaidh, leis nach eil seo cudromach airson na h-eisimpleirean againn. Gheibhear liosta iomlan de na seòrsaichean mapa a tha rim faighinn ann an <linux/bpf.h>, agus anns an earrainn seo bheir sinn mar eisimpleir a’ chiad sheòrsa gu h-eachdraidheil, an clàr hash BPF_MAP_TYPE_HASH.

Ma chruthaicheas tu clàr hash a-steach, can, C ++, chanadh tu unordered_map<int,long> woo, a tha ann an Ruisis a’ ciallachadh “Tha feum agam air bòrd woo meud gun chrìoch, aig a bheil na h-iuchraichean de sheòrsa int, agus tha na luachan mar an seòrsa long" Gus clàr hash BPF a chruthachadh, feumaidh sinn an aon rud a dhèanamh, ach a-mhàin gum feum sinn am meud as motha den chlàr a shònrachadh, agus an àite a bhith a’ sònrachadh nan seòrsaichean iuchraichean is luachan, feumaidh sinn na meudan aca a shònrachadh ann am bytes. . Gus mapaichean a chruthachadh cleachd an àithne BPF_MAP_CREATE call siostam bpf. Bheir sinn sùil air prògram nas lugha no nas lugha a chruthaicheas mapa. Às deidh a ’phrògram roimhe a bhios a’ luchdachadh phrògraman BPF, bu chòir gum biodh am fear seo a ’coimhead sìmplidh dhut:

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

An seo tha sinn a 'mìneachadh seata de pharamadairean attr, anns a bheil sinn ag ràdh “Tha feum agam air clàr hash le iuchraichean agus luachan meud sizeof(int), anns an urrainn dhomh ceithir eileamaidean aig a’ char as àirde a chuir.” Nuair a chruthaicheas tu mapaichean BPF, faodaidh tu crìochan eile a shònrachadh, mar eisimpleir, san aon dòigh ris a ’phrògram, shònraich sinn ainm an nì mar "woo".

Nach cuir sinn ri chèile agus ruith sinn am prògram:

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

Seo an gairm siostam bpf(2) thug sinn àireamh mapa an tuairisgeul air ais thugainn 3 agus an uairsin bidh am prògram, mar a bhiodh dùil, a’ feitheamh ri tuilleadh stiùiridh ann an gairm an t-siostaim pause(2).

A-nis leig dhuinn ar prògram a chuir chun chùl no fosgladh inneal-crìochnachaidh eile agus coimhead air an nì againn a’ cleachdadh a’ ghoireas bpftool (faodaidh sinn eadar-dhealachadh a dhèanamh air a’ mhapa againn bho chàch leis an ainm):

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

Is e an àireamh 114 an ID cruinneil den nì againn. Faodaidh prògram sam bith air an t-siostam an ID seo a chleachdadh gus mapa gnàthaichte fhosgladh leis an àithne BPF_MAP_GET_FD_BY_ID call siostam bpf.

A-nis is urrainn dhuinn cluich leis a’ bhòrd hash againn. Bheir sinn sùil air na tha ann:

$ sudo bpftool map dump id 114
Found 0 elements

Falamh. Nach cuir sinn luach ann hash[1] = 1:

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

Bheir sinn sùil air a’ bhòrd a-rithist:

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

Hooray! Chaidh againn air aon eileamaid a chur ris. Thoir an aire gum feum sinn a bhith ag obair aig ìre byte airson seo a dhèanamh, bhon uair sin bptftool chan eil fios dè an seòrsa a th’ anns na luachan anns a’ chlàr hash. (Faodar an t-eòlas seo a ghluasad thuice a’ cleachdadh BTF, ach barrachd air sin a-nis.)

Dè dìreach a tha bpftool a’ leughadh agus a’ cur eileamaidean ris? Bheir sinn sùil fon chochall:

$ 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

An toiseach dh’ fhosgail sinn am mapa leis an ID cruinneil aige a’ cleachdadh an àithne BPF_MAP_GET_FD_BY_ID и bpf(2) thill sinn tuairisgeul 3. Nas fhaide air adhart leis an àithne BPF_MAP_GET_NEXT_KEY lorg sinn a’ chiad iuchair sa chlàr le bhith a’ dol seachad NULL mar chomharradh air an iuchair “roimhe”. Ma tha an iuchair againn is urrainn dhuinn a dhèanamh BPF_MAP_LOOKUP_ELEMa thilleas luach gu puing value. Is e an ath cheum gum feuchaidh sinn ris an ath eileamaid a lorg le bhith a’ dol seachad air puing dhan iuchair làithreach, ach chan eil anns a’ bhòrd againn ach aon eileamaid agus an àithne BPF_MAP_GET_NEXT_KEY a' tilleadh ENOENT.

Ceart gu leòr, atharraichidh sinn an luach le iuchair 1, canaidh sinn gu feum ar loidsig gnìomhachais clàradh 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

Mar a bhiodh dùil, tha e gu math sìmplidh: an àithne BPF_MAP_GET_FD_BY_ID a 'fosgladh ar mapa le ID, agus an àithne BPF_MAP_UPDATE_ELEM a’ sgrìobhadh thairis air an eileamaid.

Mar sin, às deidh dhuinn clàr hash a chruthachadh bho aon phrògram, is urrainn dhuinn na tha ann bho phrògram eile a leughadh agus a sgrìobhadh. Thoir an aire ma bha e comasach dhuinn seo a dhèanamh bhon loidhne-àithne, faodaidh prògram sam bith eile air an t-siostam a dhèanamh. A bharrachd air na h-òrdughan a tha air am mìneachadh gu h-àrd, airson obrachadh le mapaichean bho àite luchd-cleachdaidh, na leanas:

  • BPF_MAP_LOOKUP_ELEM: lorg luach le iuchair
  • BPF_MAP_UPDATE_ELEM: ùraich / cruthaich luach
  • BPF_MAP_DELETE_ELEM: thoir air falbh an iuchair
  • BPF_MAP_GET_NEXT_KEY: lorg an ath (no an toiseach) iuchair
  • BPF_MAP_GET_NEXT_ID: a’ leigeil leat a dhol tro na mapaichean a th’ ann mar-thà, sin mar a tha e ag obair bpftool map
  • BPF_MAP_GET_FD_BY_ID: fosgail mapa gnàthaichte leis an ID chruinneil aige
  • BPF_MAP_LOOKUP_AND_DELETE_ELEM: ùraich luach nì gu atamach agus thoir air ais an t-seann fhear
  • BPF_MAP_FREEZE: dèan am mapa so-ruigsinneach bho rùm luchd-cleachdaidh (cha ghabh an obrachadh seo a chuir dheth)
  • BPF_MAP_LOOKUP_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_UPDATE_BATCH, BPF_MAP_DELETE_BATCH: obraichean mòra. Mar eisimpleir, BPF_MAP_LOOKUP_AND_DELETE_BATCH - is e seo an aon dòigh earbsach air a h-uile luach bhon mhapa a leughadh agus ath-shuidheachadh

Chan eil na h-òrdughan sin uile ag obair airson a h-uile seòrsa mapa, ach san fharsaingeachd tha a bhith ag obair le seòrsachan eile de mhapaichean bho àite luchd-cleachdaidh a’ coimhead dìreach mar a bhith ag obair le clàran hash.

Air sgàth òrdugh, leig dhuinn crìoch a chuir air na deuchainnean clàr hash againn. Cuimhnich gun do chruthaich sinn clàr anns am bi suas ri ceithir iuchraichean? Nach cuir sinn beagan eileamaidean eile ris:

$ 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

Gu ruige seo cho math:

$ 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

Feuchaidh sinn ri fear eile a chur ris:

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

Mar a bhiodh dùil, cha do shoirbhich leinn. Bheir sinn sùil nas mionaidiche air a’ mhearachd:

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

Tha a h-uile dad gu math: mar a bhiodh dùil, an sgioba BPF_MAP_UPDATE_ELEM a’ feuchainn ri iuchair ùr, còigeamh, a chruthachadh, ach tuisleadh E2BIG.

Mar sin, is urrainn dhuinn prògraman BPF a chruthachadh agus a luchdachadh, a bharrachd air mapaichean a chruthachadh agus a riaghladh bho àite luchd-cleachdaidh. A-nis tha e reusanta coimhead air mar as urrainn dhuinn mapaichean bho na prògraman BPF fhèin a chleachdadh. Dh’ fhaodadh sinn bruidhinn mu dheidhinn seo ann an cànan phrògraman a tha doirbh a leughadh ann an còdan macro inneal, ach gu dearbh tha an t-àm ann sealltainn mar a tha prògraman BPF air an sgrìobhadh agus air an cumail suas - a’ cleachdadh libbpf.

(Do luchd-leughaidh a tha mì-riaraichte le dìth eisimpleir ìre ìosal: nì sinn mion-sgrùdadh mionaideach air prògraman a bhios a’ cleachdadh mhapaichean agus gnìomhan cuideachaidh a chaidh a chruthachadh a’ cleachdadh libbpf agus innse dhut dè thachras aig an ìre teagaisg. Airson luchd-leughaidh a tha mì-riaraichte gu mòr, chuir sinn ris eisimpleir san àite iomchaidh san artaigil.)

A 'sgrìobhadh phrògraman BPF a' cleachdadh libbpf

Faodaidh sgrìobhadh phrògraman BPF a’ cleachdadh còdan inneal a bhith inntinneach a-mhàin a’ chiad uair, agus an uairsin suidhich satiety a-steach. Aig an àm seo feumaidh tu d ’aire a thionndadh llvm, aig a bheil backend airson còd a chruthachadh airson ailtireachd BPF, a bharrachd air leabharlann libbpf, a leigeas leat taobh cleachdaiche tagraidhean BPF a sgrìobhadh agus còd phrògraman BPF a chaidh a chruthachadh a’ cleachdadh a luchdachadh llvm/clang.

Gu dearbh, mar a chì sinn san artaigil seo agus às deidh sin, libbpf a’ dèanamh tòrr obrach às aonais (no innealan coltach ris - iproute2, libbcc, libbpf-go, msaa) tha e eu-comasach a bhith beò. Aon de na feartan marbhadh sa phròiseact libbpf is BPF CO-RE (Compile Once, Run Everywhere) - pròiseact a leigeas leat prògraman BPF a sgrìobhadh a ghabhas gluasad bho aon kernel gu fear eile, le comas ruith air diofar APIan (mar eisimpleir, nuair a dh’ atharraicheas structar na h-eithne bhon dreach gu dreach). Gus a bhith comasach air obrachadh le CO-RE, feumaidh an kernel agad a bhith air a chur ri chèile le taic BTF (tha sinn a’ mìneachadh mar a nì thu seo san earrann Innealan Leasachaidh. Faodaidh tu dearbhadh a bheil an kernel agad air a thogail le BTF no nach eil gu math sìmplidh - le làthaireachd an fhaidhle a leanas:

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

Bidh am faidhle seo a’ stòradh fiosrachadh mu gach seòrsa dàta a thathar a’ cleachdadh san kernel agus air a chleachdadh anns na h-eisimpleirean againn uile a’ cleachdadh libbpf. Bruidhnidh sinn gu mionaideach mu CO-RE san ath artaigil, ach anns an fhear seo - dìreach tog thu fhèin kernel leis CONFIG_DEBUG_INFO_BTF.

leabharlainn libbpf a 'fuireach ceart anns an eòlaire tools/lib/bpf kernel agus a leasachadh air a dhèanamh tron ​​​​liosta puist [email protected]. Ach, tha stòr air leth air a chumail suas airson feumalachdan thagraidhean a tha a’ fuireach taobh a-muigh an kernel https://github.com/libbpf/libbpf anns a bheil an leabharlann kernel mar sgàthan airson ruigsinneachd leughaidh barrachd no nas lugha mar a tha.

Anns an earrainn seo bheir sinn sùil air mar as urrainn dhut pròiseact a chruthachadh a chleachdas libbpf, sgrìobhamaid grunn phrògraman deuchainn (barrachd no nas lugha gun bhrìgh) agus dèan sgrùdadh mionaideach air mar a tha e uile ag obair. Leigidh seo leinn mìneachadh nas fhasa anns na h-earrannan a leanas gu dìreach mar a bhios prògraman BPF ag eadar-obrachadh le mapaichean, luchd-cuideachaidh kernel, BTF, msaa.

Mar as trice pròiseactan a 'cleachdadh libbpf cuir stòr GitHub ris mar fho-mhodal git, nì sinn an aon rud:

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

A 'dol gu libbpf gu math sìmplidh:

$ 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

Tha an ath phlana againn san roinn seo mar a leanas: sgrìobhaidh sinn prògram BPF mar BPF_PROG_TYPE_XDP, an aon rud ris an eisimpleir roimhe, ach ann an C, bidh sinn ga chur ri chèile a’ cleachdadh clang, agus sgrìobh prògram cuideachaidh a luchdaicheas a-steach don kernel. Anns na h-earrannan a leanas leudaichidh sinn comasan an dà chuid am prògram BPF agus am prògram cuideachaidh.

Eisimpleir: a’ cruthachadh tagradh làn-chuimseach a’ cleachdadh libbpf

Airson tòiseachadh, bidh sinn a 'cleachdadh an fhaidhle /sys/kernel/btf/vmlinux, a chaidh ainmeachadh gu h-àrd, agus cruthaich a cho-ionann ann an cruth faidhle cinn:

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

Bidh am faidhle seo a’ stòradh a h-uile structar dàta a tha ri fhaighinn san kernel againn, mar eisimpleir, seo mar a tha an bann-cinn IPv4 air a mhìneachadh san kernel:

$ grep -A 12 'struct iphdr {' vmlinux.h
struct iphdr {
    __u8 ihl: 4;
    __u8 version: 4;
    __u8 tos;
    __be16 tot_len;
    __be16 id;
    __be16 frag_off;
    __u8 ttl;
    __u8 protocol;
    __sum16 check;
    __be32 saddr;
    __be32 daddr;
};

A-nis sgrìobhaidh sinn ar prògram BPF ann an 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";

Ged a thionndaidh am prògram againn gu math sìmplidh, feumaidh sinn fhathast aire a thoirt do dh’ iomadh mion-fhiosrachadh. An toiseach, is e a’ chiad fhaidhle cinn a tha sinn a’ toirt a-steach vmlinux.h, a chruthaich sinn dìreach a’ cleachdadh bpftool btf dump - a-nis chan fheum sinn am pasgan cinn kernel a chuir a-steach gus faighinn a-mach cò ris a tha na structaran kernel coltach. Tha am faidhle cinn a leanas a’ tighinn thugainn bhon leabharlann libbpf. A-nis chan fheum sinn ach am macro a mhìneachadh SEC, a chuireas an caractar chun na h-earrainn iomchaidh de fhaidhle nì ELF. Tha am prògram againn anns an earrainn xdp/simple, far am bi sinn a’ mìneachadh an seòrsa prògram BPF ron t-slais - is e seo an co-chruinneachadh a thathas a’ cleachdadh ann libbpf, stèidhichte air ainm na h-earrainn cuiridh e an àite an seòrsa ceart aig toiseach tòiseachaidh bpf(2). Tha am prògram BPF fhèin C - gu math sìmplidh agus air a dhèanamh suas de aon loidhne return XDP_PASS. Mu dheireadh, earrann air leth "license" tha ainm a’ cheadachais ann.

’S urrainn dhuinn ar prògram a chur ri chèile a’ cleachdadh llvm/clang, version>= 10.0.0, no nas fheàrr fhathast, nas motha (faic an earrann Innealan Leasachaidh):

$ 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

Am measg nam feartan inntinneach: tha sinn a 'comharrachadh an ailtireachd targaid -target bpf agus an t-slighe chum nan cinn libbpf, a chuir sinn a-steach o chionn ghoirid. Cuideachd, na dìochuimhnich mu dheidhinn -O2, às aonais an roghainn seo is dòcha gum bi iongnadh ort san àm ri teachd. Bheir sinn sùil air a’ chòd againn, an deach againn air am prògram a bha sinn ag iarraidh a sgrìobhadh?

$ 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

Seadh, dh'obraich e! A-nis, tha faidhle binary againn leis a’ phrògram, agus tha sinn airson tagradh a chruthachadh a luchdaicheas a-steach don kernel. Airson an adhbhair seo an leabharlann libbpf a’ tabhann dà roghainn dhuinn - cleachd API aig ìre nas ìsle no API aig ìre nas àirde. Thèid sinn an dàrna slighe, leis gu bheil sinn airson ionnsachadh mar a sgrìobhas tu, a luchdaicheas agus a cheanglas sinn prògraman BPF le glè bheag oidhirp airson an sgrùdadh às deidh sin.

An toiseach, feumaidh sinn an “cnàimhneach” den phrògram againn a ghineadh bhon dàna aige a’ cleachdadh an aon ghoireas bpftool - sgian na h-Eilbheis de shaoghal BPF (a ghabhas a thoirt gu litearra, leis gu bheil Daniel Borkman, aon de luchd-cruthachaidh agus luchd-gleidhidh BPF, às an Eilbheis):

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

Ann am faidhle xdp-simple.skel.h anns a bheil còd binary ar prògram agus gnìomhan airson a bhith a’ riaghladh - luchdachadh, ceangal, cuir às don nì againn. Anns a ’chùis shìmplidh againn tha seo coltach ri cus cus, ach bidh e cuideachd ag obair ma tha mòran de phrògraman agus mhapaichean BPF anns an fhaidhle nì agus gus an ELF mòr seo a luchdachadh feumaidh sinn dìreach an cnàimhneach a ghineadh agus gnìomh no dhà a ghairm bhon tagradh àbhaisteach a tha sinn a’ sgrìobhadh Rachamaid air adhart a-nis.

Gu fìrinneach, tha am prògram luchdan againn beag-chuid:

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

tha e struct xdp_simple_bpf air a mhìneachadh san fhaidhle xdp-simple.skel.h agus a’ toirt cunntas air ar faidhle nì:

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

Chì sinn comharran de API aig ìre ìosal an seo: an structar struct bpf_program *simple и struct bpf_link *simple. Tha a’ chiad structar a’ toirt cunntas sònraichte air a’ phrògram againn, sgrìobhte san earrann xdp/simple, agus tha an dàrna fear ag innse mar a tha am prògram a 'ceangal ri stòr an tachartais.

gnìomh xdp_simple_bpf__open_and_load, a’ fosgladh nì ELF, ga pharsadh, a’ cruthachadh a h-uile structar agus fo-structar (a bharrachd air a’ phrògram, tha earrannan eile ann an ELF cuideachd - dàta, dàta leughaidh a-mhàin, fiosrachadh deasbaid, cead, msaa), agus an uairsin ga luchdachadh a-steach don kernel a’ cleachdadh siostam glaodh bpf, as urrainn dhuinn sgrùdadh le bhith a’ cur ri chèile agus a’ ruith a’ phrògraim:

$ 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

Bheir sinn sùil a-nis air ar prògram a’ cleachdadh bpftool. Lorg sinn an ID aice:

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

agus dump (cleachdaidh sinn cruth nas giorra den àithne bpftool prog dump xlated):

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

Rudeigin ùr! Chlò-bhuail am prògram pìosan den fhaidhle stòr C againn. Chaidh seo a dhèanamh leis an leabharlann libbpf, a lorg an roinn deasbaid anns a’ bhinearaidh, chuir e ri chèile e ann an nì BTF, ga luchdachadh a-steach don kernel a’ cleachdadh BPF_BTF_LOAD, agus an uairsin shònraich an tuairisgeul faidhle a thàinig às nuair a bha thu a’ luchdachadh a’ phrògram leis an àithne BPG_PROG_LOAD.

Luchd-cuideachaidh Kernel

Faodaidh prògraman BPF gnìomhan “taobh a-muigh” a ruith - luchd-cuideachaidh kernel. Leigidh na gnìomhan cuideachaidh seo le prògraman BPF faighinn gu structaran kernel, riaghladh mhapaichean, agus cuideachd conaltradh leis an “fìor shaoghal” - cruthaich tachartasan perf, smachd a chumail air bathar-cruaidh (mar eisimpleir, pacaidean ath-stiùireadh), msaa.

Eisimpleir: bpf_get_smp_processor_id

Taobh a-staigh frèam a’ phàtran “ionnsachadh le eisimpleir”, beachdaichidh sinn air aon de na gnìomhan cuideachaidh, bpf_get_smp_processor_id(), cinnteach ann am faidhle kernel/bpf/helpers.c. Bidh e a’ tilleadh àireamh a’ phròiseasar air a bheil am prògram BPF ris an canadh e a’ ruith. Ach chan eil uiread de dh 'ùidh againn anns an t-semantics aige' s gu bheil a bhuileachadh a 'gabhail aon loidhne:

BPF_CALL_0(bpf_get_smp_processor_id)
{
    return smp_processor_id();
}

Tha na mìneachaidhean gnìomh neach-cuideachaidh BPF coltach ri mìneachaidhean gairm siostam Linux. An seo, mar eisimpleir, tha gnìomh air a mhìneachadh aig nach eil argamaidean. (Tha gnìomh a bheir, can, trì argamaidean air a mhìneachadh a’ cleachdadh am macro BPF_CALL_3. 'S e còig an àireamh as motha de argamaidean.) Ach, chan eil an seo ach a' chiad phàirt den mhìneachadh. Is e an dàrna pàirt structar an t-seòrsa a mhìneachadh struct bpf_func_proto, anns a bheil tuairisgeul air gnìomh an neach-cuideachaidh a thuigeas an dearbhaiche:

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

A’ clàradh ghnìomhan neach-cuideachaidh

Gus am faod prògraman BPF de sheòrsa sònraichte an gnìomh seo a chleachdadh, feumaidh iad a chlàradh, mar eisimpleir airson an t-seòrsa BPF_PROG_TYPE_XDP tha gnìomh air a mhìneachadh anns an kernel xdp_func_proto, a tha a’ dearbhadh bho ID gnìomh an neach-cuideachaidh a bheil XDP a’ toirt taic don ghnìomh seo no nach eil. Tha ar dleastanas a ’toirt taic:

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

Tha seòrsaichean prògram BPF ùra “air am mìneachadh” san fhaidhle include/linux/bpf_types.h a’ cleachdadh macro BPF_PROG_TYPE. Air a mhìneachadh ann an luachan oir is e mìneachadh loidsigeach a th’ ann, agus ann an teirmean cànain C tha mìneachadh seata iomlan de structaran cruadhtan a’ tachairt ann an àiteachan eile. Gu sònraichte, anns an fhaidhle kernel/bpf/verifier.c a h-uile mìneachadh bho fhaidhle bpf_types.h air an cleachdadh gus sreath de structaran a chruthachadh 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
};

Is e sin, airson gach seòrsa de phrògram BPF, tha comharradh air structar dàta den t-seòrsa air a mhìneachadh struct bpf_verifier_ops, a tha air a thòiseachadh leis an luach _name ## _verifier_ops, i.e., xdp_verifier_ops airson xdp. Structar xdp_verifier_ops air a dhearbhadh le ann am faidhle net/core/filter.c mar a leanas:

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

An seo chì sinn ar gnìomh eòlach xdp_func_proto, a ruitheas an neach-dearbhaidh a h-uile uair a thig e tarsainn air dùbhlan cuid gnìomhan taobh a-staigh prògram BPF, faic verifier.c.

Bheir sinn sùil air mar a bhios prògram beachd-bharail BPF a’ cleachdadh a’ ghnìomh bpf_get_smp_processor_id. Gus seo a dhèanamh, bidh sinn ag ath-sgrìobhadh a’ phrògram bhon earrainn a bh’ againn roimhe mar a leanas:

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

Samhla bpf_get_smp_processor_id air a dhearbhadh le в <bpf/bpf_helper_defs.h> leabharlannan libbpf ciamar

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

s e sin, bpf_get_smp_processor_id 'S e comharra-gnìomh aig a bheil luach 8, far a bheil 8 an luach BPF_FUNC_get_smp_processor_id seòrsa enum bpf_fun_id, a tha air a mhìneachadh dhuinn anns an fhaidhle vmlinux.h (faidhle bpf_helper_defs.h anns an kernel air a chruthachadh le sgriobt, agus mar sin tha na h-àireamhan “draoidheachd” ceart gu leòr). Chan eil an gnìomh seo a’ gabhail argamaidean sam bith agus a’ tilleadh luach de sheòrsa __u32. Nuair a bhios sinn ga ruith sa phrògram againn, clang a 'cruthachadh stiùireadh BPF_CALL "an seòrsa ceart" Nach cuir sinn ri chèile am prògram agus coimhead air an earrann 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

Anns a 'chiad loidhne chì sinn stiùireadh call, paramadair IMM a tha co-ionann ri 8, agus SRC_REG - neoni. A rèir an aonta ABI a chleachd an neach-dearbhaidh, is e seo gairm gu gnìomh neach-cuideachaidh àireamh a h-ochd. Aon uair ‘s gu bheil e air a chuir air bhog, tha an loidsig sìmplidh. Thoir air ais luach bhon chlàr r0 lethbhreac gu r1 agus air loidhnichean 2,3 tha e air a thionndadh gu seòrsa u32 - tha na 32 pìosan gu h-àrd air am fuadach. Air loidhnichean 4,5,6,7 bidh sinn a’ tilleadh 2 (XDP_PASS) no 1 (XDP_DROP) a rèir an do thill an gnìomh cuideachaidh bho loidhne 0 luach neoni no neo-neoni.

Feuch an dèan sinn deuchainn oirnn fhìn: luchdaich am prògram agus coimhead air an toradh 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

Ceart gu leòr, lorg an neach-dearbhaidh an neach-cuideachaidh kernel ceart.

Eisimpleir: dol seachad air argamaidean agus mu dheireadh ruith am prògram!

Tha prototype aig a h-uile gnìomh neach-cuideachaidh ìre ruith

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

Tha paramadairean gu gnìomhan cuideachaidh air an toirt seachad ann an clàran r1-r5, agus tha an luach air a thilleadh sa chlàr r0. Chan eil gnìomhan ann a ghabhas barrachd air còig argamaidean, agus chan eil dùil gun tèid taic a chuir riutha san àm ri teachd.

Bheir sinn sùil air an neach-cuideachaidh kernel ùr agus mar a bhios BPF a’ dol seachad air paramadairean. Dèanamaid ath-sgrìobhadh xdp-simple.bpf.c mar a leanas (chan eil an còrr de na loidhnichean air atharrachadh):

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

Bidh am prògram againn a’ clò-bhualadh àireamh an CPU air a bheil e a’ ruith. Nach cuir sinn ri chèile e agus coimhead air a’ chòd:

$ 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

Ann an loidhnichean 0-7 bidh sinn a 'sgrìobhadh an t-sreang running on CPU%un, agus an uairsin air loidhne 8 bidh sinn a 'ruith an tè eòlach bpf_get_smp_processor_id. Air loidhnichean 9-12 bidh sinn ag ullachadh argamaidean an neach-cuideachaidh bpf_printk - clàran r1, r2, r3. Carson a tha trì dhiubh ann agus chan e dhà? Air sgàth bpf_printkIs e pasgan macro a tha seo timcheall air an fhìor neach-cuideachaidh bpf_trace_printk, a dh'fheumas a dhol seachad air meud an t-sreang cruth.

Leig dhuinn a-nis loidhne no dhà a chur ris xdp-simple.cgus am bi am prògram againn a’ ceangal ris an eadar-aghaidh lo agus dha-rìribh thòisich!

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

An seo cleachdaidh sinn an gnìomh bpf_set_link_xdp_fd, a cheanglas prògraman BPF seòrsa XDP ri eadar-aghaidh lìonra. Chuir sinn còd cruaidh air àireamh an eadar-aghaidh lo, a tha an-còmhnaidh 1. Bidh sinn a 'ruith a' ghnìomh dà uair gus an seann phrògram a sgaradh an toiseach ma bha e ceangailte. Thoir an aire nach eil feum againn air dùbhlan a-nis pause no lùb gun chrìoch: falbhaidh am prògram luchdan againn, ach cha tèid am prògram BPF a mharbhadh leis gu bheil e ceangailte ri stòr an tachartais. Às deidh luchdachadh sìos agus ceangal soirbheachail, thèid am prògram a chuir air bhog airson gach pasgan lìonra a ruigeas lo.

Leig leinn am prògram a luchdachadh sìos agus coimhead air an eadar-aghaidh 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

Tha ID 669 aig a’ phrògram a luchdaich sinn sìos agus chì sinn an aon ID air an eadar-aghaidh lo. Cuiridh sinn pasgan no dhà gu 127.0.0.1 (iarrtas + freagairt):

$ ping -c1 localhost

agus a-nis leig dhuinn sùil a thoirt air susbaint an fhaidhle brìgheil deasbaid /sys/kernel/debug/tracing/trace_pipe, anns a bheil bpf_printk a’ sgrìobhadh a theachdaireachdan:

# 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

Chaidh dà phacaid fhaicinn air lo agus air a phròiseasadh air CPU0 - dh’ obraich a’ chiad phrògram BPF làn-chuimsichte againn!

Is fhiach a bhith mothachail air sin bpf_printk Chan ann airson dad a bhios e a’ sgrìobhadh chun fhaidhle deasbaid: chan e seo an neach-cuideachaidh as soirbheachail airson a chleachdadh ann an cinneasachadh, ach b’ e ar n-amas rudeigin sìmplidh a nochdadh.

Luchdaich a-nuas mapaichean bho BPF prògraman

Eisimpleir: a 'cleachdadh mapa bhon phrògram BPF

Anns na h-earrannan roimhe seo dh'ionnsaich sinn mar a chruthaicheas agus a chleachdas sinn mapaichean bho àite luchd-cleachdaidh, agus a-nis leig dhuinn sùil a thoirt air a 'phàirt kernel. Feuch an tòisich sinn, mar as àbhaist, le eisimpleir. Dèanamaid ath-sgrìobhadh air ar prògram xdp-simple.bpf.c mar a leanas:

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

Aig toiseach a’ phrògram chuir sinn mìneachadh mapa ris woo: Is e seo raon 8-eileamaid a bhios a’ stòradh luachan mar u64 (ann an C mhìnicheadh ​​​​sinn a leithid de raon mar u64 woo[8]). Ann am prògram "xdp/simple" gheibh sinn an àireamh pròiseasar gnàthach gu caochladair key agus an uairsin a’ cleachdadh gnìomh cuideachaidh bpf_map_lookup_element gheibh sinn comharradh don inntrigeadh fhreagarrach anns an raon, a mheudaicheas sinn aon. Eadar-theangachadh gu Ruisis: bidh sinn a’ tomhas staitistig air an robh CPU a’ giullachd phasganan a bha a’ tighinn a-steach. Feuchaidh sinn ris a’ phrògram a ruith:

$ 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

Feuch an dèan sinn cinnteach gu bheil i ceangailte ris lo agus cuiribh cuid de na pacaidean:

$ 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

A-nis leig dhuinn sùil a thoirt air susbaint an t-sreath:

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

Chaidh cha mhòr a h-uile pròiseas a phròiseasadh air CPU7. Chan eil seo cudromach dhuinn, is e am prìomh rud gu bheil am prògram ag obair agus tha sinn a’ tuigsinn mar a gheibh sinn cothrom air mapaichean bho phrògraman BPF - a’ cleachdadh хелперов bpf_mp_*.

Clàr-innse dìomhair

Mar sin, gheibh sinn cothrom air a’ mhapa bhon phrògram BPF a’ cleachdadh fiosan mar

val = bpf_map_lookup_elem(&woo, &key);

far a bheil coltas gnìomh an neach-cuideachaidh

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

ach tha sinn a' dol seachad air puing &woo gu structar gun ainm struct { ... }...

Ma choimheadas sinn air assembler a’ phrògraim, chì sinn gu bheil an luach &woo chan eil e air a mhìneachadh gu fìrinneach (loidhne 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
...

agus tha e air a ghabhail a-steach ann an gluasadan:

$ 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

Ach ma choimheadas sinn air a’ phrògram a tha air a luchdachadh mu thràth, chì sinn puing don mhapa cheart (loidhne 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]
...

Mar sin, faodaidh sinn a cho-dhùnadh, aig àm cur air bhog ar prògram luchdan, an ceangal gu &woo chaidh rudeigin a chuir na àite le leabharlann libbpf. An toiseach bheir sinn sùil air an toradh 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

Chì sinn sin libbpf chruthaich mapa woo agus an uairsin luchdaich sìos am prògram againn simple. Bheir sinn sùil nas mionaidiche air mar a luchdaicheas sinn am prògram:

  • glaodh xdp_simple_bpf__open_and_load bho fhaidhle xdp-simple.skel.h
  • a tha ag adhbhrachadh xdp_simple_bpf__load bho fhaidhle xdp-simple.skel.h
  • a tha ag adhbhrachadh bpf_object__load_skeleton bho fhaidhle libbpf/src/libbpf.c
  • a tha ag adhbhrachadh bpf_object__load_xattr bho libbpf/src/libbpf.c

Canaidh an gnìomh mu dheireadh, am measg rudan eile, ris bpf_object__create_maps, a bhios a’ cruthachadh no a’ fosgladh mhapaichean gnàthaichte, gan tionndadh gu tuairisgeulan fhaidhlichean. (Seo far am faic sinn BPF_MAP_CREATE anns an toradh strace.) An ath rud canar an gnìomh bpf_object__relocate agus is i a tha suim dhinn, oir tha cuimhne againn air na chunnaic sinn woo anns a 'chlàr ath-shuidheachadh. Le bhith ga sgrùdadh, bidh sinn mu dheireadh gar lorg fhèin sa ghnìomh bpf_program__relocate, a tha a’ dèiligeadh ri gluasad mapa:

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

Mar sin bidh sinn a 'gabhail ris an stiùireadh againn

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

agus cuir a-steach an clàr stòr a tha ann BPF_PSEUDO_MAP_FD, agus a’ chiad IMM gu tuairisgeul faidhle ar mapa agus, ma tha e co-ionann ri, mar eisimpleir, 0xdeadbeef, an uairsin mar thoradh air sin gheibh sinn an stiùireadh

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

Seo mar a thèid fiosrachadh mapa a ghluasad gu prògram BPF sònraichte luchdaichte. Anns a 'chùis seo, faodar am mapa a chruthachadh a' cleachdadh BPF_MAP_CREATE, agus air fhosgladh le ID a’ cleachdadh BPF_MAP_GET_FD_BY_ID.

Gu h-iomlan, nuair a bhios tu a 'cleachdadh libbpf Tha an algairim mar a leanas:

  • nuair a thathar gan cur ri chèile, tha clàran air an cruthachadh sa chlàr ath-shuidheachadh airson ceanglaichean gu mapaichean
  • libbpf a’ fosgladh leabhar nithean ELF, a’ lorg a h-uile mapa cleachdte agus a’ cruthachadh tuairisgeulan faidhle dhaibh
  • tha tuairisgeulan faidhle air an luchdachadh a-steach don kernel mar phàirt den stiùireadh LD64

Mar a shaoileadh tu, tha barrachd ri thighinn agus feumaidh sinn coimhead a-steach don chridhe. Gu fortanach, tha boillsgeadh againn - tha sinn air a’ bhrìgh a sgrìobhadh sìos BPF_PSEUDO_MAP_FD a-steach don chlàr stòr agus faodaidh sinn a thiodhlacadh, a bheir sinn gu naomh nan uile naomh - kernel/bpf/verifier.c, far a bheil gnìomh le ainm sònraichte a’ dol an àite tuairisgeul faidhle le seòladh structar seòrsa 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;

(gheibhear còd slàn Ceangal). Mar sin is urrainn dhuinn ar n-algorithm a leudachadh:

  • fhad ‘s a bhios e a’ luchdachadh a ’phrògraim, bidh an neach-dearbhaidh a’ sgrùdadh cleachdadh ceart a ’mhapa agus a’ sgrìobhadh seòladh an structair fhreagarrach struct bpf_map

Nuair a bhios tu a 'luchdachadh sìos an ELF binary a' cleachdadh libbpf Tha tòrr a bharrachd a’ dol air adhart, ach bruidhnidh sinn sin ann an artaigilean eile.

Luchdaich a-nuas prògraman agus mapaichean gun libbpf

Mar a chaidh a ghealltainn, seo eisimpleir airson leughadairean a tha airson faighinn a-mach mar a chruthaicheas agus a luchdaicheas iad prògram a chleachdas mapaichean, gun chuideachadh libbpf. Faodaidh seo a bhith feumail nuair a tha thu ag obair ann an àrainneachd far nach urrainn dhut eisimeileachd a thogail, no a h-uile rud a shàbhaladh, no a bhith a’ sgrìobhadh prògram mar ply, a chruthaicheas còd binary BPF air an itealan.

Gus a dhèanamh nas fhasa an loidsig a leantainn, bidh sinn ag ath-sgrìobhadh ar n-eisimpleir airson nan adhbharan sin xdp-simple. Gheibhear an còd coileanta agus beagan leudaichte den phrògram air a bheilear a’ beachdachadh san eisimpleir seo ann an seo gist.

Tha loidsig an tagraidh againn mar a leanas:

  • cruthaich mapa seòrsa BPF_MAP_TYPE_ARRAY a’ cleachdadh an àithne BPF_MAP_CREATE,
  • cruthaich prògram a chleachdas am mapa seo,
  • ceangail am prògram ris an eadar-aghaidh lo,

a tha air eadar-theangachadh mar dhuine

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

tha e map_create a’ cruthachadh mapa san aon dòigh ’s a rinn sinn sa chiad eisimpleir mun ghairm siostam bpf - “kernel, feuch an dèan thu mapa ùr dhomh ann an cruth sreath de 8 eileamaidean mar __u64 agus thoir dhomh tuairisgeul an fhaidhle":

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

Tha am prògram cuideachd furasta a luchdachadh:

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

Am pàirt duilich prog_load Is e am mìneachadh air a’ phrògram BPF againn mar raon de structaran struct bpf_insn insns[]. Ach leis gu bheil sinn a’ cleachdadh prògram a th’ againn ann an C, is urrainn dhuinn beagan a mhealladh:

$ 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

Gu h-iomlan, feumaidh sinn 14 stiùireadh a sgrìobhadh ann an cruth structaran mar struct bpf_insn (comhairle: gabh an dump bho shuas, leugh an earrann stiùiridh a-rithist, fosgail linux/bpf.h и linux/bpf_common.h agus feuchainn ri co-dhùnadh struct bpf_insn insns[] leat fhèin):

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

Eacarsaich dhaibhsan nach do sgrìobh seo iad fhèin - lorg map_fd.

Tha aon phàirt eile nach deach fhoillseachadh sa phrògram againn - xdp_attach. Gu mì-fhortanach, chan urrainn dha prògraman leithid XDP a bhith ceangailte le bhith a’ cleachdadh gairm siostam bpf. Bha na daoine a chruthaich BPF agus XDP bhon choimhearsnachd Linux air-loidhne, a tha a’ ciallachadh gun do chleachd iad am fear as eòlaiche orra (ach chan ann gu àbhaisteach daoine) eadar-aghaidh airson eadar-obrachadh leis an kernel: socaidean netlink, Faic cuideachd RFC 3549. Tha an dòigh as sìmplidh a chur an gnìomh xdp_attach a' dèanamh lethbhreac dhen chòd bho libbpf, eadhon, bhon fhaidhle netlink.c, ciod a rinn sinn, ga ghiorrachadh beagan :

Fàilte gu saoghal socaidean netlink

Fosgail seòrsa socaid 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;
}

Leugh sinn bhon t-socaid seo:

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

Mu dheireadh, seo an gnìomh againn a tha a’ fosgladh socaid agus a’ cur teachdaireachd shònraichte thuige anns a bheil tuairisgeul faidhle:

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

Mar sin, tha a h-uile dad deiseil airson deuchainn:

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

Feuch sinn a bheil am prògram againn air ceangal a dhèanamh ris 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

Nach cuir sinn pings agus coimhead air a’ mhapa:

$ 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, bidh a h-uile dad ag obair. Thoir an aire, leis an t-slighe, gu bheil am mapa againn a-rithist air a thaisbeanadh ann an cruth bytes. Tha seo air sgàth gu bheil, eu-coltach libbpf cha do luchdaich sinn fiosrachadh seòrsa (BTF). Ach bruidhnidh sinn mu dheidhinn seo an ath thuras.

Innealan Leasachaidh

Anns an earrainn seo, seallaidh sinn ris an inneal leasaiche BPF as ìsle.

San fharsaingeachd, chan fheum thu dad sònraichte gus prògraman BPF a leasachadh - bidh BPF a’ ruith air kernel sgaoilidh reusanta sam bith, agus tha prògraman air an togail a’ cleachdadh clang, a dh'fhaodar a thoirt seachad bhon phacaid. Ach, leis gu bheil BPF ga leasachadh, tha an kernel agus na h-innealan an-còmhnaidh ag atharrachadh, mura h-eil thu airson prògraman BPF a sgrìobhadh a’ cleachdadh dhòighean seann-fhasanta bho 2019, feumaidh tu a chuir ri chèile.

  • llvm/clang
  • pahole
  • a chridhe
  • bpftool

(Airson fiosrachadh, chaidh an earrann seo agus a h-uile eisimpleir san artaigil a ruith air Debian 10.)

llvm/clag

Tha BPF càirdeil le LLVM agus, ged a dh’ fhaodar prògraman airson BPF a chur ri chèile o chionn ghoirid a’ cleachdadh gcc, tha a h-uile leasachadh làithreach air a dhèanamh airson LLVM. Mar sin, an toiseach, togaidh sinn an dreach làithreach clang bho 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
... много времени спустя
$

A-nis is urrainn dhuinn dearbhadh an tàinig a h-uile càil còmhla gu ceart:

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

(Stiùireadh cruinneachaidh clang air a thoirt leam o bpf_devel_QA.)

Cha chuir sinn a-steach na prògraman a thog sinn, ach an àite sin cuiridh sinn iad riutha PATHmar eisimpleir:

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

(Faodar seo a chur ris .bashrc no gu faidhle air leth. Gu pearsanta, bidh mi a’ cur rudan mar seo ri ~/bin/activate-llvm.sh agus nuair a bhios feum air bidh mi ga dhèanamh . activate-llvm.sh.)

Pahole agus BTF

Goireasach pahole air a chleachdadh nuair a thathar a’ togail an kernel gus fiosrachadh deasbaid a chruthachadh ann an cruth BTF. Cha tèid sinn a-steach gu mion-fhiosrachadh san artaigil seo mu fhiosrachadh teicneòlas BTF, ach a-mhàin gu bheil e goireasach agus tha sinn airson a chleachdadh. Mar sin ma tha thu gu bhith a’ togail do kernel, tog an toiseach pahole (às aonais pahole cha bhith e comasach dhut an kernel a thogail leis an roghainn 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 airson feuchainn le BPF

Nuair a bhios mi a’ sgrùdadh chothroman BPF, tha mi airson mo chridhe fhìn a chruinneachadh. Chan eil seo, sa chumantas, riatanach, oir bidh e comasach dhut prògraman BPF a chuir ri chèile agus a luchdachadh air an kernel sgaoilidh, ge-tà, le bhith a’ faighinn an kernel agad fhèin leigidh sin leat na feartan BPF as ùire a chleachdadh, a nochdas nad chuairteachadh anns na mìosan as fheàrr. , no, mar a thachras le cuid de dh’ innealan deasbaid nach tèid am pacadh idir san àm ri teachd. Cuideachd, tha a chridhe fhèin ga fhàgail cudromach a bhith a’ feuchainn a’ chòd.

Gus kernel a thogail feumaidh tu, an toiseach, an kernel fhèin, agus san dàrna àite, faidhle rèiteachaidh kernel. Gus feuchainn le BPF is urrainn dhuinn an àbhaist a chleachdadh vanilla kernel no aon de na kernels leasachaidh. Gu h-eachdraidheil, bidh leasachadh BPF a’ tachairt taobh a-staigh coimhearsnachd lìonraidh Linux agus mar sin bidh a h-uile atharrachadh luath no mall a’ dol tro David Miller, neach-gleidhidh lìonraidh Linux. A rèir an nàdur - deasachaidhean no feartan ùra - bidh atharrachaidhean lìonra a’ tuiteam ann an aon de dhà chora - net no net-next. Tha atharrachaidhean airson BPF air an sgaoileadh san aon dòigh eadar bpf и bpf-next, a tha an uairsin air an cruinneachadh ann an lìon agus lìon-ath, fa leth. Airson tuilleadh fiosrachaidh, faic bpf_devel_QA и netdev-FAQ. Mar sin tagh kernel stèidhichte air do bhlas agus feumalachdan seasmhachd an t-siostam air a bheil thu a’ dèanamh deuchainn (*-next Is e kernels an fheadhainn as neo-sheasmhach den fheadhainn a tha air an liostadh).

Tha e taobh a-muigh farsaingeachd an artaigil seo a bhith a’ bruidhinn air mar a làimhsicheas tu faidhlichean rèiteachaidh kernel - thathas a’ gabhail ris gu bheil fios agad mar a nì thu seo mu thràth, no deiseil airson ionnsachadh air do shon fein. Ach, bu chòir an stiùireadh a leanas a bhith gu ìre mhòr no nas lugha gus siostam obrachaidh le comas BPF a thoirt dhut.

Luchdaich sìos aon de na kernels gu h-àrd:

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

Tog config kernel as ìsle ag obair:

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

Dèan comas air roghainnean BPF ann am faidhle .config de do roghainn fhèin (is dòcha CONFIG_BPF bidh e air a chomasachadh mu thràth leis gu bheil systemd ga chleachdadh). Seo liosta de roghainnean bhon kernel a chaidh a chleachdadh airson an artaigil seo:

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

An uairsin is urrainn dhuinn na modalan agus an kernel a chruinneachadh agus a chuir a-steach gu furasta (co-dhiù, faodaidh tu an kernel a chruinneachadh a ’cleachdadh an fheadhainn a tha air ùr-chruinneachadh clangle bhith a' cur CC=clang):

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

agus ath-thòisich leis an kernel ùr (bidh mi a’ cleachdadh airson seo kexec bhon phacaid 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

Is e an goireas as cumanta san artaigil an goireas bpftool, air a thoirt seachad mar phàirt den kernel Linux. Tha e air a sgrìobhadh agus air a chumail suas le luchd-leasachaidh BPF airson luchd-leasachaidh BPF agus faodar a chleachdadh gus gach seòrsa de stuth BPF a riaghladh - prògraman a luchdachadh, mapaichean a chruthachadh agus a dheasachadh, beatha eag-shiostam BPF a sgrùdadh, msaa. Gheibhear sgrìobhainnean ann an cruth còdan stòr airson duilleagan duine anns a' chridhe no, air a chur ri chèile cheana, air-loidhne.

Aig àm an sgrìobhaidh seo bpftool a’ tighinn deiseil a-mhàin airson RHEL, Fedora agus Ubuntu (faic, mar eisimpleir, an t-snàthainn seo, a tha ag innse sgeulachd neo-chrìochnaichte pacaidh bpftool ann an Debian). Ach ma tha thu air do kernel a thogail mu thràth, an uairsin tog bpftool cho furasta ri 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  ]

$

(Seo ${linux} - seo an eòlaire kernel agad.) Às deidh dhut na h-òrdughan seo a chuir an gnìomh bpftool thèid a chruinneachadh ann an eòlaire ${linux}/tools/bpf/bpftool agus faodar a chur ris an t-slighe (an toiseach don neach-cleachdaidh root) no dìreach dèan lethbhreac gu /usr/local/sbin.

Cruinnich bpftool tha e nas fheàrr an tè mu dheireadh a chleachdadh clang, cruinn mar a chaidh a mhìneachadh gu h-àrd, agus dèan cinnteach a bheil e air a chruinneachadh gu ceart - a’ cleachdadh, mar eisimpleir, an àithne

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

a sheallas dè na feartan BPF a tha air an comasachadh san kernel agad.

Air an t-slighe, faodar an àithne roimhe a ruith mar

# bpftool f p k

Tha seo air a dhèanamh le samhlachas leis na goireasan bhon phacaid iproute2, far am faod sinn, mar eisimpleir, a ràdh ip a s eth0 an àite ip addr show dev eth0.

co-dhùnadh

Leigidh BPF leat flea a bhròg gus gnìomhachd a’ chridhe a thomhas gu h-èifeachdach agus air-iteig atharrachadh. Thionndaidh an siostam gu bhith air leth soirbheachail, anns na traidiseanan as fheàrr de UNIX: thug inneal sìmplidh a leigeas leat (ath) phrògramadh an kernel cothrom do àireamh mhòr de dhaoine agus de bhuidhnean feuchainn. Agus, ged a tha na deuchainnean, a bharrachd air leasachadh bun-structair BPF fhèin, fada bho bhith deiseil, tha ABI seasmhach aig an t-siostam mu thràth a leigeas leat loidsig gnìomhachais earbsach, agus as cudromaiche, èifeachdach a thogail.

Bu mhath leam a thoirt fa-near, nam bheachd-sa, gu bheil an teicneòlas air fàs cho mòr-chòrdte oir, air an aon làimh, faodaidh e cluich (faodar ailtireachd inneal a thuigsinn barrachd no nas lugha ann an aon fheasgar), agus air an làimh eile, gus fuasgladh fhaighinn air duilgheadasan nach b 'urrainnear a rèiteachadh (gu h-àlainn) mus nochd e. Bidh an dà phàirt seo còmhla a’ toirt air daoine feuchainn agus bruadar, a tha a’ leantainn gu bhith a’ nochdadh barrachd is barrachd fhuasglaidhean ùr-ghnàthach.

Chan eil anns an artaigil seo, ged nach eil e gu sònraichte goirid, ach ro-ràdh do shaoghal BPF agus chan eil e a’ toirt cunntas air feartan “adhartach” agus pàirtean cudromach den ailtireachd. Tha am plana san àm ri teachd rudeigin mar seo: bidh an ath artaigil mar thar-shealladh air seòrsachan phrògraman BPF (tha 5.8 seòrsa de phrògraman a’ faighinn taic anns an kernel 30), an uairsin seallaidh sinn mu dheireadh air mar a sgrìobhas tu fìor thagraidhean BPF a’ cleachdadh prògraman lorg kernel mar eisimpleir, an uairsin tha an t-àm ann airson cùrsa nas doimhne air ailtireachd BPF, air a leantainn le eisimpleirean de lìonrachadh BPF agus tagraidhean tèarainteachd.

Artaigilean roimhe san t-sreath seo

  1. BPF airson an fheadhainn bheaga, pàirt neoni: BPF clasaigeach

Ceanglaichean

  1. Stiùireadh BPF agus XDP - sgrìobhainnean air BPF bho cilium, no nas mionaidiche bho Daniel Borkman, fear de luchd-cruthachaidh agus luchd-gleidhidh BPF. Is e seo aon de na ciad tuairisgeulan trom, a tha eadar-dhealaichte bhon fheadhainn eile leis gu bheil fios aig Daniel cò mu dheidhinn a tha e a’ sgrìobhadh agus nach eil mearachdan ann an sin. Gu sònraichte, tha an sgrìobhainn seo a 'toirt cunntas air mar a dh'obraicheas le prògraman BPF de na seòrsaichean XDP agus TC a' cleachdadh a 'ghoireis ainmeil ip bhon phacaid iproute2.

  2. Sgrìobhainnean/lìonradh/filter.txt - faidhle tùsail le sgrìobhainnean airson BPF clasaigeach agus an uairsin leudaichte. Deagh leughadh ma tha thu airson sgrùdadh a dhèanamh air cànan cruinneachaidh agus mion-fhiosrachadh teicnigeach ailtireil.

  3. Blog mu BPF bho facebook. Is ann ainneamh a thèid ùrachadh, ach gu h-iomchaidh, mar a sgrìobh Alexei Starovoitov (ùghdar eBPF) agus Andrii Nakryiko - (neach-gleidhidh) an sin libbpf).

  4. Dìomhaireachd bpftool. Snàthainn twitter èibhinn bho Quentin Monnet le eisimpleirean agus dìomhaireachdan mu bhith a’ cleachdadh bpftool.

  5. Dàibheadh ​​​​a-steach do BPF: liosta de stuthan leughaidh. Liosta mòr (agus fhathast air a chumail suas) de cheanglaichean gu sgrìobhainnean BPF bho Quentin Monnet.

Source: www.habr.com

Cuir beachd ann