Tany am-piandohana dia nisy teknolojia iray ary nantsoina hoe BPF. Nijery azy izahay , Testamenta Taloha, lahatsoratra ato amin'ity andiany ity. Tamin'ny taona 2013, noho ny ezaka nataon'i Alexei Starovoitov sy Daniel Borkman, dia novolavolaina ary nampidirina tao amin'ny fototra izy io Linux Dikan-teny nohatsaraina izay natao manokana ho an'ny milina maoderina 64-bit. Nantsoina vetivety hoe Internal BPF ity teknolojia vaovao ity, avy eo novaina anarana hoe Extended BPF, ary ankehitriny, taona maro aty aoriana, dia antsoin'ny rehetra hoe BPF fotsiny.
Amin'ny ankapobeny, ny BPF dia mamela ny kaody nomen'ny mpampiasa tsy ara-dalàna hiasa ao amin'ny habaka kernel. Linux Ary nahomby tokoa ilay maritrano vaovao ka nila lahatsoratra am-polony fanampiny isika mba hanazavana ireo fampiharana rehetra ao aminy. (Ny hany zavatra tsy vitan'ireo mpamorona, araka ny hitanao ao amin'ny tabilao fahombiazana etsy ambany, dia ny famoronana logo mendrika.)
Ity lahatsoratra ity dia manoritsoritra ny firafitry ny milina virtoaly BPF, ny fifandraisana amin'ny kernel ho an'ny fiaraha-miasa amin'ny BPF, ny fitaovana fampandrosoana, ary koa ny fijery fohy sy fohy momba ny fahaiza-manao efa misy, i.e. izay rehetra ilaintsika amin'ny ho avy amin'ny fandalinana lalindalina kokoa ny fampiharana azo ampiharina amin'ny BPF.
Famintinana ny lahatsoratra
Voalohany, hojerentsika amin'ny mason'ny vorona ny maritrano BPF ary hanoritra ireo singa fototra.
Efa manana hevitra momba ny maritrano amin'ny ankapobeny isika dia hamaritra ny firafitry ny milina virtoaly BPF.
Amin'ity fizarana ity dia hojerentsika akaiky ny tsingerin'ny fiainan'ny zavatra BPF - programa sy sarintany.
Miaraka amin'ny fahatakarana ny rafitra efa misy, dia hojerentsika amin'ny farany ny fomba hamoronana sy fanodikodinana zavatra avy amin'ny habaka mpampiasa amin'ny alàlan'ny antso rafitra manokana − bpf(2).
Mazava ho azy fa afaka manoratra programa amin'ny alàlan'ny antso an-tariby ianao. Sarotra anefa izany. Ho an'ny toe-javatra tena misy, dia nanamboatra tranomboky ny mpandrindra nokleary libbpf. Hamorona taolana fampiharana BPF fototra izay hampiasaintsika amin'ny ohatra manaraka.
Eto isika dia hianatra ny fomba ahafahan'ny programa BPF miditra amin'ny asan'ny mpanampy kernel - fitaovana iray izay, miaraka amin'ny sarintany, dia manitatra ifotony ny fahaizan'ny BPF vaovao raha oharina amin'ny mahazatra.
Amin'izao fotoana izao dia ho ampy ny mahafantatra tsara ny fomba hamoronana programa mampiasa sarintany. Ary andeha isika hijery haingana ny mpanamarina lehibe sy mahery.
Fizarana fanampiana amin'ny fomba fanangonana ireo fitaovana ilaina sy kernel ho an'ny fanandramana.
Any amin’ny faran’ilay lahatsoratra, dia hahita teny mandrisika sy fanazavana fohy momba izay hitranga ao amin’ny lahatsoratra manaraka ireo izay mamaky izany. Hitanisa rohy maromaro ihany koa izahay ho an'ny fianarana samirery ho an'ireo izay tsy manana faniriana na fahaizana miandry ny tohiny.
Fampidirana ny Architecture BPF
Alohan'ny hanombohantsika handinika ny maritrano BPF dia hiresaka farany (oh) isika , izay novolavolaina ho valin'ny fahatongavan'ireo milina RISC ary namaha ny olan'ny fanivanana fonosana mahomby. Nahomby tokoa ny maritrano hany ka teraka tamin'ny taona fahasivy ambin'ny folo tao Berkeley UNIX, nafindra tany amin'ny ankamaroan'ny rafitra fiasana efa misy izy io, tafavoaka tamin'ny faha-roapolo taonany ary mbola mahita fampiharana vaovao.
Ny BPF vaovao dia novolavolaina ho valin'ny ubiquity ny milina 64-bit, serivisy rahona ary ny fitomboan'ny filana fitaovana hamoronana SDN (Smatetika-defined nNovolavolain'ireo injeniera fototra amin'ny tambajotra ho solon'ny BPF mahazatra, ny BPF vaovao dia nahita fampiharana tamin'ny asa sarotra amin'ny fanaraha-maso enim-bolana monja taty aoriana. Linux rafitra, ary ankehitriny, enin-taona aorian'ny nisehoany, dia mila lahatsoratra vaovao tanteraka isika mba hitanisa ireo karazana fandaharana samihafa.
Sary mampihomehy
Amin'ny fotony, BPF dia milina virtoaly sandbox izay ahafahanao mampandeha kaody "tsy misy dikany" ao amin'ny habaka kernel nefa tsy mampandefitra ny fiarovana. Ny programa BPF dia noforonina ao amin'ny habaka mpampiasa, ampidirina ao amin'ny kernel, ary mifandray amin'ny loharano hetsika sasany. Ny hetsika iray dia mety ho, ohatra, ny fandefasana fonosana iray mankany amin'ny seha-pifandraisana, ny fandefasana ny fiasa kernel sasany, sns. Raha misy fonosana iray, ny programa BPF dia hanana fidirana amin'ny angona sy metadata amin'ny fonosana (ho an'ny famakiana sy, angamba, fanoratana, miankina amin'ny karazana programa amin'ny trangan'ny fampandehanana ny kernel, ny hevitra momba ny ny asa, anisan'izany ny tondro ho fahatsiarovana kernel, sns.
Andeha hojerentsika akaiky ity dingana ity. Hanombohana, andao hiresaka momba ny fahasamihafana voalohany amin'ny BPF mahazatra, ny programa izay nosoratana tamin'ny assembler. Ao amin'ny dikan-teny vaovao, ny maritrano dia nitarina mba hahafahan'ny programa hosoratana amin'ny fiteny avo lenta, indrindra indrindra, mazava ho azy, ao amin'ny C. Noho izany, dia novolavolaina ny backend ho an'ny llvm, izay ahafahanao mamorona bytecode ho an'ny architecture BPF.

Ny maritrano BPF dia natao, amin'ny ampahany, mba hampandeha tsara amin'ny milina maoderina. Mba hanaovana izany amin'ny fampiharana, ny BPF bytecode, rehefa tafiditra ao anaty kernel, dia adika amin'ny kaody teratany amin'ny fampiasana singa antsoina hoe JIT compiler (JUst In Time). Avy eo, raha tadidinao, ao amin'ny BPF mahazatra ny programa dia nampidirina tao amin'ny kernel ary napetaka tamin'ny loharano atomika - ao anatin'ny tontolon'ny antso rafitra tokana. Ao amin'ny maritrano vaovao, mitranga amin'ny dingana roa izany - voalohany, ampidirina ao anaty kernel ny code amin'ny alàlan'ny antso an-tariby bpf(2)ary avy eo, avy eo, amin'ny alàlan'ny rafitra hafa izay miovaova arakaraka ny karazana fandaharana, ny programa dia miraikitra amin'ny loharanon'ny hetsika.
Eto dia mety manana fanontaniana ny mpamaky: azo atao ve izany? Ahoana no hiantohana ny fiarovana ny famonoana ny fehezan-dalàna toy izany? Ny fiarovana amin'ny famonoana dia miantoka antsika amin'ny alàlan'ny dingan'ny famenoana ny programa BPF antsoina hoe verifier (amin'ny teny anglisy dia antsoina hoe verifier ity dingana ity ary mbola hampiasa ny teny anglisy aho):

Ny Verifier dia mpanadihady static izay miantoka fa tsy manelingelina ny fiasan'ny kernel ny programa iray. Izany, raha ny marina, dia tsy midika fa ny programa dia tsy afaka hanelingelina ny fampandehanana ny rafitra - BPF fandaharana, arakaraka ny karazana, dia afaka mamaky sy mamerina manoratra fizarana ny kernel fahatsiarovana, mamerina ny soatoavin'ny asa, trim, append, rewrite ary na dia ny fonosana tambajotra mandroso aza. Ny Verifier dia miantoka fa ny fampandehanana programa BPF dia tsy hanimba ny kernel ary ny programa izay, araka ny fitsipika, dia manana fidirana amin'ny fanoratana, ohatra, ny angon-drakitra momba ny fonosana iray mivoaka, dia tsy afaka mandika ny fahatsiarovana kernel ivelan'ny fonosana. Hojerentsika amin'ny antsipiriany bebe kokoa ao amin'ny fizarana mifanaraka amin'izany ny verifier, rehefa avy mahafantatra ny singa hafa rehetra amin'ny BPF isika.
Inona àry no nianarantsika hatreto? Manoratra programa amin'ny C ny mpampiasa, mampiditra azy ao anaty kernel amin'ny alàlan'ny antso an-tariby bpf(2), izay nohamarinin'ny mpanamarina ary nadika ho bytecode teratany. Avy eo ilay mpampiasa iray na iray hafa dia mampifandray ny programa amin'ny loharano hetsika ary manomboka manatanteraka izany. Ilaina ny fisarahana ny boot sy ny fifandraisana noho ny antony maro. Voalohany, lafo be ny fampandehanana mpanamarina ary amin'ny alàlan'ny fampidinana programa mitovy imbetsaka dia mandany fotoana amin'ny solosaina isika. Faharoa, ny fomba ifandraisan'ny programa iray dia miankina amin'ny karazana azy, ary ny interface "universal" iray novolavolaina herintaona lasa izay dia mety tsy mety amin'ny karazana programa vaovao. (Na dia mihamatotra kokoa aza ny maritrano amin'izao fotoana izao, misy ny hevitra hampiraisana an'io interface io amin'ny ambaratonga libbpf.)
Mety ho hitan'ny mpamaky tsara fa mbola tsy vita ny sary. Eny tokoa, ireo rehetra voalaza etsy ambony ireo dia tsy manazava ny antony hanovan'ny BPF ny sary raha oharina amin'ny BPF mahazatra. Fanavaozana roa izay manitatra be ny haavon'ny fampiharana dia ny fahafahana mampiasa ny fahatsiarovana iombonana sy ny asan'ny mpanampy kernel. Ao amin'ny BPF, ny fahatsiarovana zaraina dia ampiharina amin'ny alàlan'ny antsoina hoe sarintany - rafitra angon-drakitra ifampizarana miaraka amin'ny API manokana. Azo inoana fa nahazo an'io anarana io izy ireo satria ny karazana sari-tany voalohany niseho dia latabatra tenifototra. Avy eo dia nipoitra ny arrays, tabilao tenifototra eo an-toerana (per-CPU) sy array eo an-toerana, hazo fikarohana, sarintany misy tondro ho an'ny programa BPF sy ny maro hafa. Ny mahaliana anay ankehitriny dia ny programa BPF ankehitriny dia manana fahafahana mitazona fanjakana eo anelanelan'ny antso ary mizara izany amin'ny programa hafa sy amin'ny habaka mpampiasa.
Sarintany dia azo avy amin'ny fizotry ny mpampiasa amin'ny alàlan'ny antso an-tariby bpf(2), ary avy amin'ny programa BPF mandeha ao amin'ny kernel mampiasa asa mpanampy. Ankoatr'izay, misy mpanampy tsy miasa amin'ny sarintany fotsiny, fa koa miditra amin'ny fahaiza-manao kernel hafa. Ohatra, ny programa BPF dia afaka mampiasa asa mpanampy mba handefasana fonosana amin'ny interface hafa, hamorona hetsika perf, fidirana amin'ny rafitra kernel, sy ny sisa.

Raha fintinina, ny BPF dia manome fahafahana hampiditra tsy misy dikany, izany hoe, verifier-tested, kaody mpampiasa ao amin'ny habaka kernel. Ity kaody ity dia afaka mamonjy fanjakana eo anelanelan'ny antso sy ny fifanakalozana angona amin'ny habaka mpampiasa, ary afaka miditra amin'ny subsystems kernel avelan'ity karazana programa ity ihany koa.
Izany dia efa mitovy amin'ny fahaiza-manao omen'ny kernel modules, raha ampitahaina amin'ny BPF manana tombony sasany (mazava ho azy, azonao atao ny mampitaha ny fampiharana mitovy, ohatra, ny fanaraha-maso ny rafitra - tsy afaka manoratra mpamily tsy misy dikany amin'ny BPF ianao). Azonao atao ny manamarika ny tokonam-baravarana ambany kokoa (ny fitaovana sasany mampiasa BPF dia tsy mitaky ny mpampiasa hanana fahaiza-manao fandaharana kernel, na fahaiza-manao fandaharana amin'ny ankapobeny), fiarovana amin'ny fotoana fandehanana (atsangano ny tananao amin'ny fanehoan-kevitra ho an'ireo izay tsy nanapaka ny rafitra rehefa manoratra. na mody fitsapana), atomicity - misy fotoana tsy mitsaha-mitombo rehefa mamerina ny maody, ary ny subsystem BPF dia miantoka fa tsy misy zava-mitranga tsy hita (raha ny marina, tsy marina izany amin'ny karazana programa BPF rehetra).
Ny fisian'ny fahaiza-manao toy izany dia mahatonga ny BPF ho fitaovana manerantany amin'ny fanitarana ny kernel, izay voamarina amin'ny fampiharana: mihamaro ny karazana programa vaovao ampidirina amin'ny BPF, mihamaro ny orinasa lehibe mampiasa BPF amin'ny mpizara ady 24 × 7, mihamaro hatrany. Ny fanombohana dia manorina ny orinasany amin'ny vahaolana mifototra amin'ny BPF. Ny BPF dia ampiasaina na aiza na aiza: amin'ny fiarovana amin'ny fanafihana DDoS, mamorona SDN (ohatra, fametrahana tambajotra ho an'ny kubernetes), ho toy ny fitaovana fanaraha-maso sy mpanangona antontan'isa fototra, amin'ny rafitra fitsikilovana sy rafitra sandbox, sns.
Andao hamarana ny ampahany amin'ny topimaso amin'ny lahatsoratra eto ary hijery amin'ny antsipiriany bebe kokoa ny milina virtoaly sy ny tontolo iainana BPF.
Digression: utility
Mba hahafahana mampandeha ireo ohatra amin'ireto fizarana manaraka ireto dia mety mila fitaovana maromaro ianao, farafaharatsiny llvm/clang miaraka amin'ny fanohanana bpf ary bpftool. Ao amin'ny fizarana Azonao atao ny mamaky ny torolalana amin'ny fanangonana ny fitaovana, ary koa ny kernel anao. Apetraka eto ambany ity fizarana ity mba tsy hanelingelina ny firindran’ny famelabelarana ataontsika.
BPF Virtual Machine Registers and Instruction System
Ny rafitra rafitra sy ny baikon'ny BPF dia novolavolaina tamin'ny fijerena ny zava-misy fa ny programa dia hosoratana amin'ny C ary, aorian'ny fampidirana ao anaty kernel, dia adika amin'ny kaody teratany. Noho izany, ny isan'ny rejisitra sy ny andian-baiko dia nofidina miaraka amin'ny maso amin'ny fihaonan-dàlana, amin'ny heviny matematika, ny fahaizan'ny milina maoderina. Fanampin'izany, nisy fameperana isan-karazany napetraka tamin'ny programa, ohatra, hatramin'ny vao haingana dia tsy azo atao ny manoratra loops sy subroutines, ary ny isan'ny torolàlana dia voafetra ho 4096 (ankehitriny ny programa manana tombontsoa dia afaka mitondra hatramin'ny toromarika iray tapitrisa).
Ny BPF dia manana rejisitra 64-bit iraika ambin'ny folo azon'ny mpampiasa idirana r0-r10 ary counter program. hisoratra anarana r10 misy tondro-rafitra ary vakiana fotsiny. Ny programa dia afaka miditra amin'ny stack 512-byte amin'ny fotoana fampandehanana ary fahatsiarovana zaraina tsy misy fetra amin'ny endrika sarintany.
Ny fandaharan'asa BPF dia avela hitantana andiana mpanampy kernel karazana programa ary, vao haingana, fiasa mahazatra. Ny asa antsoina tsirairay dia afaka mandray tohan-kevitra dimy, alefa amin'ny rejisitra r1-r5, ary ny sanda miverina dia alefa any r0. Azo antoka fa aorian'ny fiverenana avy amin'ny asa, ny votoatin'ny rejisitra r6-r9 Tsy hiova.
Ho an'ny fandikana programa mahomby dia misoratra anarana r0-r11 satria ny maritrano tohanana rehetra dia voasokajy manokana amin'ny rejisitra tena izy, raha jerena ny endri-javatra ABI amin'ny maritrano ankehitriny. Ohatra, ho an'ny x86_64 firaketana r1-r5, ampiasaina handefasana masontsivana fiasa, dia aseho amin'ny rdi, rsi, rdx, rcx, r8, izay ampiasaina handefasana masontsivana amin'ny fiasa x86_64. Ohatra, ny kaody eo ankavia dia mandika ny kaody eo ankavanana toy izao:
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 0x0000000000001ee8hisoratra anarana r0 ampiasaina ihany koa hamerenana ny vokatry ny fanatanterahana ny programa, ary ao amin'ny rejisitra r1 Ny programa dia alefa tondro mankany amin'ny teny manodidina - miankina amin'ny karazana programa, mety ho, ohatra, rafitra izany (ho an'ny XDP) na rafitra (ho an'ny programa tambajotra samihafa) na rafitra (ho an'ny karazana programa fanaraha-maso samihafa), sns.
Noho izany, nanana rejisitra maromaro izahay, mpanampy kernel, stack, pointer contexte ary fahatsiarovana iombonana amin'ny endrika sarintany. Tsy hoe ilaina tanteraka amin’ny dia izany rehetra izany, fa...
Andeha isika hanohy ny famaritana ary hiresaka momba ny rafitra baiko hiasa amin'ireo zavatra ireo. rehetra () Ny torolàlana BPF dia manana habe 64-bit raikitra. Raha mijery torolàlana iray amin'ny milina Big Endian 64-bit ianao dia ho hitanao
![]()
izany Code - ity no fandiovana ny fampianarana, Dst/Src dia ny encoding ny mpandray sy ny loharano, tsirairay avy, Off - 16-bit sonia indentation, ary Imm dia integer voasonia 32-bit ampiasaina amin'ny torolàlana sasany (mitovy amin'ny cBPF constant K). Encoding Code manana iray amin'ireo karazany roa:

Ny kilasy fampianarana 0, 1, 2, 3 dia mamaritra ny baiko hiasa amin'ny fitadidiana. izy ireo , BPF_LD, BPF_LDX, BPF_ST, BPF_STX, tsirairay avy. Kilasy 4, 7 (BPF_ALU, BPF_ALU64) dia ahitana toromarika ALU. Kilasy 5, 6 (BPF_JMP, BPF_JMP32) misy toromarika hitsambikina.
Ny drafitra fanampiny amin'ny fandalinana ny rafitra fampianarana BPF dia toy izao manaraka izao: fa tsy mitanisa amim-pitandremana ny torolàlana rehetra sy ny masontsivanany, dia hijery ohatra roa amin'ity fizarana ity isika ary avy amin'izy ireo dia hazava ny fomba fiasan'ny torolàlana sy ny fomba esory amin'ny tanana ny rakitra binary ho an'ny BPF. Mba hanamafisana ny fitaovana any aoriana ao amin'ny lahatsoratra, dia hihaona amin'ny torolalana manokana ao amin'ny fizarana momba ny Verifier, JIT compiler, fandikana ny BPF mahazatra, ary koa rehefa mianatra sarintany, fiantsoana, sns.
Rehefa miresaka momba ny toromarika tsirairay isika dia hiresaka momba ny rakitra fototra и , izay mamaritra ny kaody nomerika amin'ny torolàlana BPF. Rehefa mandalina ny maritrano amin'ny tenanao manokana sy/na manao parsing binary ianao dia afaka mahita semantika amin'ireto loharano manaraka ireto, alahatra araka ny filaharany: , , ary, mazava ho azy, ao amin'ny kaody loharano Linux — mpanamarina, JIT, mpandika teny BPF.
Ohatra: famongorana ny BPF amin'ny lohanao
Andeha hojerentsika ny ohatra iray amin'ny fanangonana programa iray readelf-example.c ary jereo ny binary vokatr'izany. Haneho ny votoatiny tany am-boalohany izahay readelf-example.c eto ambany, aorian'ny famerenana ny lojikany amin'ny kaody 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 ................Andry voalohany amin'ny vokatra readelf dia indentation ary ny programanay dia misy baiko efatra:
Code Dst Src Off Imm
b7 0 0 0000 01000000
15 0 1 0100 00000000
b7 0 0 0000 02000000
95 0 0 0000 00000000Mitovy ny kaody baiko b7, 15, b7 и 95. Tsarovy fa ny telo bitika kely indrindra dia ny kilasy fampianarana. Amin'ny tranga misy antsika, ny bit fahefatra amin'ny torolàlana rehetra dia foana, ka ny kilasy fampianarana dia 7, 5, 7, 5, tsirairay avy BPF_ALU64ary 5, BPF_JMP. Ho an'ireo kilasy roa ireo dia mitovy ny endrika fampianarana (jereo etsy ambony) ary azontsika atao ny mamerina manoratra ny fandaharantsika tahaka izao (miaraka izany dia hamerina hanoratra ireo tsanganana sisa amin'ny endrik'olombelona isika):
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 0hetsika b kilasy ALU64 - ity dia . Izy io dia manome sanda ho an'ny rejisitra toerana haleha. Raha napetraka ny bit s (loharano), dia alaina avy amin'ny rejisitra loharano ny sandany, ary raha toa ka tsy napetraka izany, dia alaina avy amin'ny saha ny sandany. Imm. Koa amin'ny torolàlana voalohany sy fahatelo dia manao ny fandidiana izahay r0 = Imm. Fanampin'izany, ny fandidiana JMP kilasy 1 dia (mitsambikina raha mitovy). Amin'ny tranga misy antsika, hatramin'ny kely S dia aotra, mampitaha ny sandan'ny rejisitra loharano amin'ny saha Imm. Raha mifanandrify ny soatoavina, dia mitranga ny fifindrana PC + Offizay PC, toy ny mahazatra, dia ahitana ny adiresin'ny fampianarana manaraka. Farany, ny JMP Class 9 Operation dia . Ity torolàlana ity dia mamarana ny programa, miverina amin'ny kernel r0. Andao hanampy tsanganana vaovao amin'ny latabatray:
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 exitAfaka manoratra izany amin'ny endrika mety kokoa isika:
r0 = 1
if (r1 == 0) goto END
r0 = 2
END:
exitRaha tadidintsika izay ao anaty rejisitra r1 Ny programa dia alefa tondro mankany amin'ny contexte avy amin'ny kernel, ary ao amin'ny rejisitra r0 ny sanda dia averina amin'ny kernel, dia ho hitantsika fa raha zero ny pointer amin'ny contexte, dia miverina 1 isika, ary raha tsy izany - 2. Andeha hojerentsika fa marina isika amin'ny fijerena ny loharano:
$ cat readelf-example.c
int foo(void *ctx)
{
return ctx ? 2 : 1;
}Eny, programa tsy misy dikany izy io, saingy adika amin'ny torolalana tsotra efatra fotsiny.
Ohatra miavaka: fampianarana 16-byte
Nolazainay teo aloha fa ny torolàlana sasany dia maka mihoatra ny 64 bits. Izany dia mihatra, ohatra, ny toromarika lddw (Code = 0x18 = | | ) — asio teny roa avy amin'ny saha ho ao amin'ny rejisitra Imm. Ny zava-misy izany Imm manana haben'ny 32, ary ny teny roa dia 64 bit, noho izany dia tsy mandeha ny fampidirana sanda 64-bit avy hatrany ao anaty rejisitra amin'ny fampianarana 64-bit iray. Mba hanaovana izany, toromarika roa mifanakaiky no ampiasaina hitahiry ny ampahany faharoa amin'ny sanda 64-bit ao amin'ny saha. Imm... ohatra:
$ 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 ........Roa ihany no torolalana amin'ny programa binary:
Binary Disassm
18000000 ddccbbaa 00000000 44332211 r0 = Imm[0]|Imm[1]
95000000 00000000 exitHihaona amin’ny toromarika indray isika lddw, rehefa miresaka momba ny fifindra-monina sy miasa amin'ny sarintany.
Ohatra: famongorana ny BPF mampiasa fitaovana mahazatra
Noho izany, nianatra namaky ny kaody binary BPF izahay ary vonona ny hamantatra ny torolàlana rehetra raha ilaina. Na izany aza, ilaina ny milaza fa amin'ny fampiharana dia mora kokoa sy haingana kokoa ny manafoana ny programa amin'ny fampiasana fitaovana mahazatra, ohatra:
$ 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 exitNy fiainan'ny zavatra BPF, rafitra rakitra bpffs
(Nianatra ny sasany tamin'ireo antsipiriany voalaza ato amin'ity fizarana ity aho tamin'ny Alexei Starovoitov in .)
Ny zavatra BPF - programa sy sarintany - dia noforonina avy amin'ny habaka mpampiasa mampiasa baiko BPF_PROG_LOAD и BPF_MAP_CREATE antso rafitra bpf(2), hiresaka momba ny fomba marina hitrangan'izany isika ao amin'ny fizarana manaraka. Izany dia mamorona rafitra data kernel sy ho an'ny tsirairay amin'izy ireo refcount (fanisana reference) dia apetraka amin'ny iray, ary averina amin'ny mpampiasa ny mpamoritra rakitra manondro ilay zavatra. Rehefa mikatona ny tahony refcount ny zavatra dia mihena iray, ary rehefa tonga aotra dia potika ilay zavatra.
Raha mampiasa sarintany ny programa dia refcount ireo sarintany ireo dia ampitomboina iray aorian'ny fametrahana ny programa, i.e. Ny famariparitana ny rakitra dia azo akatona amin'ny fizotran'ny mpampiasa ary mbola refcount tsy ho aotra:

Rehefa vita ny fametrahana programa iray, dia matetika no ampifandraisinay amin'ny karazana mpamorona hetsika izany. Ohatra, azontsika atao ny mametraka azy eo amin'ny seha-pifandraisana iray mba hikarakarana ny fonosana miditra na hampifandray azy amin'ny sasany tracepoint amin'ny fotony. Amin'izao fotoana izao dia hitombo iray ihany koa ny kaontera fanondro ary ho afaka hanidy ny mpamoritra rakitra ao amin'ny programa loader isika.
Inona no mitranga raha manakatona ny bootloader isika izao? Izany dia miankina amin'ny karazana hetsika mpamokatra (hook). Hisy daholo ny hooks amin'ny tambajotra rehefa vita ny loader, ireo no antsoina hoe hooks manerantany. Ary, ohatra, ny programa trace dia havoaka rehefa tapitra ny dingana namoronana azy ireo (ary noho izany dia antsoina hoe eo an-toerana, manomboka amin'ny "local mankany amin'ny dingana"). Ara-teknika, ny hooks eo an-toerana dia manana famaritana rakitra mifanaraka amin'ny habaka mpampiasa ary noho izany dia mikatona rehefa mikatona ny dingana, saingy tsy misy ny hooks manerantany. Amin'ity sary manaraka ity, amin'ny fampiasana lakroa mena, dia manandrana mampiseho ny fiantraikan'ny fampitsaharana ny programa loader amin'ny androm-piainan'ny zavatra aho amin'ny tranga eo an-toerana sy manerantany.

Nahoana no misy fahasamihafana eo amin'ny hook eo an-toerana sy manerantany? Ny fampandehanana karazana programa tambajotra sasany dia misy dikany raha tsy misy mpampiasa, ohatra, alaivo sary an-tsaina ny fiarovana DDoS - manoratra ny fitsipika ny bootloader ary mampifandray ny programa BPF amin'ny interface interface, ary avy eo dia afaka mandeha ny bootloader ary mamono tena. Etsy ankilany, alaivo sary an-tsaina ny programa trace debug izay nosoratanao teo amin'ny lohalinao tao anatin'ny folo minitra - rehefa vita izany dia tianao ny tsy hisy fako tavela ao amin'ny rafitra, ary hiantoka izany ny hooks eo an-toerana.
Etsy ankilany, alaivo sary an-tsaina hoe te hifandray amin'ny tracepoint amin'ny kernel ianao ary hanangona antontan'isa mandritra ny taona maro. Amin'ity tranga ity, tianao ny hameno ny ampahany amin'ny mpampiasa ary hiverina amin'ny antontan'isa tsindraindray. Ny rafitra rakitra bpf dia manome izany fahafahana izany. Izy io dia rafitra pseudo-file ao anaty fitadidiana irery izay mamela ny famoronana rakitra izay manondro ny zavatra BPF ary mampitombo izany. refcount zavatra. Aorian'izany dia afaka mivoaka ny loader, ary mbola ho velona ireo zavatra noforoniny.

Ny famoronana rakitra ao amin'ny bpffs izay manondro ny zavatra BPF dia antsoina hoe "pinning" (toy ny amin'ity andian-teny manaraka ity: "afaka manindrona programa na sarintany BPF ny dingana"). Ny famoronana zavatra rakitra ho an'ny zavatra BPF dia misy dikany tsy amin'ny fanitarana ny fiainan'ny zavatra eo an-toerana ihany, fa koa amin'ny fampiasana ny zavatra manerantany - miverina amin'ny ohatra miaraka amin'ny programa fiarovana DDoS eran-tany, tianay ny ho tonga hijery ny antontan'isa. indraindray.
Ny rafitra rakitra BPF dia matetika napetraka ao /sys/fs/bpf, fa azo apetraka eo an-toerana ihany koa, ohatra, toy izao:
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpointNy anaran'ny rafitra fisie dia noforonina amin'ny alalan'ny baiko BPF_OBJ_PIN BPF rafitra antso. Mba hanazavana izany, andeha isika haka programa iray, manangona azy, mampakatra azy ary apetaho amin'ny bpffs. Ny programanay dia tsy manao na inona na inona mahasoa, ny kaody ihany no atolotray mba hahafahanao mamerina ilay ohatra:
$ cat test.c
__attribute__((section("xdp"), used))
int test(void *ctx)
{
return 0;
}
char _license[] __attribute__((section("license"), used)) = "GPL";Andao hanangona ity programa ity ary hamorona dika mitovy eo an-toerana amin'ny rafi-drakitra bpffs:
$ clang -target bpf -c test.c -o test.o
$ mkdir bpf-mountpoint
$ sudo mount -t bpf none bpf-mountpointAndeha isika haka ny programa amin'ny fampiasana ny utility bpftool ary jereo ny antson'ny rafitra miaraka aminy bpf(2) (tsipika tsy misy ifandraisany sasany nesorina tamin'ny famoahana 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) = 0Eto izahay dia nametraka ny programa mampiasa BPF_PROG_LOAD, nahazo famaritana rakitra avy amin'ny kernel 3 ary mampiasa ny baiko BPF_OBJ_PIN napetaka ho toy ny rakitra ity mpamoritra rakitra ity "bpf-mountpoint/test". Aorian'io, ny programa bootloader bpftool vita, fa ny programanay dia nijanona tao amin'ny kernel, na dia tsy nampifandraisinay tamin'ny fifandraisana amin'ny tambajotra aza izany:
$ 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 4096BAfaka mamafa ny zavatra rakitra ara-dalàna isika unlink(2) ary aorian'izay dia hofafana ny programa mifandraika amin'izany:
$ sudo rm ./bpf-mountpoint/test
$ sudo bpftool prog show id 783
Error: get by id (783): No such file or directoryFamafana zavatra
Raha miresaka momba ny famafana zavatra, dia ilaina ny manazava fa rehefa tapaka ny fandaharana amin'ny hook (event generator), dia tsy misy zava-nitranga vaovao na dia iray monja no hitarika ny fandefasana azy, na izany aza, ny zava-mitranga rehetra amin'izao fotoana izao ny fandaharana dia ho vita amin'ny lamina mahazatra. .
Ny karazana programa BPF sasany dia ahafahanao manolo ny programa amin'ny lalitra, i.e. manome sequence atomicity replace = detach old program, attach new program. Amin'ity tranga ity, ny tranga mavitrika rehetra amin'ny dikan-teny taloha amin'ny programa dia hamita ny asany, ary hisy mpikarakara hetsika vaovao hoforonina avy amin'ny programa vaovao, ary ny "atomicity" eto dia midika fa tsy misy hetsika iray tsy ho diso.
Ampifandraiso amin'ny loharanon-javatra ny fandaharana
Ato amin'ity lahatsoratra ity dia tsy hamariparitra misaraka ny fampifandraisana ny programa amin'ny loharanon-javatra, satria misy dikany ny mandalina izany amin'ny sehatry ny karazana programa manokana. Cm. etsy ambany, izay asehontsika ny fomba ifandraisan'ny programa toa ny XDP.
Manipulating zavatra mampiasa ny bpf System Call
Programa BPF
Ny zavatra BPF rehetra dia noforonina sy tantana avy amin'ny habaka mpampiasa mampiasa antso an-tariby bpf, manana ireto prototype manaraka ireto:
#include <linux/bpf.h>
int bpf(int cmd, union bpf_attr *attr, unsigned int size);Ity ny ekipa cmd dia iray amin'ireo soatoavin'ny karazana , attr - fanondro ny masontsivana ho an'ny fandaharana manokana sy size - haben'ny zavatra araka ny tondro, i.e. matetika ity sizeof(*attr). Ao amin'ny kernel 5.8 ny antso an-tariby bpf manohana baiko 34 samy hafa, ary union bpf_attr manana andalana 200. Saingy tsy tokony hatahotra an'izany isika, satria ho zatra ny tenantsika amin'ny baiko sy ny mari-pamantarana mandritra ny lahatsoratra maromaro.
Andeha isika hanomboka amin'ny ekipa BPF_PROG_LOAD, izay mamorona programa BPF - maka andiana torolalana BPF ary ampidirina ao anaty kernel. Amin'ny fotoana fandefasana dia atomboka ny verifier, ary avy eo ny compiler JIT ary, aorian'ny famonoana mahomby, dia averina amin'ny mpampiasa ny descriptor rakitra programa. Hitantsika teo amin’ny fizarana teo aloha izay nanjo azy .
Hanoratra programa mahazatra izay hampiditra programa BPF tsotra isika izao, fa mila manapa-kevitra aloha izay karazana programa tiantsika hampidirina - tsy maintsy misafidy isika. ary ao anatin'ny rafitr'ity karazana ity, manorata programa iray izay handalo ny fitsapana fanamarinana. Na izany aza, mba tsy hanasarotra ny dingana dia izao no vahaolana vonona: handray programa toy ny BPF_PROG_TYPE_XDP, izay hamerina ny sandany XDP_PASS (adino ny fonosana rehetra). Ao amin'ny assembler BPF dia toa tsotra be izany:
r0 = 2
exitRehefa avy nanapa-kevitra izahay fa Hampiditra, afaka milaza aminao ny fomba hanaovanay izany izahay:
#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();
}Ny hetsika mahaliana ao amin'ny fandaharana dia manomboka amin'ny famaritana ny array insns - ny programa BPF amin'ny kaody milina. Amin'ity tranga ity, ny torolalana tsirairay avy amin'ny programa BPF dia tafiditra ao anatin'ny rafitra . Singa voalohany insns manaraka ny toromarika r0 = 2, ny faharoa - exit.
Retreat. Ny kernel dia mamaritra macros mety kokoa amin'ny fanoratana kaody milina, ary mampiasa ny rakitra lohatenin'ny kernel tools/include/linux/filter.h afaka nanoratra izahay
struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, XDP_PASS),
BPF_EXIT_INSN()
};Saingy satria ny fanoratana programa BPF amin'ny kaody teratany dia ilaina amin'ny fanoratana fitsapana ao amin'ny kernel sy lahatsoratra momba ny BPF, ny tsy fisian'ireo macro ireo dia tsy tena manasarotra ny fiainan'ny mpamorona.
Aorian'ny famaritana ny programa BPF dia mandroso amin'ny fampidirana azy ao anaty kernel. Ny fitambaran'ny paramètre minimalista attr dia ahitana ny karazana fandaharana, napetraka sy ny isan'ny toromarika, fahazoan-dalana ilaina, ary ny anarana "woo", izay ampiasainay hitadiavana ny programanay ao amin'ny rafitra rehefa avy misintona. Ny programa, araka ny nampanantenaina, dia ampidirina ao amin'ny rafitra mampiasa antso an-tariby bpf.
Any amin'ny faran'ny programa dia miafara amin'ny loop tsy manam-petra izay simulates ny enta-mavesatra. Raha tsy misy izany dia hovonoin'ny kernel ny programa rehefa mihidy ny mpamoritra rakitra izay naverin'ny rafitra antso taminay bpf, ary tsy ho hitantsika ao amin'ny rafitra izany.
Eny ary, vonona amin'ny fitsapana izahay. Andao hanangona sy hamita ny programa eo ambany stracemba hanamarina fa mandeha araka ny tokony ho izy ny zava-drehetra:
$ 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(Tsara ny zava-drehetra, bpf(2) namerina ny tànana 3 taminay ary niditra tao amin'ny loop tsy manam-petra izahay pause(). Andao hiezaka hitady ny programantsika ao amin'ny rafitra. Mba hanaovana izany dia handeha amin'ny terminal hafa isika ary hampiasa ny utility bpftool:
# bpftool prog | grep -A3 woo
390: xdp name woo tag 3b185187f1855c4c gpl
loaded_at 2020-08-31T24:66:44+0000 uid 0
xlated 16B jited 40B memlock 4096B
pids simple-prog(10381)Hitantsika fa misy programa feno amin'ny rafitra woo izay manana ID maneran-tany dia 390 ary an-dalam-pandrosoana amin'izao fotoana izao simple-prog misy famaritana rakitra misokatra manondro ny programa (ary raha simple-prog hamita ny asa, dia woo hanjavona). Araka ny efa nampoizina, ny fandaharana woo maka 16 bytes - torolalana roa - amin'ny code binary ao amin'ny architecture BPF, fa amin'ny endriny voajanahary (x86_64) dia efa 40 bytes izany. Andeha hojerentsika ny programantsika amin'ny endriny voalohany:
# bpftool prog dump xlated id 390
0: (b7) r0 = 2
1: (95) exittsy misy surprise. Andeha hojerentsika izao ny code novokarin'ny JIT compiler:
# bpftool prog dump jited id 390
bpf_prog_3b185187f1855c4c_woo:
0: nopl 0x0(%rax,%rax,1)
5: push %rbp
6: mov %rsp,%rbp
9: sub $0x0,%rsp
10: push %rbx
11: push %r13
13: push %r14
15: push %r15
17: pushq $0x0
19: mov $0x2,%eax
1e: pop %rbx
1f: pop %r15
21: pop %r14
23: pop %r13
25: pop %rbx
26: leaveq
27: retqtsy dia mahomby amin'ny exit(2), fa raha ny rariny dia tsotra loatra ny fandaharanay, ary mazava ho azy fa ilaina ny prologue sy ny epilogue nampidirin'ny JIT compiler ho an'ny programa tsy misy dikany.
Maps
Ny programa BPF dia afaka mampiasa faritra fitadidiana voarafitra izay azon'ny programa BPF hafa sy ny programa ao amin'ny habaka mpampiasa. Ireo zavatra ireo dia antsoina hoe sarintany ary amin'ity fizarana ity dia hasehontsika ny fomba fanodikodinana azy ireo amin'ny alàlan'ny antso rafitra bpf.
Aleo lazaina avy hatrany fa ny fahaizan'ny sarintany dia tsy voafetra amin'ny fidirana amin'ny fitadidiana iombonana ihany. Misy sarintany misy tanjona manokana misy, ohatra, tondro ho an'ny programa BPF na tondro amin'ny fifandraisana amin'ny tambajotra, sarintany hiasa amin'ny hetsika perf, sns. Tsy hiresaka momba azy ireo eto izahay, mba tsy hampisavoritaka ny mpamaky. Ankoatra izany, tsy miraharaha ny olana momba ny fampifanarahana izahay, satria tsy zava-dehibe amin'ny ohatra ataontsika izany. Ny lisitra feno amin'ireo karazana sari-tany misy dia azo jerena ao , ary amin'ity fizarana ity dia horaisintsika ho ohatra ny karazana voalohany ara-tantara, ny latabatra hash BPF_MAP_TYPE_HASH.
Raha mamorona latabatra tenifototra ianao, lazao hoe, C ++, hoy ianao unordered_map<int,long> woo, izay midika hoe “Mila latabatra aho amin’ny teny Rosiana woo habe tsy voafetra, izay karazana ny lakileny int, ary ny sanda dia ny karazana long" Mba hamoronana latabatra tenifototra BPF dia mila manao zavatra mitovy ihany isika, afa-tsy ny tsy maintsy mamaritra ny haben'ny latabatra ambony indrindra, ary raha tokony hamaritra ireo karazana fanalahidy sy soatoavina isika dia mila mamaritra ny habeny amin'ny bytes. . Mba hamoronana sarintany dia ampiasao ny baiko BPF_MAP_CREATE antso rafitra bpf. Andeha hojerentsika ny programa kely indrindra na kely indrindra izay mamorona sarintany. Aorian'ny programa teo aloha izay mameno ny programa BPF dia toa tsotra aminao ity iray ity:
$ 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();
}Eto isika dia mamaritra andiana paramètre attr, izay ilaintsika hoe “Mila latabatra hash misy fanalahidy sy soatoavina habe aho sizeof(int), izay ahafahako mametraka singa efatra ambony indrindra." Rehefa mamorona sarintany BPF dia azonao atao ny mamaritra ny masontsivana hafa, ohatra, amin'ny fomba mitovy amin'ny ohatra amin'ny programa, nofaritanay ny anaran'ilay zavatra. "woo".
Andao hanangona sy hampandeha ny programa:
$ 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(Ity ny antson'ny rafitra bpf(2) namerina anay ny laharan'ny sari-tany 3 ary avy eo ny programa, araka ny efa nampoizina, dia miandry toromarika fanampiny amin'ny antson'ny rafitra pause(2).
Andeha isika handefa ny programa ho any ambadika na hanokatra terminal hafa ary hijery ny zavatra ataontsika amin'ny fampiasana ny utility bpftool (azontsika manavaka ny sarintany amin'ny hafa amin'ny anarany):
$ sudo bpftool map
...
114: hash name woo flags 0x0
key 4B value 4B max_entries 4 memlock 4096B
...Ny isa 114 dia ny kaontinanta manerantany momba ny zavatra ataontsika. Ny programa rehetra ao amin'ny rafitra dia afaka mampiasa ity ID ity hanokafana sari-tany efa misy amin'ny alàlan'ny baiko BPF_MAP_GET_FD_BY_ID antso rafitra bpf.
Afaka milalao amin'ny latabatra hash isika izao. Andeha hojerentsika ny ao anatiny:
$ sudo bpftool map dump id 114
Found 0 elementsFoana. Aleo asiana lanja hash[1] = 1:
$ sudo bpftool map update id 114 key 1 0 0 0 value 1 0 0 0Andeha hojerentsika indray ny latabatra:
$ sudo bpftool map dump id 114
key: 01 00 00 00 value: 01 00 00 00
Found 1 elementHooray! Nahavita nanampy singa iray izahay. Mariho fa tsy maintsy miasa amin'ny ambaratonga byte isika mba hanaovana izany, satria bptftool tsy mahalala hoe inona no karazana sanda ao amin'ny latabatra hash. (Ity fahalalana ity dia azo afindra aminy amin'ny fampiasana BTF, fa bebe kokoa amin'izany ankehitriny.)
Ahoana marina ny mamaky sy manampy singa ny bpftool? Andeha hojerentsika ao ambanin'ny satroka:
$ 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 ENOENTVoalohany dia nosokafanay ny sarintany tamin'ny alàlan'ny ID manerantany amin'ny alàlan'ny baiko BPF_MAP_GET_FD_BY_ID и bpf(2) namerina ny descriptor 3 taminay BPF_MAP_GET_NEXT_KEY Hitanay teo amin'ny latabatra ny lakile voalohany tamin'ny nandalovanay NULL ho fanondro ny fanalahidy "teo aloha". Raha manana ny fanalahidy isika dia afaka manao BPF_MAP_LOOKUP_ELEMizay mamerina sanda amin'ny tondro value. Ny dingana manaraka dia miezaka mitady ny singa manaraka isika amin'ny alàlan'ny fandefasana tondro mankany amin'ny fanalahidy ankehitriny, fa ny latabatray dia tsy misy afa-tsy singa iray sy ny baiko. BPF_MAP_GET_NEXT_KEY miverina ENOENT.
Eny ary, andao hanova ny sanda amin'ny fanalahidy 1, andao atao hoe mila fisoratana anarana ny lojikan'ny orinasantsika 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) = 0Araka ny efa nampoizina dia tena tsotra: ny baiko BPF_MAP_GET_FD_BY_ID manokatra ny sari-tany amin'ny ID, ary ny baiko BPF_MAP_UPDATE_ELEM overwrite ny singa.
Noho izany, aorian'ny famoronana latabatra hash avy amin'ny programa iray dia afaka mamaky sy manoratra ny votoatiny avy amin'ny iray hafa isika. Mariho fa raha afaka nanao izany avy amin'ny baikon'ny baiko izahay, dia afaka manao izany ny programa hafa amin'ny rafitra. Ho fanampin'ireo baiko voalaza etsy ambony, ho an'ny fiasana amin'ny sarintany avy amin'ny habaka mpampiasa, :
BPF_MAP_LOOKUP_ELEM: mitady sanda amin'ny lakileBPF_MAP_UPDATE_ELEM: manavao/mamorona sandaBPF_MAP_DELETE_ELEM: manala lakileBPF_MAP_GET_NEXT_KEY: tadiavo ny fanalahidy manaraka (na voalohany).BPF_MAP_GET_NEXT_ID: ahafahanao mamakivaky ny sari-tany rehetra misy, izany no fomba fiasanybpftool mapBPF_MAP_GET_FD_BY_ID: manokatra sari-tany efa misy amin'ny alàlan'ny ID manerantanyBPF_MAP_LOOKUP_AND_DELETE_ELEM: fanavaozana atomika ny sandan'ny zavatra iray ary avereno ilay talohaBPF_MAP_FREEZE: Ataovy tsy azo ovaina ny sari-tany avy amin'ny espace mpampiasa (tsy azo tsoahina ity hetsika ity)BPF_MAP_LOOKUP_BATCH,BPF_MAP_LOOKUP_AND_DELETE_BATCH,BPF_MAP_UPDATE_BATCH,BPF_MAP_DELETE_BATCH: asa faobe. Ohatra,BPF_MAP_LOOKUP_AND_DELETE_BATCH- ity no hany fomba azo antoka hamakiana sy hamerenana ny soatoavina rehetra amin'ny sarintany
Tsy ireo baiko rehetra ireo no miasa amin'ny karazana sari-tany rehetra, fa amin'ny ankapobeny dia mitovy tanteraka amin'ny fiasana amin'ny tabilao hash ny fiasana amin'ny karazana sari-tany hafa avy amin'ny habaka mpampiasa.
Mba hahazoana baiko, andao hamita ny fanandramana latabatra hash. Tsarovy fa namorona latabatra afaka misy fanalahidy efatra izahay? Andeha isika hanampy singa vitsivitsy:
$ 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 0Hatreto aloha dia mbola mety tsara:
$ 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 elementsAndeha isika hanampy iray hafa:
$ sudo bpftool map update id 114 key 5 0 0 0 value 1 0 0 0
Error: update failed: Argument list too longAraka ny efa nampoizina dia tsy nahomby izahay. Andeha hojerentsika ny fahadisoana amin'ny antsipiriany bebe kokoa:
$ 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 +++Tsara ny zava-drehetra: araka ny efa nampoizina, ny ekipa BPF_MAP_UPDATE_ELEM miezaka mamorona vaovao, fahadimy, fanalahidy, fa nianjera E2BIG.
Noho izany, afaka mamorona sy mampiditra programa BPF isika, ary koa mamorona sy mitantana sarintany avy amin'ny habaka mpampiasa. Ankehitriny dia lojika ny mijery ny fomba ahafahantsika mampiasa sarintany avy amin'ny programa BPF. Azontsika atao ny miresaka momba izany amin'ny fiteny programa sarotra vakiana amin'ny kaody macro milina, fa raha ny marina dia tonga ny fotoana hanehoana ny fomba nanoratana sy fikojakojana ny programa BPF - mampiasa libbpf.
(Ho an'ny mpamaky izay tsy afa-po amin'ny tsy fahampian'ny ohatra ambany: hamakafaka amin'ny antsipiriany ireo programa mampiasa sarintany sy asa mpanampy noforonina izahay. libbpf ary lazao aminao izay mitranga amin'ny ambaratonga fampianarana. Ho an'ny mpamaky tsy afa-po be dia be, nanampy izahay amin'ny toerana mety ao amin'ny lahatsoratra.)
Manoratra programa BPF mampiasa libbpf
Ny fanoratana programa BPF amin'ny fampiasana kaody milina dia mety hahaliana raha tsy amin'ny voalohany, ary avy eo dia manomboka ny fahafaham-po. Amin'izao fotoana izao dia mila mampitodika ny sainao ianao llvm, izay manana lamosina amin'ny famoronana kaody ho an'ny maritrano BPF, ary koa tranomboky libbpf, izay ahafahanao manoratra ny lafiny mpampiasa amin'ny rindranasa BPF ary mametaka ny kaody amin'ny programa BPF novokarina amin'ny fampiasana llvm/clang.
Raha ny marina, araka ny ho hitantsika ato amin’ity sy lahatsoratra manaraka ity, libbpf manao asa betsaka tsy misy azy (na fitaovana mitovy amin'izany - iproute2, libbcc, libbpf-go, sns.) tsy azo iainana. Iray amin'ireo endri-javatra mamono ny tetikasa libbpf dia BPF CO-RE (Compile Once, Run Everywhere) - tetikasa ahafahanao manoratra programa BPF izay azo entina avy amin'ny kernel iray mankany amin'ny iray hafa, miaraka amin'ny fahafahana mandeha amin'ny API samihafa (ohatra, rehefa miova ny rafitra kernel amin'ny dikan-teny). ho version). Mba hahafahana miara-miasa amin'ny CO-RE dia tsy maintsy atambatra miaraka amin'ny fanohanan'ny BTF ny kernel anao (famaritanay ny fomba hanaovana izany ao amin'ny fizarana . Azonao atao ny manamarina raha natsangana tamin'ny BTF ny kernel-nao na tsia - amin'ny fisian'ity rakitra manaraka ity:
$ ls -lh /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 2.6M Jul 29 15:30 /sys/kernel/btf/vmlinuxIty rakitra ity dia mitahiry fampahalalana momba ny karazana data rehetra ampiasaina amin'ny kernel ary ampiasaina amin'ny ohatra rehetra ampiasaintsika libbpf. Hiresaka amin'ny antsipiriany momba ny CO-RE isika amin'ny lahatsoratra manaraka, fa amin'ity iray ity - manangana kernel miaraka amin'ny tenanao fotsiny CONFIG_DEBUG_INFO_BTF.
fitehirizam-boky libbpf mipetraka ao amin'ny lahatahiry tools/lib/bpf kernel sy ny fivoarany dia atao amin'ny alàlan'ny lisitry ny mailaka bpf@vger.kernel.org. Na izany aza, fitahirizana mitokana no tazonina ho an'ny filan'ny fampiharana miaina ivelan'ny kernel izay ahitana taratry ny tranomboky kernel ho an'ny fidirana mamaky bebe kokoa na latsaka.
Amin'ity fizarana ity dia hojerentsika ny fomba ahafahanao mamorona tetikasa mampiasa libbpf, andao hanoratra programa fitsapana maromaro (mihoatra na latsaka kely) ary hamakafaka amin'ny antsipiriany ny fomba fiasan'izy rehetra. Izany dia ahafahantsika manazava mora kokoa amin'ny fizarana manaraka ny fomba ifandraisan'ny programa BPF amin'ny sarintany, mpanampy kernel, BTF, sns.
Matetika ny tetikasa mampiasa libbpf ampio tahiry GitHub ho submodule git, hanao toy izany koa izahay:
$ 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.Handeha any libbpf tena tsotra:
$ 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.pcNy drafitra manaraka amin'ity fizarana ity dia toy izao manaraka izao: hanoratra programa BPF toy ny BPF_PROG_TYPE_XDP, mitovy amin'ny ohatra teo aloha, fa ao amin'ny C, dia manangona azy amin'ny fampiasana clang, ary manorata programa mpanampy izay hampiditra azy ao anaty kernel. Amin'ireto fizarana manaraka ireto dia hanitatra ny fahafahan'ny programa BPF sy ny programa mpanampy isika.
Ohatra: mamorona fampiharana feno amin'ny fampiasana libbpf
Hanombohana dia mampiasa ny rakitra izahay /sys/kernel/btf/vmlinux, izay voalaza etsy ambony, ary mamorona ny mitovy aminy amin'ny endrika fisie header:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.hIty rakitra ity dia hitahiry ny rafitra angon-drakitra rehetra misy ao amin'ny kernel, ohatra, toy izao ny famaritana ny lohatenin'ny IPv4 ao amin'ny 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;
};Ankehitriny dia hanoratra ny programa BPF amin'ny C izahay:
$ 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";Na dia hita ho tsotra be aza ny fandaharanay, dia mbola mila mandinika tsipiriany maro izahay. Voalohany, ny rakitra voalohany ampidirintsika dia vmlinux.h, izay vao noforoninay tamin'ny fampiasana bpftool btf dump - izao dia tsy mila mametraka ny fonosana kernel-headers isika raha te hahalala ny endrik'ireo rafitra kernel. Tonga eto amintsika avy ao amin'ny tranomboky ity rakitra lohateny manaraka ity libbpf. Amin'izao fotoana izao dia mila mamaritra ny macro fotsiny isika SEC, izay mandefa ny endri-tsoratra mankany amin'ny fizarana mifanaraka amin'ny rakitra zavatra ELF. Ny programanay dia voarakitra ao amin'ny fizarana xdp/simple, izay alohan'ny slash dia mamaritra ny karazana programa BPF - ity no fivoriambe ampiasaina amin'ny libbpf, mifototra amin'ny anaran'ny fizarana dia hisolo ny karazana marina amin'ny fanombohana bpf(2). Ny programa BPF mihitsy no C - tena tsotra ary ahitana andalana iray return XDP_PASS. Farany, fizarana misaraka "license" misy ny anaran'ny lisansa.
Afaka manangona ny programa amin'ny alàlan'ny llvm/clang, version>= 10.0.0, na tsara kokoa, lehibe kokoa (jereo ny fizarana ):
$ 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.oAnisan'ireo endri-javatra mahaliana: manondro ny maritrano kendrena -target bpf ary ny lalana mankany amin'ny lohapejy libbpf, izay napetrakay vao haingana. Aza adino koa ny momba -O2, raha tsy misy an'io safidy io dia mety ho gaga ianao amin'ny ho avy. Andeha hojerentsika ny kaodinay, nahavita nanoratra ny programa tadiavintsika ve isika?
$ 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: exitEny, nandaitra izany! Ankehitriny, manana rakitra binary miaraka amin'ny programa izahay, ary te hamorona fampiharana izay hampiditra azy ao anaty kernel. Ho an'ity tanjona ity, ny tranomboky libbpf dia manolotra safidy roa ho antsika - mampiasa API ambany kokoa na API ambony kokoa. Handeha amin'ny lalana faharoa isika, satria te hianatra hanoratra, hampiditra ary hampifandray ny programa BPF miaraka amin'ny ezaka kely indrindra ho an'ny fianarany manaraka.
Voalohany, mila mamorona ny "skeleton" amin'ny programantsika avy amin'ny binary isika amin'ny fampiasana fitaovana mitovy bpftool — ny antsy Soisa an'ny tontolon'ny BPF (izay azo raisina ara-bakiteny, satria Soisa i Daniel Borkman, iray amin'ireo mpamorona sy mpikarakara ny BPF):
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.hAo anaty rakitra xdp-simple.skel.h mirakitra ny code binary an'ny programantsika sy ny asany amin'ny fitantanana - mametaka, mametaka, mamafa ny zavatra. Amin'ny tranga tsotra ataontsika dia toa be loatra izany, fa miasa ihany koa amin'ny tranga misy ny rakitra zavatra misy programa sy sarintany BPF maro ary mba hamenoana ity ELF goavambe ity dia mila mamorona ny skeleton fotsiny isika ary miantso fiasa iray na roa avy amin'ny fampiharana mahazatra. dia manoratra Andao handroso izao.
Raha ny marina, ny programa loader dia tsy misy dikany:
#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);
}izany struct xdp_simple_bpf voafaritra ao anaty rakitra xdp-simple.skel.h ary manoritsoritra ny rakitra zavatra ataontsika:
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;
};Hitantsika eto ny dian'ny API ambany: ny rafitra struct bpf_program *simple и struct bpf_link *simple. Ny rafitra voalohany dia mamaritra manokana ny programantsika, voasoratra ao amin'ny fizarana xdp/simple, ary ny faharoa dia mamaritra ny fomba ifandraisan'ny programa amin'ny loharanon'ny hetsika.
asa xdp_simple_bpf__open_and_load, manokatra zavatra ELF, manara-maso azy, mamorona ny rafitra sy ny substructure rehetra (ankoatra ny programa, ny ELF dia misy fizarana hafa - angona, angona vakiana fotsiny, fampahalalana debug, fahazoan-dàlana, sns.), ary ampidiro ao anaty kernel mampiasa rafitra ANTSO bpf, izay azontsika jerena amin'ny fanangonana sy fampandehanana ny programa:
$ 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) = 4Andeha hojerentsika izao ny programa ampiasaintsika bpftool. Andeha hojerentsika ny ID azy:
# 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)ary dump (mampiasa endrika fanafohezana ny baiko izahay bpftool prog dump xlated):
# bpftool p d x id 463
int simple(void *ctx):
; return XDP_PASS;
0: (b7) r0 = 2
1: (95) exitZava-baovao! Ny programa dia nanonta ny ampahany amin'ny rakitra loharano C libbpf, izay nahita ny fizarana debug tao amin'ny binary, nanangona azy ho zavatra BTF, nampiditra azy tao amin'ny kernel mampiasa BPF_BTF_LOAD, ary avy eo dia namaritra ny famaritana rakitra vokarina rehefa mameno ny programa miaraka amin'ny baiko BPG_PROG_LOAD.
Kernel Helpers
Ny programa BPF dia afaka mitantana ny fiasa "ivelany" - mpanampy kernel. Ireo fiasa mpanampy ireo dia ahafahan'ny programa BPF miditra amin'ny rafitry ny kernel, mitantana sarintany, ary mifandray amin'ny "tontolo tena izy" - mamorona hetsika perf, mifehy ny fitaovana (ohatra, redirect packets), sns.
Ohatra: bpf_get_smp_processor_id
Ao anatin'ny rafitry ny paradigma "fianarana amin'ny alalan'ny ohatra", andeha hodinihintsika ny iray amin'ireo asa mpanampy, bpf_get_smp_processor_id(), anaty rakitra kernel/bpf/helpers.c. Mamerina ny isan'ny processeur izay mandeha ny programa BPF izay niantso azy io. Saingy tsy dia liana loatra amin'ny semantika izahay fa ny fampiharana azy dia misy andalana iray:
BPF_CALL_0(bpf_get_smp_processor_id)
{
return smp_processor_id();
}Mitovy amin'ny famaritana ny antso an-tariby ny famaritana ny asan'ny mpanampy BPF. LinuxEto, ohatra, dia voafaritra ny asa iray izay tsy misy tohan-kevitra. (Ny asa iray izay maka, ohatra, tohan-kevitra telo dia voafaritra amin'ny fampiasana macro BPF_CALL_3. Ny isan'ny tohan-kevitra ambony indrindra dia dimy.) Ny ampahany voalohany amin'ny famaritana ihany anefa io. Ny ampahany faharoa dia ny famaritana ny karazana rafitra struct bpf_func_proto, izay misy famaritana ny asan'ny mpanampy izay takatry ny mpanamarina:
const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
.func = bpf_get_smp_processor_id,
.gpl_only = false,
.ret_type = RET_INTEGER,
};Misoratra anarana Helper Functions
Mba hahafahan'ny programa BPF amin'ny karazana manokana hampiasa an'io fiasa io, dia tsy maintsy misoratra anarana izy ireo, ohatra ho an'ny karazana BPF_PROG_TYPE_XDP misy asa voafaritra ao amin'ny kernel xdp_func_proto, izay mamaritra avy amin'ny ID asa mpanampy raha manohana an'io asa io na tsia ny XDP. Ny asantsika dia :
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;
...
}
}Ny karazana programa BPF vaovao dia "voafaritra" ao anaty rakitra mampiasa macro BPF_PROG_TYPE. Voafaritra amin'ny teny nindramina satria famaritana lojika izy io, ary amin'ny fiteny C dia miseho any amin'ny toerana hafa ny famaritana ny rafitra mivaingana iray manontolo. Indrindra indrindra, amin'ny rakitra kernel/bpf/verifier.c famaritana rehetra avy amin'ny rakitra bpf_types.h dia ampiasaina hamoronana rafitra maromaro 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
};Izany hoe, ho an'ny karazana programa BPF tsirairay, dia misy tondro ho an'ny firafitry ny angona amin'ny karazana struct bpf_verifier_ops, izay natomboka tamin'ny sandany _name ## _verifier_ops, izany hoe, xdp_verifier_ops ho an'ny xdp. FIRAFITRA xdp_verifier_ops anaty rakitra net/core/filter.c toy izao manaraka izao:
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,
};Eto isika dia mahita ny asany mahazatra xdp_func_proto, izay hampandeha ny mpanamarina isaky ny tojo fanamby ny sasany miasa ao anatin'ny programa BPF, jereo .
Andeha hojerentsika ny fomba ampiasain'ny programa BPF hypothetical ny fiasa bpf_get_smp_processor_id. Mba hanaovana izany, avereno soratana ny programa avy amin'ny fizarana teo aloha toy izao manaraka izao:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
SEC("xdp/simple")
int simple(void *ctx)
{
if (bpf_get_smp_processor_id() != 0)
return XDP_DROP;
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";Символ bpf_get_smp_processor_id в <bpf/bpf_helper_defs.h> trano famakiam-boky libbpf Ahoana no
static u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;izany hoe, bpf_get_smp_processor_id dia tondro fiasa izay manana sanda 8, ary 8 no sandany BPF_FUNC_get_smp_processor_id karazana enum bpf_fun_id, izay voafaritra ho antsika ao amin'ny rakitra vmlinux.h (file bpf_helper_defs.h ao amin'ny kernel dia novokarin'ny script, ka ny isa "majika" dia mety). Ity fiasa ity dia tsy maka hevitra ary mamerina sanda iray karazana __u32. Rehefa manao izany ao amin'ny programa izahay, clang miteraka fampianarana BPF_CALL "karazana mety" Andao hanangona ny programa ary hijery ny fizarana 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 exitAo amin'ny andalana voalohany isika dia mahita toromarika call, parameter IMM izay mitovy amin'ny 8, ary SRC_REG - aotra. Araka ny fifanarahana ABI ampiasain'ny mpanamarina, antso ho an'ny asa mpanampy laharana faha-8 ity. Raha vao natomboka dia tsotra ny lojika. Mamerina sanda avy amin'ny rejisitra r0 adika amin'ny r1 ary amin'ny andalana 2,3 dia avadika ho karazana u32 - voafafa ny 32 bit ambony. Amin'ny andalana 4,5,6,7 dia miverina 2 isika (XDP_PASS) na 1 (XDP_DROP) miankina amin'ny hoe namerina sanda aotra na tsy aotra ny asa mpanampy avy amin'ny andalana 0.
Andao hizaha toetra ny tenantsika: ento ny programa ary jereo ny vokatra 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) exitOk, nahita ny mpanampy kernel marina ny mpanamarina.
Ohatra: mandalo ady hevitra ary farany mihazakazaka ny programa!
Ny asa mpanampy amin'ny run-level rehetra dia manana prototype
u64 fn(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)Ny parametres amin'ny asa mpanampy dia alefa amin'ny rejisitra r1-r5, ary averina ao amin'ny rejisitra ny sandany r0. Tsy misy asa izay mitaky hevitra mihoatra ny dimy, ary ny fanohanana azy ireo dia tsy antenaina hiampy amin'ny ho avy.
Andeha hojerentsika ny mpanampy kernel vaovao sy ny fomba fandalovan'ny BPF paramètre. Andao hanoratra indray xdp-simple.bpf.c toy izao manaraka izao (tsy niova ny andalana sisa):
SEC("xdp/simple")
int simple(void *ctx)
{
bpf_printk("running on CPU%un", bpf_get_smp_processor_id());
return XDP_PASS;
}Ny programanay dia manonta ny laharan'ny CPU izay iasany. Andao hanangona azy ary hijery ny code:
$ 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: exitAo amin'ny andalana 0-7 no manoratra ny tady running on CPU%un, ary avy eo amin'ny andalana faha-8 dia mihazakazaka ilay mahazatra bpf_get_smp_processor_id. Ao amin'ny andalana 9-12 dia manomana ny tohan-kevitry ny mpanampy isika bpf_printk - rejisitra r1, r2, r3. Nahoana no telo izy ireo fa tsy roa? SATRIA bpf_printk - manodidina ny tena mpanampy bpf_trace_printk, izay mila mandalo ny haben'ny tady endrika.
Andeha isika hanampy andalana roa amin'ny xdp-simple.cmba hifandray amin'ny interface ny programantsika lo ary tena nanomboka!
$ 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);
}Eto isika dia mampiasa ny asa bpf_set_link_xdp_fd, izay mampifandray ny programa BPF karazana XDP amin'ny fifandraisana amin'ny tambajotra. Nofehezinay mafy ny laharan'ny interface lo, izay foana 1. Indroa mandeha ny asa mba hanaisotra ny programa taloha raha ampiarahina. Mariho fa tsy mila fanamby isika izao pause na loop tsy manam-petra: hivoaka ny programan'ny loader, fa tsy hovonoina ny programa BPF satria mifandray amin'ny loharanon'ny hetsika. Rehefa vita ny fampidinana sy ny fifandraisana, dia hatomboka ho an'ny fonosan-tambajotra tsirairay tonga ny fandaharana lo.
Andeha isika haka ny fandaharana ary hijery ny interface tsara 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 669Ny programa alainay dia manana ID 669 ary mahita ID mitovy amin'ny interface izahay lo. Handefa fonosana roa izahay 127.0.0.1 (fangatahana + valiny):
$ ping -c1 localhostary ankehitriny andeha hojerentsika ny votoatin'ny rakitra virtoaly debug /sys/kernel/debug/tracing/trace_pipe, izay bpf_printk manoratra ny hafany:
# 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 CPU0Nahitana fonosana roa lo ary nokarakaraina tamin'ny CPU0 - niasa ny programa BPF tsy misy dikany voalohany!
Tsara homarihina izany bpf_printk Tsy maninona ny manoratra amin'ny rakitra debug: tsy ity no mpanampy mahomby indrindra ampiasaina amin'ny famokarana, fa ny tanjonay dia ny hampiseho zavatra tsotra.
Fidirana sarintany avy amin'ny programa BPF
Ohatra: mampiasa sarintany avy amin'ny programa BPF
Tao amin'ny fizarana teo aloha dia nianatra ny fomba hamoronana sy fampiasana sarintany avy amin'ny habaka mpampiasa isika, ary andeha hojerentsika ny ampahany kernel. Andeha isika hanomboka, toy ny mahazatra, amin'ny ohatra. Andao hanoratra indray ny programantsika xdp-simple.bpf.c toy izao manaraka izao:
#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";Tany am-piandohan'ny fandaharana dia nanampy famaritana sari-tany izahay woo: Ity dia singa 8 izay mitahiry sanda toa ny u64 (ao amin'ny C dia mamaritra ny array toy ny u64 woo[8]). Amin'ny programa iray "xdp/simple" dia mahazo ny laharan'ny processeur amin'izao fotoana izao ho lasa miovaova key ary avy eo mampiasa ny asa mpanampy bpf_map_lookup_element isika dia mahazo tondro mankany amin'ny fidirana mifanaraka amin'ny array, izay ampitombointsika iray. Nadika amin'ny teny rosiana: kajy ny antontan'isa momba ny CPU nanodinana fonosana ho avy. Andeha isika hanandrana ny programa:
$ 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-simpleAndeha hojerentsika fa mifandray izy lo ary mandefa fonosana vitsivitsy:
$ 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; doneAndeha hojerentsika izao ny votoatin'ny array:
$ 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 }
]Saika ny dingana rehetra dia nokarakaraina tamin'ny CPU7. Tsy zava-dehibe amintsika izany, ny zava-dehibe dia ny fiasan'ny programa ary azontsika ny fomba fidirana amin'ny sarintany avy amin'ny programa BPF - mampiasa .
Fanondroana mistika
Noho izany, afaka miditra amin'ny sarintany avy amin'ny programa BPF isika amin'ny fampiasana antso toy ny
val = bpf_map_lookup_elem(&woo, &key);izay misy ny asan'ny mpanampy
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)fa mandalo tondro isika &woo amin'ny rafitra tsy fantatra anarana struct { ... }...
Raha mijery ny programa assembler isika dia mahita fa ny sanda &woo tsy tena voafaritra (andalana 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
...ary voarakitra ao anatin'ny fifindra-monina:
$ 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 wooFa raha mijery ny programa efa feno isika dia mahita tondro mankany amin'ny sarintany marina (andalana 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]
...Noho izany, afaka manatsoaka hevitra isika fa amin'ny fotoana hanombohana ny programa loader, ny rohy mankany &woo nosoloina zavatra misy tranomboky libbpf. Hojerentsika aloha ny vokatra 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) = 5Hitantsika izany libbpf namorona sarintany woo ary avy eo nisintona ny programantsika simple. Andeha hojerentsika akaiky ny fomba fametahana ny programa:
- ANTSO
xdp_simple_bpf__open_and_loadavy amin'ny rakitraxdp-simple.skel.h - izay mahatonga
xdp_simple_bpf__loadavy amin'ny rakitraxdp-simple.skel.h - izay mahatonga
bpf_object__load_skeletonavy amin'ny rakitralibbpf/src/libbpf.c - izay mahatonga
bpf_object__load_xattravy amin'nylibbpf/src/libbpf.c
Ny asa farany, ankoatra ny zavatra hafa, dia hiantso bpf_object__create_maps, izay mamorona na manokatra sari-tany efa misy, mamadika azy ireo ho mpamaritra rakitra. (Eto no hitantsika BPF_MAP_CREATE amin'ny vokatra strace.) Manaraka ny asa dia antsoina bpf_object__relocate ary izy no mahaliana antsika, satria tsaroantsika izay efa hitantsika woo ao amin'ny latabatra fifindra-monina. Rehefa mikaroka azy isika dia mahita ny tenantsika amin'ny asa bpf_program__relocate, izay :
case RELO_LD64:
insn[0].src_reg = BPF_PSEUDO_MAP_FD;
insn[0].imm = obj->maps[relo->map_idx].fd;
break;Noho izany dia raisinay ny torolalana
18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 llary soloy ny rejisitra loharano ao aminy BPF_PSEUDO_MAP_FD, ary ny IMM voalohany amin'ny famaritana ny rakitra amin'ny sari-tany ary, raha mitovy amin'ny, ohatra, 0xdeadbeef, dia ho azonay ny fampianarana
18 11 00 00 ef eb ad de 00 00 00 00 00 00 00 00 r1 = 0 llToy izao ny fomba famindrana ny mombamomba ny sari-tany mankany amin'ny programa BPF feno entana. Amin'ity tranga ity, ny sarintany dia azo noforonina amin'ny fampiasana BPF_MAP_CREATE, ary nosokafana tamin'ny ID mampiasa BPF_MAP_GET_FD_BY_ID.
Total, rehefa mampiasa libbpf ny algorithm dia toy izao manaraka izao:
- mandritra ny fanangonana dia amboarina ao amin'ny latabatra fifindra-monina ny firaketana ho an'ny rohy mankany amin'ny sarintany
libbpfmanokatra ny bokin'ny zavatra ELF, mahita ny sari-tany ampiasaina rehetra ary mamorona famaritana rakitra ho azy ireo- Ny famaritana ny rakitra dia ampidirina ao amin'ny kernel ho ampahany amin'ny fampianarana
LD64
Araka ny azonao eritreretina, mbola misy zavatra ho avy ary tsy maintsy hojerentsika ny fototra. Soa ihany fa manana hevitra izahay - nanoratra ny dikany izahay BPF_PSEUDO_MAP_FD ao amin'ny rejisitra loharano ary azontsika alevina izany, izay hitondra antsika ho any amin'ny Masin'ny olo-masina rehetra - kernel/bpf/verifier.c, izay misy fiasa misy anarana miavaka manolo ny mpamoritra rakitra miaraka amin'ny adiresin'ny rafitra iray 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;(azo jerena ny code feno ). Noho izany dia afaka manitatra ny algorithm izahay:
- eo am-pandrafetana ny programa dia manamarina ny fampiasana marina ny sarintany ny mpanamarina ary manoratra ny adiresin'ny rafitra mifanaraka amin'izany
struct bpf_map
Rehefa misintona ny binary ELF mampiasa libbpf Mbola betsaka ny zavatra mitranga, fa hodinihintsika ao amin'ny lahatsoratra hafa izany.
Mametraka programa sy sarintany tsy misy libbpf
Araka ny nampanantenaina, ity misy ohatra ho an'ny mpamaky te-hahafantatra ny fomba hamoronana sy hampidirana programa mampiasa sarintany, tsy misy fanampiana. libbpf. Mety ilaina izany rehefa miasa amin'ny tontolo iray izay tsy ahafahanao manangana fiankinan-doha, na mitahiry kely, na manoratra programa toy ny , izay miteraka kaody binary BPF amin'ny lalitra.
Mba hanamorana ny fanarahana ny lojika, dia hamerina hanoratra ny ohatra ho an'ireo tanjona ireo isika xdp-simple. Ny kaody feno sy mivelatra kely amin'ny programa resahina amin'ity ohatra ity dia azo jerena ato .
Ny lojikan'ny fampiharana anay dia toy izao manaraka izao:
- mamorona sarintany karazana
BPF_MAP_TYPE_ARRAYmampiasa ny baikoBPF_MAP_CREATE, - mamorona programa mampiasa ity sari-tany ity,
- mampifandray ny programa amin'ny interface
lo,
izay adika ho olombelona hoe
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);
}izany map_create mamorona sarintany mitovy amin'ny nataonay tamin'ny ohatra voalohany momba ny antson'ny rafitra bpf - “Kernel, azafady, ataovy sari-tany vaovao aho amin'ny endrika andiana singa 8 toy ny __u64 ary avereno amiko ny famaritana ny rakitra":
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));
}Ny programa dia mora entina ihany koa:
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));
}Ny ampahany sarotra prog_load dia ny famaritana ny fandaharan'asa BPF ho toy ny rafitra maromaro struct bpf_insn insns[]. Saingy satria mampiasa programa izay ananantsika amin'ny C isika dia afaka mamitaka kely:
$ 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 exitAmin'ny fitambarany, mila manoratra torolàlana 14 amin'ny endrika rafitra toy ny struct bpf_insn (toro-hevitra: alao ny fanariam-pako avy any ambony, avereno vakiana ny fizarana toromarika, sokafy и ary miezaka hamantatra struct bpf_insn insns[] irery):
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
},
};Fanatanjahan-tena ho an'ireo izay tsy nanoratra izany ny tenany - mahita map_fd.
Misy ampahany iray hafa tsy fantatra tavela ao amin'ny programantsika - xdp_attach. Mampalahelo fa tsy azo ampifandraisina amin'ny alàlan'ny antso an-tariby ny programa toa ny XDP bpfAvy amin'ny vondrom-piarahamonina an-tserasera ireo olona namorona ny BPF sy XDP. Linux, izay midika fa nampiasa ilay nahazatra azy ireo indrindra izy ireo (fa tsy ho an'ny ara-dalàna olona) interface tsara hifandraisana amin'ny kernel: , Jereo ihany koa . Ny fomba tsotra indrindra fampiharana xdp_attach dia maka kaody avy amin'ny libbpf, izany hoe avy amin'ny rakitra , izay no nataonay, nohafohezina kely:
Tongasoa eto amin'ny tontolon'ny socket netlink
Sokafy karazana socket 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;
}Mamaky avy amin'ity socket ity izahay:
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;
}Farany, ity ny asa ataontsika izay manokatra socket ary mandefa hafatra manokana ho azy izay misy famaritana rakitra:
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;
}Noho izany, ny zava-drehetra dia vonona amin'ny fitsapana:
$ 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 +++Andeha hojerentsika raha mifandray amin'ny programantsika 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 160Andao handefa ping ary hijery ny sari-tany:
$ 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 elementsHira, mandeha ny zava-drehetra. Mariho, raha ny marina, fa ny sarintany dia aseho indray amin'ny endrika bytes. Izany dia noho ny zava-misy fa, tsy mitovy libbpf tsy nampiditra fampahalalana momba ny karazana (BTF) izahay. Mbola hiresaka bebe kokoa momba izany anefa isika amin’ny manaraka.
Fitaovana fampandrosoana
Amin'ity fizarana ity dia hojerentsika ny fitaovana fampivoarana BPF kely indrindra.
Amin'ny ankapobeny, tsy mila zavatra manokana ianao hamolavola programa BPF - BPF dia mandeha amin'ny kernel fizarana mendrika, ary ny programa dia aorina amin'ny fampiasana clang, izay azo omena avy amin'ny fonosana. Na izany aza, noho ny zava-misy fa ny BPF dia eo an-dalam-pandrosoana, ny kernel sy ny fitaovana dia miova tsy tapaka, raha tsy te hanoratra programa BPF ianao amin'ny fampiasana fomba efa tranainy manomboka amin'ny 2019, dia tsy maintsy manangona ianao.
llvm/clangpahole- ny fotony
bpftool
(Ho an'ny referansa: ity fizarana ity sy ny ohatra rehetra ao amin'ny lahatsoratra dia notanterahina tamin'ny Debian 10.)
llvm/clang
BPF dia sariaka amin'ny LLVM ary, na dia vao haingana aza ny programa ho an'ny BPF dia azo angonina amin'ny fampiasana gcc, ny fampandrosoana ankehitriny rehetra dia natao ho an'ny LLVM. Noho izany, voalohany indrindra, hanangana ny dikan-teny ankehitriny isika clang avy amin'ny 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
... много времени спустя
$Ankehitriny isika dia afaka manamarina raha nitambatra tsara ny zava-drehetra:
$ ./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(Torolalana momba ny fivoriambe clang nalaiko avy .)
Tsy hametraka ireo programa vao naorinay izahay fa ampio fotsiny PATH, ohatra:
export PATH="`pwd`/bin:$PATH"(Ity dia azo ampiana amin'ny .bashrc na amina rakitra misaraka. Amiko manokana dia ampiako zavatra toa izao ~/bin/activate-llvm.sh ary rehefa ilaina dia ataoko izany . activate-llvm.sh.)
Pahole sy BTF
Utility pahole ampiasaina amin'ny fananganana kernel mba hamoronana fampahalalana debug amin'ny endrika BTF. Tsy hiditra amin'ny antsipiriany amin'ity lahatsoratra ity izahay momba ny antsipirian'ny teknolojia BTF, ankoatra ny maha-mety azy ary tiantsika ny hampiasa azy. Ka raha te hanangana ny kernel ianao dia amboary aloha pahole (tsy misy pahole tsy ho afaka hanorina kernel miaraka amin'ny safidy ianao 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/paholeKernels hanaovana fanandramana amin'ny BPF
Rehefa mikaroka ny mety ho an'ny BPF aho dia te hanangona ny atiko manokana. Izany, amin'ny ankapobeny, dia tsy ilaina, satria ho afaka hanangona sy hampiditra programa BPF amin'ny kernel fizarana ianao, na izany aza, ny fananana kernel anao manokana dia ahafahanao mampiasa ny endri-javatra BPF farany indrindra, izay hiseho amin'ny fizaranao mandritra ny volana tsara indrindra. , na, toy ny amin'ny fitaovana debugging sasany dia tsy hofonosina mihitsy amin'ny hoavy. Ary koa, ny fotony manokana dia mahatonga azy ho zava-dehibe ny manandrana ny kaody.
Mba hamoronana kernel dia ilainao ny voalohany, ny kernel mihitsy, ary ny faharoa, ny rakitra fanamafisana kernel. Mba hanandrana amin'ny BPF dia afaka mampiasa ny mahazatra fototra na iray amin'ireo fototry ny mpamorona. Ara-tantara, ny fampandrosoana ny BPF dia nitranga tao anatin'ny vondrom-piarahamonina tambajotra. Linux ary noho izany ny fiovana rehetra na ho ela na ho haingana dia mandalo an'i David Miller, ilay mpikarakara tambajotra LinuxMiankina amin'ny toetrany—fanamboarana na endri-javatra vaovao—ny fiovan'ny tambajotra dia miafara amin'ny iray amin'ireo fototra roa: na . Ny fiovana ho an'ny BPF dia zaraina amin'ny fomba mitovy и , izay atambatra ho net sy net-next avy eo. Raha mila fanazavana fanampiny dia jereo и . Noho izany dia mifidiana kernel iray mifototra amin'ny tsironao sy ny filàna fahamarinan'ny rafitra izay andramanao (*-next kernels no tsy marin-toerana indrindra amin'ireo voatanisa).
Mihoatra noho ity lahatsoratra ity ny miresaka momba ny fomba fitantanana ny rakitra fanamafisana kernel - heverina fa efa fantatrao ny fomba hanaovana izany, na samirery. Na izany aza, ireto torolalana manaraka ireto dia tokony ho ampy na latsaka mba hanomezana anao rafitra miasa BPF.
Ampidino ny iray amin'ireo kernel etsy ambony:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
$ cd bpf-nextManangana config kernel miasa kely indrindra:
$ cp /boot/config-`uname -r` .config
$ make localmodconfigAlefaso ny safidy BPF amin'ny rakitra .config amin'ny safidinao manokana (azo inoana fa CONFIG_BPF dia efa alefa hatramin'ny nampiasan'ny systemd azy). Ity misy lisitry ny safidy avy amin'ny kernel ampiasaina amin'ity lahatsoratra ity:
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=yAvy eo dia afaka manangona sy mametraka mora foana ny modules sy ny kernel isika (amin'ny fomba, azonao atao ny manangona ny kernel amin'ny alàlan'ny vao tafavory. clangamin'ny fanampiana CC=clang):
$ make -s -j $(getconf _NPROCESSORS_ONLN)
$ sudo make modules_install
$ sudo make installary avereno miaraka amin'ny kernel vaovao (ampiasaiko amin'ity kexec avy amin'ny fonosana 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 -ebpftool
Ny fitaovana ampiasaina matetika indrindra amin'ny lahatsoratra dia ny utility bpftool, omena ho anisan'ny kernel LinuxNosoratan'ireo mpamorona BPF sy tantanan'izy ireo ho an'ny mpamorona BPF izy io ary azo ampiasaina hitantanana ny karazana zavatra BPF rehetra—mampiditra programa, mamorona sy manova sarintany, mikaroka ny tontolo iainana BPF, sy ny maro hafa. Azo jerena ao ny antontan-taratasy amin'ny endrika kaody loharano ho an'ny pejy man. na, efa natambatra, .
Tamin'ny fotoana nanoratana ity bpftool tonga efa vita ho an'ny RHEL, Fedora ary Ubuntu (jereo, ohatra, , izay milaza ny tantara tsy vita momba ny fonosana bpftool в Debian). Fa raha efa nanangona ny kernel-nao ianao dia amboary bpftool mora toy ny 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 ]
$(Eto ${linux} - ity no lahatahiry kernel anao.) Rehefa avy nanatanteraka ireo baiko ireo bpftool angonina ao anaty lahatahiry iray ${linux}/tools/bpf/bpftool ary azo ampidirina amin'ny lalana (voalohany indrindra amin'ny mpampiasa root) na kopia fotsiny amin'ny /usr/local/sbin.
manangona bpftool tsara ny mampiasa ity farany clang, miangona araka ny voalaza etsy ambony, ary jereo raha mitambatra tsara - mampiasa, ohatra, ny baiko
$ 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
...izay hampiseho izay endri-javatra BPF alefa ao amin'ny kernel anao.
Raha ny marina, ny baiko teo aloha dia azo atao toy ny
# bpftool f p kIzany dia atao amin'ny alàlan'ny analogy amin'ny fitaovana avy amin'ny fonosana iproute2, izay azontsika, ohatra, milaza ip a s eth0 raha tokony ip addr show dev eth0.
famaranana
Ny BPF dia ahafahanao manao kiraro parasy mba handrefesana tsara sy hanova ny fiasan'ny fotony. Tena nahomby ilay rafitra, tamin'ny fomba amam-panao tsara indrindra an'ny UNIX: rafitra tsotra izay ahafahanao (manodina) ny kernel dia namela olona sy fikambanana marobe hanandrana. Ary, na dia mbola tsy vita aza ny andrana, ary koa ny fampandrosoana ny fotodrafitrasa BPF, ny rafitra dia efa manana ABI stable izay ahafahanao manangana lojika ara-barotra azo itokisana, ary indrindra indrindra, mahomby.
Tiako ny manamarika fa, raha ny hevitro, dia lasa malaza be ny teknolojia satria, amin'ny lafiny iray, afaka milalao (azo takarina ao anatin'ny takariva iray na latsaka ny maritrano amin'ny milina iray), ary amin'ny lafiny iray, mba hamahana olana izay tsy voavaha (tsara tarehy) alohan'ny fisehoany. Ireo singa roa ireo miaraka dia manery ny olona hanandrana sy hanonofy, izay mitarika amin'ny firongatry ny vahaolana vaovao kokoa.
Ity lahatsoratra ity, na dia tsy fohy loatra aza, dia fampidirana fotsiny ny tontolon'ny BPF ary tsy mamaritra ireo endri-javatra "ambony" sy ireo ampahany manan-danja amin'ny maritrano. Ny drafitra ho avy dia toa izao: ny lahatsoratra manaraka dia hijery ny karazana programa BPF (misy karazana programa 5.8 tohanana ao amin'ny kernel 30), dia hojerentsika amin'ny farany ny fomba fanoratana ny rindranasa BPF tena izy amin'ny fampiasana programa tracing kernel. Ohatra, tonga ny fotoana hanaovana fianarana lalindalina kokoa momba ny maritrano BPF, arahin'ny ohatra momba ny tambajotra BPF sy ny fampiharana fiarovana.
Lahatsoratra teo aloha amin'ity andiany ity
rohy
- antontan-taratasy momba ny BPF avy amin'ny cilium, na ny marimarina kokoa avy amin'i Daniel Borkman, iray amin'ireo mpamorona sy mpitahiry ny BPF. Io no iray amin’ireo filazalazana lehibe voalohany, izay tsy mitovy amin’ireo hafa, satria fantatr’i Daniela tsara izay nosoratany ary tsy misy fahadisoana ao. Indrindra indrindra, ity antontan-taratasy ity dia mamaritra ny fomba fiasa miaraka amin'ny programa BPF amin'ny karazana XDP sy TC amin'ny fampiasana ny fitaovana malaza.
ipavy amin'ny fonosanaiproute2.- rakitra tany am-boalohany miaraka amin'ny antontan-taratasy ho an'ny BPF mahazatra ary avy eo miitatra. Vakiteny tsara raha te-handalina ny fiteny fivoriambe sy ny antsipirian'ny maritrano ara-teknika ianao.
. Tsy dia nohavaozina izy io, saingy mety, araka ny nosoratan'i Alexei Starovoitov (mpanoratra ny eBPF) sy Andrii Nakryiko - (mpihazona) ao.
libbpf).. Lohahevitra twitter mampiala voly avy amin'i Quentin Monnet misy ohatra sy tsiambaratelo amin'ny fampiasana bpftool.
. Lisitry ny rohy lehibe (ary mbola tazonina) mankany amin'ny antontan-taratasy BPF avy amin'ny Quentin Monnet.
Source: www.habr.com
