BPF pou ti piti yo, premye pati: BPF pwolonje

Nan kòmansman an te gen yon teknoloji epi li te rele BPF. Nou te gade li anvan yo, Ansyen Testaman atik nan seri sa a. Nan 2013, atravè efò yo nan Alexei Starovoitov ak Daniel Borkman, yon vèsyon amelyore nan li, optimize pou machin modèn 64-bit, te devlope ak enkli nan nwayo a Linux. Nouvo teknoloji sa a te rele yon ti tan BPF Entèn, Lè sa a, chanje non BPF pwolonje, epi kounye a, apre plizyè ane, tout moun tou senpleman rele li BPF.

Apeprè pale, BPF pèmèt ou kouri kòd abitrè itilizatè-apwovizyone nan espas nwayo Linux la, ak nouvo achitekti a te vin tèlman reyisi ke nou pral bezwen yon douzèn atik plis dekri tout aplikasyon li yo. (Sèl bagay devlopè yo pa t 'fè byen, jan ou ka wè nan kòd pèfòmans ki anba a, se te kreye yon logo desan.)

Atik sa a dekri estrikti machin vityèl BPF, koòdone nwayo pou travay ak BPF, zouti devlopman, ansanm ak yon apèsi tou kout, trè kout sou kapasite ki egziste deja, i.e. tout sa nou pral bezwen nan tan kap vini an pou yon etid pi fon nan aplikasyon yo pratik nan BPF.
BPF pou ti piti yo, premye pati: BPF pwolonje

Rezime atik la

Entwodiksyon nan achitekti BPF. Premyèman, nou pral pran yon je zwazo nan achitekti BPF ak deskripsyon eleman prensipal yo.

Anrejistre ak sistèm kòmand nan machin vityèl BPF. Deja gen yon lide sou achitekti a kòm yon antye, nou pral dekri estrikti a nan machin nan BPF vityèl.

Sik lavi objè BPF, sistèm dosye bpffs. Nan seksyon sa a, nou pral pran yon gade pi pre nan sik lavi a nan objè BPF - pwogram ak kat.

Jere objè lè l sèvi avèk apèl sistèm bpf la. Avèk kèk konpreyansyon sou sistèm nan deja an plas, nou pral finalman gade nan ki jan yo kreye ak manipile objè ki soti nan espas itilizatè lè l sèvi avèk yon apèl sistèm espesyal - bpf(2).

Пишем программы BPF с помощью libbpf. Natirèlman, ou ka ekri pwogram lè l sèvi avèk yon apèl sistèm. Men, li difisil. Pou yon senaryo ki pi reyalis, pwogramasyon nikleyè devlope yon bibliyotèk libbpf. Nou pral kreye yon eskèlèt aplikasyon BPF debaz ke nou pral itilize nan egzanp ki vin apre yo.

Èd Kernel. La a nou pral aprann kijan pwogram BPF ka jwenn aksè nan fonksyon asistan nwayo a - yon zouti ki, ansanm ak kat, fondamantalman elaji kapasite nouvo BPF la konpare ak youn klasik la.

Aksè nan kat nan pwogram BPF. Nan pwen sa a, nou pral konnen ase yo konprann egzakteman ki jan nou ka kreye pwogram ki sèvi ak kat. Epi ann menm pran yon ti gade vit nan verifikatè a gwo ak vanyan sòlda.

Zouti devlopman. Ede seksyon sou kòman yo rasanble sèvis piblik ki nesesè yo ak nwayo pou eksperyans.

Konklizyon an. Nan fen atik la, moun ki li sa yo pral jwenn mo ki motive ak yon deskripsyon tou kout sou sa ki pral rive nan atik ki vin apre yo. Nou pral lis tou yon kantite lyen pou etid pwòp tèt ou pou moun ki pa gen dezi a oswa kapasite yo rete tann pou kontinyasyon an.

Entwodiksyon nan BPF Achitekti

Anvan nou kòmanse konsidere achitekti BPF a, nou pral refere yon dènye fwa (oh). BPF klasik, ki te devlope kòm yon repons a avènement de machin RISC ak rezoud pwoblèm nan nan filtraj pake efikas. Achitekti a te vin tèlman siksè ke, li te fèt nan katreventèn yo bèl nan Berkeley UNIX, li te pote nan pifò sistèm opere ki egziste deja, siviv nan ven yo fou epi li toujou jwenn nouvo aplikasyon.

Nouvo BPF a te devlope kòm yon repons a omniprésente nan machin 64-bit, sèvis nwaj ak bezwen an ogmante pou zouti pou kreye SDN (Sofisyel-ddefini ntravay). Devlope pa enjenyè rezo nwayo kòm yon ranplasman amelyore pou BPF klasik la, nouvo BPF la literalman sis mwa pita te jwenn aplikasyon nan travay la difisil nan trase sistèm Linux, epi kounye a, sis ane apre aparans li, nou pral bezwen yon atik antye pwochen jis pou lis diferan kalite pwogram yo.

Foto komik

Nan nwayo li yo, BPF se yon machin vityèl sandbox ki pèmèt ou kouri kòd "abitrè" nan espas nwayo san yo pa konpwomèt sekirite. Pwogram BPF yo kreye nan espas itilizatè, chaje nan nwayo a, epi konekte ak kèk sous evènman. Yon evènman ta ka, pou egzanp, livrezon yon pake nan yon koòdone rezo, lansman kèk fonksyon nwayo, elatriye. Nan ka yon pake, pwogram BPF a pral gen aksè a done yo ak metadata nan pake a (pou lekti ak, pètèt, ekri, tou depann de kalite pwogram nan); nan ka a nan kouri yon fonksyon nwayo, agiman yo nan fonksyon an, ki gen ladan konsèy sou memwa nwayo, elatriye.

Ann pran yon gade pi pre nan pwosesis sa a. Pou kòmanse, ann pale sou premye diferans ak BPF klasik, pwogram yo te ekri nan asanblaj. Nan nouvo vèsyon an, achitekti a te elaji pou pwogram yo te kapab ekri nan lang wo nivo, prensipalman, nan kou, nan C. Pou sa, yo te devlope yon backend pou llvm, ki pèmèt ou jenere bytecode pou achitekti BPF la.

BPF pou ti piti yo, premye pati: BPF pwolonje

Achitekti BPF te fèt, an pati, pou kouri avèk efikasite sou machin modèn yo. Pou fè sa a travay an pratik, BPF bytecode a, yon fwa chaje nan nwayo a, yo tradui nan kòd natif natal lè l sèvi avèk yon eleman ki rele yon du JIT (JUST In Time). Apre sa, si ou sonje, nan klasik BPF pwogram nan te chaje nan nwayo a ak tache ak sous evènman an atomik - nan yon kontèks yon apèl sistèm sèl. Nan nouvo achitekti a, sa rive nan de etap - premye, kòd la chaje nan nwayo a lè l sèvi avèk yon apèl sistèm. bpf(2)epi apre, pita, atravè lòt mekanis ki varye selon kalite pwogram nan, pwogram nan tache ak sous evènman an.

Isit la lektè a ka gen yon kesyon: èske li posib? Ki jan sekirite egzekisyon kòd sa a garanti? Sekirite egzekisyon garanti pou nou pa etap nan chaje pwogram BPF yo rele verifier (an angle etap sa a rele verifier epi mwen pral kontinye sèvi ak mo angle a):

BPF pou ti piti yo, premye pati: BPF pwolonje

Verifier se yon analizè estatik ki asire ke yon pwogram pa deranje operasyon nòmal nwayo a. Sa a, nan chemen an, pa vle di ke pwogram nan pa ka entèfere ak operasyon an nan sistèm nan - pwogram BPF, tou depann de kalite a, ka li ak reekri seksyon nan memwa nwayo, retounen valè nan fonksyon, taye, ajoute, reekri. e menm voye pakè rezo yo. Verifier garanti ke kouri yon pwogram BPF pa pral fè aksidan nwayo a epi ke yon pwogram ki, dapre règleman yo, gen aksè ekri, pou egzanp, done yo nan yon pake sortan, pa pral kapab ranplase memwa nwayo a deyò pake a. Nou pral gade nan verifikatè nan yon ti kras plis detay nan seksyon ki koresponn lan, apre nou fin fè konesans ak tout lòt eleman yo nan BPF.

Kidonk, kisa nou aprann jiskaprezan? Itilizatè a ekri yon pwogram nan C, chaje li nan nwayo a lè l sèvi avèk yon apèl sistèm bpf(2), kote li tcheke pa yon verifikatè ak tradui nan bytecode natif natal. Lè sa a, menm itilizatè a oswa yon lòt konekte pwogram nan sous evènman an epi li kòmanse egzekite. Separe bòt ak koneksyon nesesè pou plizyè rezon. Premyèman, kouri yon verifikatè se relativman chè epi lè w telechaje menm pwogram nan plizyè fwa nou gaspiye tan òdinatè. Dezyèmman, egzakteman ki jan yon pwogram konekte depann sou kalite li yo, ak yon sèl "inivèsèl" koòdone devlope yon ane de sa ka pa apwopriye pou nouvo kalite pwogram. (Malgre ke kounye a ke achitekti a ap vin pi matirite, gen yon lide inifye koòdone sa a nan nivo a. libbpf.)

Lektè atantif la ka remake ke nou poko fini ak foto yo. Vreman vre, tout sa ki anwo yo pa eksplike poukisa BPF fondamantalman chanje foto a konpare ak BPF klasik. De inovasyon ki elaji siyifikativman sijè ki abòde lan aplikab yo se kapasite pou itilize memwa pataje ak fonksyon asistan nwayo. Nan BPF, pataje memwa aplike lè l sèvi avèk sa yo rele kat - estrikti done pataje ak yon API espesifik. Yo pwobableman te resevwa non sa a paske premye kalite kat ki parèt se te yon tab hash. Lè sa a, etalaj te parèt, lokal (pa CPU) tab hash ak etalaj lokal yo, pye bwa rechèch, kat ki gen konsèy sou pwogram BPF ak plis ankò. Ki sa ki enteresan pou nou kounye a se ke pwogram BPF kounye a gen kapasite nan pèsiste eta ant apèl yo epi pataje li ak lòt pwogram ak espas itilizatè.

Maps jwenn aksè nan pwosesis itilizatè yo lè l sèvi avèk yon apèl sistèm bpf(2), ak nan pwogram BPF ki kouri nan nwayo a lè l sèvi avèk fonksyon asistan. Anplis, moun k ap ede yo egziste pa sèlman pou travay ak kat, men tou pou jwenn aksè nan lòt kapasite nwayo. Pa egzanp, pwogram BPF yo ka sèvi ak fonksyon asistan pou voye pakè yo nan lòt koòdone, jenere evènman perf, jwenn aksè nan estrikti nwayo, ak sou sa.

BPF pou ti piti yo, premye pati: BPF pwolonje

An rezime, BPF bay kapasite pou chaje kòd itilizatè abitrè, sa vle di, verifye-teste, nan espas nwayo. Kòd sa a ka sove eta ant apèl yo ak echanj done ak espas itilizatè, epi tou li gen aksè a subsistèm nwayo ki pèmèt sa a kalite pwogram.

Sa a se deja menm jan ak kapasite yo bay nan modil nwayo, konpare ak ki BPF gen kèk avantaj (nan kou, ou ka sèlman konpare aplikasyon ki sanble, pou egzanp, trase sistèm - ou pa ka ekri yon chofè abitrè ak BPF). Ou ka sonje yon papòt antre pi ba (kèk sèvis piblik ki sèvi ak BPF pa mande pou itilizatè a gen ladrès pwogram nwayo, oswa konpetans nan pwogramasyon an jeneral), sekirite nan kouri (leve men ou nan kòmantè yo pou moun ki pa t 'kraze sistèm nan lè ekri. oswa modil tès), atomite - gen tan D 'lè rechaje modil yo, ak subsistèm BPF asire ke pa gen okenn evènman yo rate (yo dwe jis, sa a se pa vre pou tout kalite pwogram BPF).

Prezans nan kapasite sa yo fè BPF yon zouti inivèsèl pou elaji nwayo a, ki se konfime nan pratik: pi plis ak plis nouvo kalite pwogram yo ajoute nan BPF, pi plis ak plis gwo konpayi itilize BPF sou sèvè konba 24 × 7, pi plis ak plis. startups bati biznis yo sou solisyon ki baze sou ki baze sou BPF. BPF yo itilize toupatou: nan pwoteje kont atak DDoS, kreye SDN (pa egzanp, mete ann aplikasyon rezo pou kubernetes), kòm zouti prensipal trase sistèm ak pèseptè estatistik, nan sistèm deteksyon entrizyon ak sistèm sandbox, elatriye.

Ann fini pati apèsi atik la isit la epi gade machin vityèl la ak ekosistèm BPF an plis detay.

Digression: sèvis piblik

Pou kapab kouri egzanp yo nan seksyon sa yo, ou ka bezwen yon kantite sèvis piblik, omwen llvm/clang ak sipò bpf ak bpftool... Nan chapit la Zouti Devlopman Ou ka li enstriksyon yo pou rasanble sèvis piblik yo, osi byen ke nwayo ou a. Seksyon sa a mete anba a pou pa deranje amoni nan prezantasyon nou an.

BPF Virtual Machine Enskri ak Sistèm Enstriksyon

Yo te devlope achitekti ak sistèm lòd BPF pran an kont lefèt ke pwogram yo pral ekri nan lang C a epi, apre yo fin chaje nan nwayo a, yo pral tradui nan kòd natif natal. Se poutèt sa, kantite rejis ak seri kòmandman yo te chwazi ak yon je nan entèseksyon an, nan sans matematik, nan kapasite yo nan machin modèn. Anplis de sa, divès kalite restriksyon yo te enpoze sou pwogram yo, pou egzanp, jiska dènyèman li pa t posib yo ekri bouk ak souroutines, ak kantite enstriksyon yo te limite a 4096 (kounye a pwogram privilejye yo ka chaje jiska yon milyon enstriksyon).

BPF gen onz rejis 64-bit aksesib pou itilizatè r0-r10 ak yon kontwa pwogram. Enskri r10 gen yon konsèy ankadreman epi li sèlman pou li. Pwogram yo gen aksè a yon pil 512-byte nan ègzekutabl ak yon kantite san limit memwa pataje sou fòm kat yo.

Pwogram BPF yo gen dwa kouri yon seri espesifik nan asistan nwayo ki kalite pwogram ak, pi resamman, fonksyon regilye. Chak fonksyon rele ka pran jiska senk agiman, pase nan rejis r1-r5, epi yo pase valè retounen nan r0. Li garanti ke apre retounen soti nan fonksyon an, sa ki nan enskri yo r6-r9 Pa pral chanje.

Pou tradiksyon pwogram efikas, anrejistre r0-r11 pou tout achitekti sipòte yo inikman trase nan rejis reyèl, pran an kont karakteristik yo ABI nan achitekti aktyèl la. Pou egzanp, pou x86_64 anrejistre r1-r5, yo itilize pou pase paramèt fonksyon, yo parèt sou rdi, rsi, rdx, rcx, r8, ki itilize pou pase paramèt fonksyon yo x86_64. Pou egzanp, kòd ki sou bò gòch la tradui nan kòd ki sou bò dwat la tankou sa a:

1:  (b7) r1 = 1                    mov    $0x1,%rdi
2:  (b7) r2 = 2                    mov    $0x2,%rsi
3:  (b7) r3 = 3                    mov    $0x3,%rdx
4:  (b7) r4 = 4                    mov    $0x4,%rcx
5:  (b7) r5 = 5                    mov    $0x5,%r8
6:  (85) call pc+1                 callq  0x0000000000001ee8

Rejis la r0 tou itilize yo retounen rezilta a nan ekzekisyon pwogram, ak nan rejis la r1 se pwogram nan pase yon pwent nan kontèks la - depann sou kalite a nan pwogram, sa a ta ka, pou egzanp, yon estrikti. struct xdp_md (pou XDP) oswa estrikti struct __sk_buff (pou diferan pwogram rezo) oswa estrikti struct pt_regs (pou diferan kalite pwogram tras), elatriye.

Se konsa, nou te gen yon seri rejis, asistan nwayo, yon pil, yon konsèy kontèks ak memwa pataje sou fòm kat. Se pa ke tout bagay sa yo se absoliman nesesè nan vwayaj la, men ...

Ann kontinye deskripsyon an epi pale sou sistèm lòd pou travay ak objè sa yo. Tout (Prèske tout) Enstriksyon BPF yo gen yon gwosè fiks 64-bit. Si ou gade nan yon enstriksyon sou yon machin Big Endian 64-bit ou pral wè

BPF pou ti piti yo, premye pati: BPF pwolonje

Isit la Code - sa a se kodaj enstriksyon an, Dst/Src se kodaj reseptè a ak sous, respektivman, Off - 16-bit siyen endentasyon, ak Imm se yon nonb antye relatif ki siyen 32-bit yo itilize nan kèk enstriksyon (menm jan ak konstan cBPF K). Kodaj Code gen youn nan de kalite:

BPF pou ti piti yo, premye pati: BPF pwolonje

Klas enstriksyon 0, 1, 2, 3 defini kòmandman pou travay ak memwa. Yo yo rele yo, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, respektivman. Klas 4, 7 (BPF_ALU, BPF_ALU64) konstitye yon seri enstriksyon ALU. Klas 5, 6 (BPF_JMP, BPF_JMP32) genyen enstriksyon so.

Pli lwen plan pou etidye sistèm ansèyman BPF a se jan sa a: olye pou nou mete yon lis tout enstriksyon yo ak paramèt yo, nou pral gade yon koup nan egzanp nan seksyon sa a epi nan men yo li pral vin klè ki jan enstriksyon yo aktyèlman ap travay ak ki jan yo. manyèlman demonte nenpòt dosye binè pou BPF. Pou konsolide materyèl la pita nan atik la, nou pral rankontre tou ak enstriksyon endividyèl nan seksyon yo sou Verifier, JIT du, tradiksyon BPF klasik, osi byen ke lè etidye kat, apèl fonksyon, elatriye.

Lè nou pale sou enstriksyon endividyèl, nou pral refere a dosye debaz yo bpf.h и bpf_common.h, ki defini kòd nimerik enstriksyon BPF yo. Lè w ap etidye achitekti poukont ou ak/oswa analiz binè, ou ka jwenn semantik nan sous sa yo, klase nan lòd konpleksite: Espesifikasyon eBPF ki pa ofisyèl, Gid Referans BPF ak XDP, Set Enstriksyon, Dokimantasyon/rezo/filter.txt epi, nan kou, nan kòd sous Linux - verifikatè, JIT, BPF entèprèt.

Egzanp: demonte BPF nan tèt ou

Ann gade nan yon egzanp kote nou konpile yon pwogram readelf-example.c epi gade nan binè ki kapab lakòz. Nou pral revele kontni orijinal la readelf-example.c anba a, apre nou retabli lojik li nan kòd binè:

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

Premye kolòn nan pwodiksyon an readelf se yon endentasyon epi pwogram nou an konsiste de kat kòmandman:

Code Dst Src Off  Imm
b7   0   0   0000 01000000
15   0   1   0100 00000000
b7   0   0   0000 02000000
95   0   0   0000 00000000

Kòd kòmand yo egal b7, 15, b7 и 95. Sonje byen ke twa ti pi piti enpòtan yo se klas enstriksyon an. Nan ka nou an, katriyèm ti moso nan tout enstriksyon yo vid, kidonk klas enstriksyon yo se 7, 5, 7, 5, respektivman. BPF_ALU64, ak 5 se BPF_JMP. Pou tou de klas yo, fòma ansèyman an se menm (gade pi wo a) epi nou ka reekri pwogram nou an konsa (an menm tan an nou pral reekri kolòn ki rete yo nan fòm imen):

Op S  Class   Dst Src Off  Imm
b  0  ALU64   0   0   0    1
1  0  JMP     0   1   1    0
b  0  ALU64   0   0   0    2
9  0  JMP     0   0   0    0

Operasyon b klas ALU64 - Eske BPF_MOV. Li bay yon valè nan rejis destinasyon an. Si bit la mete s (sous), Lè sa a, valè a pran nan rejis sous la, epi si, tankou nan ka nou an, li pa mete, Lè sa a, valè a pran nan jaden an. Imm. Se konsa, nan premye ak twazyèm enstriksyon yo nou fè operasyon an r0 = Imm. Pli lwen, JMP klas 1 operasyon se BPF_JEQ (sote si egal). Nan ka nou an, depi ti jan an S se zewo, li konpare valè rejis sous la ak jaden an Imm. Si valè yo kowenside, Lè sa a, tranzisyon an rive PC + Offkote PC, kòm dabitid, gen adrès pwochen ansèyman an. Finalman, JMP Klas 9 Operasyon se BPF_EXIT. Enstriksyon sa a mete fen nan pwogram nan, retounen nan nwayo a r0. Ann ajoute yon nouvo kolòn nan tablo nou an:

Op    S  Class   Dst Src Off  Imm    Disassm
MOV   0  ALU64   0   0   0    1      r0 = 1
JEQ   0  JMP     0   1   1    0      if (r1 == 0) goto pc+1
MOV   0  ALU64   0   0   0    2      r0 = 2
EXIT  0  JMP     0   0   0    0      exit

Nou ka reekri sa a nan yon fòm ki pi pratik:

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

Si nou sonje sa ki nan rejis la r1 se pwogram nan pase yon konsèy nan kontèks la soti nan nwayo a, ak nan rejis la r0 valè a retounen nan nwayo a, Lè sa a, nou ka wè ke si konsèy la nan kontèks la se zewo, Lè sa a, nou retounen 1, epi otreman - 2. Ann tcheke si nou gen rezon lè nou gade sous la:

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

Wi, se yon pwogram san sans, men li tradwi nan sèlman kat enstriksyon senp.

Eksepsyon egzanp: 16-byte enstriksyon

Nou mansyone pi bonè ke kèk enstriksyon pran plis pase 64 Bits. Sa a aplike, pou egzanp, nan enstriksyon yo lddw (Kòd = 0x18 = BPF_LD | BPF_DW | BPF_IMM) - chaje yon mo doub soti nan jaden yo nan rejis la Imm... Reyalite a se sa Imm gen yon gwosè 32, ak yon mo doub se 64 bit, kidonk chaje yon valè imedya 64-bit nan yon rejis nan yon enstriksyon 64-bit pa pral travay. Pou fè sa, de enstriksyon adjasan yo itilize pou estoke dezyèm pati valè 64-bit la nan jaden an Imm... Egzanp:

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

Gen sèlman de enstriksyon nan yon pwogram binè:

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

Nou pral rankontre ankò ak enstriksyon yo lddw, lè nou pale sou demenajman ak travay ak kat.

Egzanp: demonte BPF lè l sèvi avèk zouti estanda

Se konsa, nou te aprann li kòd binè BPF epi nou pare pou analize nenpòt enstriksyon si sa nesesè. Sepandan, li vo di ke nan pratik li pi pratik ak pi vit demonte pwogram lè l sèvi avèk zouti estanda, pou egzanp:

$ llvm-objdump -d x64.o

Disassembly of section .text:

0000000000000000 <foo>:
 0: 18 00 00 00 dd cc bb aa 00 00 00 00 44 33 22 11 r0 = 1234605617868164317 ll
 2: 95 00 00 00 00 00 00 00 exit

Sik lavi objè BPF, sistèm dosye bpffs

(Mwen te premye aprann kèk nan detay ki dekri nan sou-seksyon sa a nan poste Alexei Starovoitov nan BPF Blog.)

Objè BPF - pwogram ak kat - yo kreye nan espas itilizatè lè l sèvi avèk kòmandman BPF_PROG_LOAD и BPF_MAP_CREATE apèl sistèm bpf(2), nou pral pale sou egzakteman ki jan sa rive nan pwochen seksyon an. Sa a kreye estrikti done nwayo ak pou chak nan yo refcount (referans konte) mete sou youn, epi yon deskriptè fichye ki montre objè a retounen bay itilizatè a. Apre manch lan fèmen refcount objè a redwi pa youn, epi lè li rive nan zewo, objè a detwi.

Si pwogram nan itilize kat, lè sa a refcount kat sa yo ogmante pa youn apre yo fin chaje pwogram nan, sa vle di. deskriptè dosye yo ka fèmen nan pwosesis itilizatè a epi toujou refcount p ap vin zewo:

BPF pou ti piti yo, premye pati: BPF pwolonje

Apre w fin chaje yon pwogram avèk siksè, anjeneral nou tache li nan kèk kalite dèlko evènman. Pou egzanp, nou ka mete l 'sou yon koòdone rezo a trete pake fèk ap rantre oswa konekte li nan kèk tracepoint nan nwayo a. Nan pwen sa a, kontwa referans lan ap ogmante tou pa youn epi nou pral kapab fèmen deskriptè dosye a nan pwogram loader a.

Kisa k ap pase si kounye a nou fèmen bootloader la? Sa depann de ki kalite dèlko evènman (kwòk). Tout kwòk rezo ap egziste apre loader a fini, sa yo se sa yo rele kwòk mondyal yo. Epi, pou egzanp, pwogram tras yo pral lage apre pwosesis ki te kreye yo fini (ak Se poutèt sa yo rele lokal, soti nan "lokal nan pwosesis la"). Teknikman, kwòk lokal yo toujou gen yon deskriptè fichye ki koresponn nan espas itilizatè yo e se poutèt sa fèmen lè pwosesis la fèmen, men kwòk mondyal yo pa fè sa. Nan figi sa a, lè l sèvi avèk kwa wouj, mwen eseye montre ki jan revokasyon an nan pwogram nan loader afekte lavi a nan objè nan ka kwòk lokal ak mondyal.

BPF pou ti piti yo, premye pati: BPF pwolonje

Poukisa gen yon distenksyon ant kwòk lokal ak mondyal? Kouri kèk kalite pwogram rezo fè sans san yon espas itilizatè, pou egzanp, imajine pwoteksyon DDoS - bootloader a ekri règ yo epi konekte pwogram BPF a nan koòdone rezo a, apre sa bootloader a ka ale epi touye tèt li. Nan lòt men an, imajine yon pwogram tras debogaj ke ou te ekri sou jenou ou nan dis minit - lè li fini, ou ta renmen pa gen okenn fatra kite nan sistèm nan, ak zen lokal yo pral asire sa.

Nan lòt men an, imajine ke ou vle konekte ak yon tracepoint nan nwayo a epi kolekte estatistik sou plizyè ane. Nan ka sa a, ou ta vle ranpli pati itilizatè a epi retounen nan estatistik yo de tan zan tan. Sistèm fichye bpf bay opòtinite sa a. Li se yon sistèm pseudo-fichye ki nan memwa sèlman ki pèmèt kreyasyon fichye ki fè referans ak objè BPF epi kidonk ogmante. refcount objè yo. Apre sa, loader a ka sòti, ak objè yo li te kreye yo ap rete vivan.

BPF pou ti piti yo, premye pati: BPF pwolonje

Kreye fichye nan bpffs ki fè referans a objè BPF yo rele "pinning" (tankou nan fraz sa a: "pwosesis ka pin yon pwogram BPF oswa kat"). Kreye objè fichye pou objè BPF fè sans non sèlman pou pwolonje lavi objè lokal yo, men tou pou itilizasyon objè mondyal yo - retounen nan egzanp lan ak pwogram pwoteksyon DDoS mondyal la, nou vle kapab vin gade estatistik. de tanzantan.

Se sistèm nan dosye BPF anjeneral monte nan /sys/fs/bpf, men li kapab tou monte lokalman, pou egzanp, tankou sa a:

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

Non sistèm dosye yo kreye lè l sèvi avèk kòmandman an BPF_OBJ_PIN Rele sistèm BPF. Pou ilistre, ann pran yon pwogram, konpile li, telechaje li, epi pin li nan bpffs. Pwogram nou an pa fè anyen ki itil, nou sèlman prezante kòd la pou ou ka repwodui egzanp lan:

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

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

Ann konpile pwogram sa a epi kreye yon kopi lokal sistèm dosye a bpffs:

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

Koulye a, ann telechaje pwogram nou an lè l sèvi avèk sèvis piblik la bpftool epi gade apèl sistèm ki akonpaye yo bpf(2) (kèk liy petinan retire nan pwodiksyon strace):

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

Isit la nou te chaje pwogram nan lè l sèvi avèk BPF_PROG_LOAD, te resevwa yon deskriptè fichye ki soti nan nwayo a 3 epi itilize kòmandman an BPF_OBJ_PIN fikse deskriptè fichye sa a kòm yon fichye "bpf-mountpoint/test". Apre sa, pwogram bootloader la bpftool fini travay, men pwogram nou an rete nan nwayo a, byenke nou pa tache li nan okenn koòdone rezo:

$ sudo bpftool prog | tail -3
783: xdp  name test  tag 5c8ba0cf164cb46c  gpl
        loaded_at 2020-05-05T13:27:08+0000  uid 0
        xlated 24B  jited 41B  memlock 4096B

Nou ka efase objè a fichye nòmalman unlink(2) epi apre sa yo pral efase pwogram ki koresponn lan:

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

Efase objè yo

Pale sou efase objè, li nesesè klarifye ke apre nou te dekonekte pwogram nan nan zen an (dèlko evènman), pa yon sèl nouvo evènman pral deklanche lansman li yo, sepandan, tout sikonstans aktyèl nan pwogram nan pral fini nan lòd nòmal la. .

Gen kèk kalite pwogram BPF pèmèt ou ranplase pwogram nan sou vole, i.e. bay atomite sekans replace = detach old program, attach new program. Nan ka sa a, tout sikonstans aktif nan vèsyon an ansyen nan pwogram nan pral fini travay yo, ak nouvo moun kap okipe evènman yo pral kreye nan nouvo pwogram nan, ak "atomisite" isit la vle di ke pa pral yon sèl evènman rate.

Tache pwogram nan sous evènman yo

Nan atik sa a, nou pa pral dekri separeman konekte pwogram ak sous evènman, paske li fè sans pou etidye sa a nan yon kontèks yon kalite pwogram espesifik. Cm. egzanp anba a, kote nou montre kouman pwogram tankou XDP yo konekte.

Manipile objè lè l sèvi avèk apèl sistèm bpf la

Pwogram BPF

Tout objè BPF yo kreye ak jere nan espas itilizatè lè l sèvi avèk yon apèl sistèm bpf, ki gen pwototip sa a:

#include <linux/bpf.h>

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

Men ekip la cmd se youn nan valè yo nan kalite enum bpf_cmd, attr — yon konsèy sou paramèt pou yon pwogram espesifik ak size - gwosè objè dapre konsèy la, i.e. anjeneral sa a sizeof(*attr). Nan du 5.8 apèl sistèm lan bpf sipòte 34 kòmandman diferan, ak definisyon union bpf_attr okipe 200 liy. Men, nou pa ta dwe entimide pa sa a, depi nou pral familyarize tèt nou ak kòmandman yo ak paramèt sou kou a nan plizyè atik.

Ann kòmanse ak ekip la BPF_PROG_LOAD, ki kreye pwogram BPF - pran yon seri enstriksyon BPF epi chaje li nan nwayo a. Nan moman sa a nan loading, verifikatè a te lanse, ak Lè sa a, konpilatè JIT la epi, apre ekzekisyon siksè, deskriptè dosye pwogram nan retounen bay itilizatè a. Nou te wè sa k ap pase l apre nan seksyon anvan an sou sik lavi objè BPF yo.

Nou pral kounye a ekri yon pwogram koutim ki pral chaje yon pwogram BPF senp, men anvan nou bezwen deside ki kalite pwogram nou vle chaje - nou pral oblije chwazi di ki kalite ak nan kad sa a kalite, ekri yon pwogram ki pral pase tès la verifye. Sepandan, yo nan lòd yo pa konplike pwosesis la, isit la se yon solisyon pare-fè: nou pral pran yon pwogram tankou BPF_PROG_TYPE_XDP, ki pral retounen valè a XDP_PASS (sote tout pakè). Nan BPF assembler li sanble trè senp:

r0 = 2
exit

Apre nou te deside sou ki nou pral telechaje, nou ka di ou ki jan nou pral fè li:

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

Evènman enteresan nan yon pwogram kòmanse ak definisyon yon etalaj insns - pwogram BPF nou an nan kòd machin. Nan ka sa a, chak enstriksyon nan pwogram BPF chaje nan estrikti a bpf_insn. Premye eleman insns konfòme ak enstriksyon yo r0 = 2, dezyèm lan - exit.

Retrè. Kernel la defini makro ki pi pratik pou ekri kòd machin yo, epi itilize dosye header nwayo a tools/include/linux/filter.h nou te kapab ekri

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

Men, depi ekri pwogram BPF nan kòd natif natal sèlman nesesè pou ekri tès nan nwayo a ak atik sou BPF, absans makro sa yo pa vrèman konplike lavi pwomotè a.

Apre nou fin defini pwogram BPF a, nou ale nan chaje li nan nwayo a. Seri minimalist nou an nan paramèt attr gen ladan kalite pwogram, seri ak kantite enstriksyon, lisans obligatwa, ak non "woo", ke nou itilize pou jwenn pwogram nou an sou sistèm nan apre telechaje. Pwogram nan, jan yo te pwomèt la, chaje nan sistèm nan lè l sèvi avèk yon apèl sistèm bpf.

Nan fen pwogram nan nou fini nan yon bouk enfini ki simulation chaj la. San li, pwogram nan pral touye pa nwayo a lè deskriptè fichye ke apèl sistèm lan retounen ba nou fèmen. bpf, epi nou pa pral wè li nan sistèm nan.

Oke, nou pare pou tès la. Ann rasanble epi kouri pwogram nan anba stracepou tcheke si tout bagay ap fonksyone jan li ta dwe:

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

Tout bagay anfòm, bpf(2) te retounen manch 3 pou nou epi nou te antre nan yon bouk enfini ak pause(). Ann eseye jwenn pwogram nou an nan sistèm nan. Pou fè sa, nou pral ale nan yon lòt tèminal epi sèvi ak sèvis piblik la 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)

Nou wè ke gen yon pwogram chaje sou sistèm nan woo ki gen idantite mondyal se 390 epi li se kounye a nan pwogrè simple-prog gen yon deskriptè dosye louvri ki montre pwogram nan (e si simple-prog pral fini travay la, lè sa a woo pral disparèt). Kòm espere, pwogram nan woo pran 16 bytes - de enstriksyon - nan kòd binè nan achitekti BPF a, men nan fòm natif natal li (x86_64) li deja 40 bytes. Ann gade pwogram nou an nan fòm orijinal li:

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

pa gen sipriz. Koulye a, ann gade nan kòd ki te pwodwi pa du JIT la:

# bpftool prog dump jited id 390
bpf_prog_3b185187f1855c4c_woo:
   0:   nopl   0x0(%rax,%rax,1)
   5:   push   %rbp
   6:   mov    %rsp,%rbp
   9:   sub    $0x0,%rsp
  10:   push   %rbx
  11:   push   %r13
  13:   push   %r14
  15:   push   %r15
  17:   pushq  $0x0
  19:   mov    $0x2,%eax
  1e:   pop    %rbx
  1f:   pop    %r15
  21:   pop    %r14
  23:   pop    %r13
  25:   pop    %rbx
  26:   leaveq
  27:   retq

pa trè efikas pou exit(2), men ann jistis, pwogram nou an twò senp, epi pou pwogram ki pa trivial, prològ ak epilòg ki te ajoute pa du JIT la, nan kou, nesesè.

Kat

Pwogram BPF ka itilize zòn memwa estriktire ki aksesib tou de lòt pwogram BPF ak pwogram nan espas itilizatè. Objè sa yo rele kat epi nan seksyon sa a nou pral montre kijan pou manipile yo lè l sèvi avèk yon apèl sistèm bpf.

Ann di touswit ke kapasite kat yo pa limite sèlman ak aksè nan memwa pataje. Gen kat objektif espesyal ki genyen, pou egzanp, konsèy sou pwogram BPF oswa endikasyon sou koòdone rezo, kat pou travay ak evènman perf, elatriye. Nou pa pral pale sou yo isit la, pou yo pa konfonn lektè a. Apa de sa, nou inyore pwoblèm senkronizasyon, paske sa a pa enpòtan pou egzanp nou yo. Ou ka jwenn yon lis konplè kalite kat ki disponib nan <linux/bpf.h>, ak nan seksyon sa a nou pral pran kòm yon egzanp istorikman premye kalite a, tab la hash BPF_MAP_TYPE_HASH.

Si ou kreye yon tab hash nan, di, C++, ou ta di unordered_map<int,long> woo, ki nan Ris vle di "Mwen bezwen yon tab woo gwosè san limit, ki gen kle yo nan kalite int, ak valè yo se kalite a long" Yo nan lòd yo kreye yon tab hash BPF, nou bezwen fè anpil menm bagay la, eksepte ke nou dwe presize gwosè maksimòm tab la, epi olye pou yo presize kalite kle ak valè, nou bezwen presize gwosè yo an byte. . Pou kreye kat itilize kòmandman an BPF_MAP_CREATE apèl sistèm bpf. Ann gade yon pwogram plis oswa mwens minim ki kreye yon kat. Apre pwogram anvan an ki chaje pwogram BPF, sa a ta dwe sanble senp pou ou:

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

Isit la nou defini yon seri paramèt attr, nan ki nou di "Mwen bezwen yon tab hash ak kle ak valè gwosè sizeof(int), nan ki mwen ka mete yon maksimòm de kat eleman." Lè w ap kreye kat BPF, ou ka presize lòt paramèt, pou egzanp, nan menm fason an kòm nan egzanp lan ak pwogram nan, nou espesifye non an nan objè a kòm "woo".

Ann konpile epi kouri pwogram nan:

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

Men apèl sistèm lan bpf(2) te retounen nou nimewo kat deskriptè a 3 ak Lè sa a, pwogram nan, jan yo espere, ap tann pou plis enstriksyon nan apèl la sistèm pause(2).

Koulye a, ann voye pwogram nou an nan background nan oswa louvri yon lòt tèminal epi gade nan objè nou an lè l sèvi avèk sèvis piblik la bpftool (nou ka distenge kat nou an de lòt moun pa non li):

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

Nimewo 114 se ID mondyal objè nou an. Nenpòt pwogram sou sistèm nan ka itilize ID sa a pou ouvri yon kat ki deja egziste lè l sèvi avèk kòmandman an BPF_MAP_GET_FD_BY_ID apèl sistèm bpf.

Koulye a, nou ka jwe ak tab hash nou an. Ann gade nan kontni li yo:

$ sudo bpftool map dump id 114
Found 0 elements

Vide. Ann mete yon valè ladan l hash[1] = 1:

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

Ann gade tab la ankò:

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

Houra! Nou jere ajoute yon sèl eleman. Remake byen ke nou dwe travay nan nivo byte pou fè sa, depi bptftool pa konnen ki kalite valè yo nan tablo hash la. (Konesans sa a ka transfere ba li lè l sèvi avèk BTF, men plis sou sa kounye a.)

Ki jan egzakteman bpftool li epi ajoute eleman? Ann pran yon gade anba kapo a:

$ sudo strace -e bpf bpftool map dump id 114
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=NULL, next_key=0x55856ab65280}, 120) = 0
bpf(BPF_MAP_LOOKUP_ELEM, {map_fd=3, key=0x55856ab65280, value=0x55856ab652a0}, 120) = 0
key: 01 00 00 00  value: 01 00 00 00
bpf(BPF_MAP_GET_NEXT_KEY, {map_fd=3, key=0x55856ab65280, next_key=0x55856ab65280}, 120) = -1 ENOENT

Premye nou te louvri kat la pa ID mondyal li yo lè l sèvi avèk kòmandman an BPF_MAP_GET_FD_BY_ID и bpf(2) te retounen nou deskriptè 3. Plis itilize kòmandman an BPF_MAP_GET_NEXT_KEY nou jwenn premye kle nan tablo a pa pase NULL kòm yon konsèy sou "previous" kle a. Si nou gen kle a nou ka fè BPF_MAP_LOOKUP_ELEMki retounen yon valè nan yon konsèy value. Pwochen etap la se nou eseye jwenn pwochen eleman nan pase yon konsèy sou kle aktyèl la, men tab nou an gen sèlman yon eleman ak kòmandman an. BPF_MAP_GET_NEXT_KEY retounen ENOENT.

Oke, ann chanje valè a pa kle 1, ann di lojik biznis nou an mande pou enskri. hash[1] = 2:

$ sudo strace -e bpf bpftool map update id 114 key 1 0 0 0 value 2 0 0 0
bpf(BPF_MAP_GET_FD_BY_ID, {map_id=114, next_id=0, open_flags=0}, 120) = 3
bpf(BPF_MAP_UPDATE_ELEM, {map_fd=3, key=0x55dcd72be260, value=0x55dcd72be280, flags=BPF_ANY}, 120) = 0

Kòm espere, li trè senp: lòd la BPF_MAP_GET_FD_BY_ID ouvè kat nou an pa ID, ak lòd la BPF_MAP_UPDATE_ELEM ranplase eleman an.

Se konsa, apre yo fin kreye yon tab hash soti nan yon pwogram, nou ka li ak ekri sa ki nan yon lòt. Remake byen ke si nou te kapab fè sa nan liy lòd la, Lè sa a, nenpòt lòt pwogram sou sistèm nan ka fè li. Anplis kòmandman ki dekri pi wo a, pou travay ak kat ki soti nan espas itilizatè, Sa ki anba la:

  • BPF_MAP_LOOKUP_ELEM: jwenn valè pa kle
  • BPF_MAP_UPDATE_ELEM: mete ajou/kreye valè
  • BPF_MAP_DELETE_ELEM: retire kle
  • BPF_MAP_GET_NEXT_KEY: jwenn pwochen (oswa premye) kle a
  • BPF_MAP_GET_NEXT_ID: pèmèt ou ale nan tout kat ki egziste deja, se konsa li fonksyone bpftool map
  • BPF_MAP_GET_FD_BY_ID: louvri yon kat ki deja egziste pa ID mondyal li yo
  • BPF_MAP_LOOKUP_AND_DELETE_ELEM: atomik mete ajou valè yon objè epi retounen ansyen an
  • BPF_MAP_FREEZE: fè kat la imuiabl nan espas itilizatè (operasyon sa a pa ka defèt)
  • BPF_MAP_LOOKUP_BATCH, BPF_MAP_LOOKUP_AND_DELETE_BATCH, BPF_MAP_UPDATE_BATCH, BPF_MAP_DELETE_BATCH: operasyon an mas. Pa egzanp, BPF_MAP_LOOKUP_AND_DELETE_BATCH - sa a se sèl fason serye pou li ak Reyajiste tout valè nan kat la

Se pa tout kòmandman sa yo travay pou tout kalite kat, men an jeneral travay ak lòt kalite kat ki soti nan espas itilizatè sanble egzakteman menm jan ak travay ak tab hash.

Pou dedomajman pou lòd, an n fini eksperyans tab hash nou yo. Sonje ke nou te kreye yon tab ki ka genyen jiska kat kle? Ann ajoute kèk eleman plis:

$ sudo bpftool map update id 114 key 2 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 3 0 0 0 value 1 0 0 0
$ sudo bpftool map update id 114 key 4 0 0 0 value 1 0 0 0

Jiskaprezan byen:

$ sudo bpftool map dump id 114
key: 01 00 00 00  value: 01 00 00 00
key: 02 00 00 00  value: 01 00 00 00
key: 04 00 00 00  value: 01 00 00 00
key: 03 00 00 00  value: 01 00 00 00
Found 4 elements

Ann eseye ajoute yon lòt:

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

Kòm espere, nou pa t reyisi. Ann gade erè a an plis detay:

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

Tout bagay anfòm: jan yo espere, ekip la BPF_MAP_UPDATE_ELEM eseye kreye yon nouvo, senkyèm, kle, men aksidan E2BIG.

Se konsa, nou ka kreye ak chaje pwogram BPF, osi byen ke kreye ak jere kat soti nan espas itilizatè. Koulye a, li lojik gade nan ki jan nou ka itilize kat nan pwogram yo BPF tèt yo. Nou ta ka pale sou sa a nan lang pwogram ki difisil pou li nan kòd makro machin, men an reyalite lè a rive pou montre kouman pwogram BPF yo aktyèlman ekri ak konsève - lè l sèvi avèk libbpf.

(Pou lektè ki pa satisfè ak mank de yon egzanp ki ba: nou pral analize an detay pwogram ki sèvi ak kat ak fonksyon asistan ki kreye lè l sèvi avèk libbpf epi di w sa k ap pase nan nivo enstriksyon an. Pou lektè ki pa satisfè anpil, nou te ajoute egzanp nan plas ki apwopriye nan atik la.)

Ekri pwogram BPF lè l sèvi avèk libbpf

Ekri pwogram BPF lè l sèvi avèk kòd machin yo ka enteresan sèlman premye fwa a, ak Lè sa a, sasyete mete nan. Nan moman sa a ou bezwen vire atansyon ou a llvm, ki gen yon backend pou jenere kòd pou achitekti BPF, osi byen ke yon bibliyotèk libbpf, ki pèmèt ou ekri bò itilizatè aplikasyon BPF epi chaje kòd pwogram BPF ki te pwodwi lè l sèvi avèk yo llvm/clang.

An reyalite, jan nou pral wè nan atik sa a ak atik ki vin apre yo, libbpf fè anpil travay san li (oswa zouti menm jan an - iproute2, libbcc, libbpf-go, elatriye) li enposib pou viv. Youn nan karakteristik yo ki asasen nan pwojè a libbpf se BPF CO-RE (Konpile yon fwa, kouri toupatou) - yon pwojè ki pèmèt ou ekri pwogram BPF ki pòtab soti nan yon nwayo nan yon lòt, ak kapasite nan kouri sou diferan APIs (pa egzanp, lè estrikti nwayo a chanje soti nan vèsyon an). nan vèsyon). Pou kapab travay ak CO-RE, nwayo ou a dwe konpile ak sipò BTF (nou dekri kijan pou fè sa nan seksyon an. Zouti Devlopman. Ou ka tcheke si nwayo ou a bati ak BTF oswa ou pa trè tou senpleman - pa prezans nan dosye sa a:

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

Fichye sa a estoke enfòmasyon sou tout kalite done yo itilize nan nwayo a epi yo itilize nan tout egzanp nou yo lè l sèvi avèk yo libbpf. Nou pral pale an detay sou CO-RE nan pwochen atik la, men nan yon sèl sa a - jis bati tèt ou yon nwayo ak CONFIG_DEBUG_INFO_BTF.

bibliyotèk libbpf ap viv dwat nan anyè a tools/lib/bpf Kernel ak devlopman li fèt atravè lis adrès la [email protected]. Sepandan, yo kenbe yon depo separe pou bezwen aplikasyon k ap viv deyò nwayo a https://github.com/libbpf/libbpf nan ki bibliyotèk nwayo a reflete pou aksè li plis oswa mwens jan li ye.

Nan seksyon sa a nou pral gade ki jan ou ka kreye yon pwojè ki itilize libbpf, ann ekri plizyè pwogram tès (plis oswa mwens san sans) epi analize an detay kijan tout bagay mache. Sa a pral pèmèt nou pi fasil eksplike nan seksyon sa yo egzakteman ki jan pwogram BPF kominike avèk kat, asistan nwayo, BTF, elatriye.

Tipikman pwojè lè l sèvi avèk libbpf ajoute yon depo GitHub kòm yon submodule git, nou pral fè menm bagay la:

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

Ale nan libbpf trè senp:

$ cd libbpf/src
$ mkdir build
$ OBJDIR=build DESTDIR=root make -s install
$ find root
root
root/usr
root/usr/include
root/usr/include/bpf
root/usr/include/bpf/bpf_tracing.h
root/usr/include/bpf/xsk.h
root/usr/include/bpf/libbpf_common.h
root/usr/include/bpf/bpf_endian.h
root/usr/include/bpf/bpf_helpers.h
root/usr/include/bpf/btf.h
root/usr/include/bpf/bpf_helper_defs.h
root/usr/include/bpf/bpf.h
root/usr/include/bpf/libbpf_util.h
root/usr/include/bpf/libbpf.h
root/usr/include/bpf/bpf_core_read.h
root/usr/lib64
root/usr/lib64/libbpf.so.0.1.0
root/usr/lib64/libbpf.so.0
root/usr/lib64/libbpf.a
root/usr/lib64/libbpf.so
root/usr/lib64/pkgconfig
root/usr/lib64/pkgconfig/libbpf.pc

Pwochen plan nou an nan seksyon sa a se jan sa a: nou pral ekri yon pwogram BPF tankou BPF_PROG_TYPE_XDP, menm jan ak nan egzanp anvan an, men nan C, nou konpile li lè l sèvi avèk clang, epi ekri yon pwogram asistan ki pral chaje li nan nwayo a. Nan seksyon sa yo nou pral elaji kapasite pwogram BPF ak pwogram asistan an.

Egzanp: kreye yon aplikasyon konplè lè l sèvi avèk libbpf

Pou kòmanse, nou itilize dosye a /sys/kernel/btf/vmlinux, ki te mansyone pi wo a, epi kreye ekivalan li yo nan fòm yon dosye header:

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

Fichye sa a pral estoke tout estrikti done ki disponib nan nwayo nou an, pou egzanp, men ki jan header IPv4 la defini nan nwayo a:

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

Koulye a, nou pral ekri pwogram BPF nou an nan C:

$ cat xdp-simple.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

SEC("xdp/simple")
int simple(void *ctx)
{
        return XDP_PASS;
}

char LICENSE[] SEC("license") = "GPL";

Malgre ke pwogram nou an te tounen trè senp, nou toujou bezwen peye atansyon sou anpil detay. Premyèman, premye dosye header nou enkli se vmlinux.h, ke nou jis pwodwi lè l sèvi avèk bpftool btf dump - kounye a nou pa bezwen enstale pake kernel-headers pou chèche konnen ki jan estrikti nwayo yo sanble. Fichye header sa a vin jwenn nou nan bibliyotèk la libbpf. Koulye a, nou sèlman bezwen li defini makro a SEC, ki voye karaktè a nan seksyon apwopriye nan fichye objè ELF la. Pwogram nou an genyen nan seksyon an xdp/simple, kote anvan koupe a nou defini kalite pwogram BPF - sa a se konvansyon yo itilize nan libbpf, ki baze sou non an seksyon li pral ranplase kalite ki kòrèk la nan demaraj bpf(2). Pwogram BPF nan tèt li se C - trè senp epi li konsiste de yon liy return XDP_PASS. Finalman, yon seksyon separe "license" gen non lisans lan.

Nou ka konpile pwogram nou an lè l sèvi avèk llvm/clang, vèsyon >= 10.0.0, oswa pi bon toujou, pi gwo (gade seksyon Zouti Devlopman):

$ clang --version
clang version 11.0.0 (https://github.com/llvm/llvm-project.git afc287e0abec710398465ee1f86237513f2b5091)
...

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o

Pami karakteristik yo ki enteresan: nou endike achitekti sib la -target bpf ak chemen an nan tèt yo libbpf, ke nou fèk enstale. Epitou, pa bliye sou -O2, san opsyon sa a ou ka nan pou sipriz nan tan kap vini an. Ann gade kòd nou an, èske nou te rive ekri pwogram nou te vle a?

$ llvm-objdump --section=xdp/simple --no-show-raw-insn -D xdp-simple.bpf.o

xdp-simple.bpf.o:       file format elf64-bpf

Disassembly of section xdp/simple:

0000000000000000 <simple>:
       0:       r0 = 2
       1:       exit

Wi, li te travay! Koulye a, nou gen yon dosye binè ak pwogram nan, epi nou vle kreye yon aplikasyon ki pral chaje li nan nwayo a. Pou rezon sa a bibliyotèk la libbpf ofri nou de opsyon - sèvi ak yon API ki pi ba oswa yon API ki pi wo. Nou pral ale nan dezyèm wout la, depi nou vle aprann ki jan yo ekri, chaje ak konekte pwogram BPF ak efò minim pou etid ki vin apre yo.

Premyèman, nou bezwen jenere "skelèt" pwogram nou an soti nan binè li yo lè l sèvi avèk menm sèvis piblik la bpftool - kouto Swis nan mond BPF (ki ka pran literalman, depi Daniel Borkman, youn nan kreyatè ak mentnitè BPF, se Swis):

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

Nan dosye xdp-simple.skel.h gen kòd binè pwogram nou an ak fonksyon pou jere - chaje, atache, efase objè nou an. Nan ka senp nou an, sa sanble twòp, men li travay tou nan ka kote fichye objè a gen anpil pwogram BPF ak kat epi pou chaje ELF jeyan sa a nou jis bezwen jenere kilè eskèlèt la epi rele youn oubyen de fonksyon nan aplikasyon koutim nou an. ap ekri Ann avanse kounye a.

Fè egzateman pale, pwogram loader nou an se trivial:

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

Isit la struct xdp_simple_bpf defini nan dosye a xdp-simple.skel.h epi dekri dosye objè nou an:

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

Nou ka wè tras nan yon API ba-nivo isit la: estrikti a struct bpf_program *simple и struct bpf_link *simple. Premye estrikti a espesyalman dekri pwogram nou an, ki ekri nan seksyon an xdp/simple, ak dezyèm lan dekri kijan pwogram nan konekte ak sous evènman an.

Fonksyon xdp_simple_bpf__open_and_load, ouvri yon objè ELF, analize li, kreye tout estrikti ak substruktur yo (anplis pwogram nan, ELF gen ladan tou lòt seksyon - done, done lekti sèlman, enfòmasyon debogaj, lisans, elatriye), ak Lè sa a, chaje li nan nwayo a lè l sèvi avèk yon sistèm. rele bpf, ke nou ka tcheke nan konpile ak kouri pwogram nan:

$ clang -O2 -I ./libbpf/src/root/usr/include/ xdp-simple.c -o xdp-simple ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz

$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_BTF_LOAD, 0x7ffdb8fd9670, 120)  = 3
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, insn_cnt=2, insns=0xdfd580, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 8, 0), prog_flags=0, prog_name="simple", prog_ifindex=0, expected_attach_type=0x25 /* BPF_??? */, ...}, 120) = 4

Ann gade kounye a nan pwogram nou an lè l sèvi avèk bpftool. Ann jwenn idantite li:

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

ak pil fatra (nou itilize yon fòm ki pi kout nan lòd la bpftool prog dump xlated):

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

Yon bagay nouvo! Pwogram nan enprime moso nan fichye sous C nou an. Se bibliyotèk la ki te fè sa libbpf, ki te jwenn seksyon debug la nan binè a, konpile li nan yon objè BTF, chaje li nan nwayo a lè l sèvi avèk BPF_BTF_LOAD, ak Lè sa a, espesifye deskriptè dosye a ki kapab lakòz lè w ap chaje pwogram nan ak lòd la BPG_PROG_LOAD.

Èd Kernel

Pwogram BPF ka kouri fonksyon "ekstèn" - kernel helpers. Fonksyon asistan sa yo pèmèt pwogram BPF jwenn aksè nan estrikti nwayo, jere kat, epi tou kominike ak "monn reyèl" - kreye evènman perf, kontwole pyès ki nan konpitè (pa egzanp, redireksyon pake), elatriye.

Egzanp: bpf_get_smp_processor_id

Nan kad paradigm "aprann pa egzanp", ann konsidere youn nan fonksyon èd yo, bpf_get_smp_processor_id(), sèten nan dosye kernel/bpf/helpers.c. Li retounen nimewo processeur kote pwogram BPF ki rele l ap kouri. Men, nou pa menm enterese nan semantik li yo tankou nan lefèt ke aplikasyon li pran yon liy:

BPF_CALL_0(bpf_get_smp_processor_id)
{
    return smp_processor_id();
}

Definisyon fonksyon asistan BPF yo sanble ak definisyon apèl sistèm Linux yo. Isit la, pou egzanp, yo defini yon fonksyon ki pa gen okenn agiman. (Yon fonksyon ki pran, di, twa agiman defini lè l sèvi avèk makro a BPF_CALL_3. Kantite maksimòm agiman se senk.) Sepandan, sa a se sèlman premye pati nan definisyon an. Dezyèm pati a se defini estrikti nan kalite struct bpf_func_proto, ki gen yon deskripsyon fonksyon asistan ke verifikatè konprann:

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

Anrejistre Fonksyon Èd

Pou pwogram BPF nan yon kalite patikilye yo sèvi ak fonksyon sa a, yo dwe anrejistre li, pou egzanp pou kalite a BPF_PROG_TYPE_XDP se yon fonksyon defini nan nwayo a xdp_func_proto, ki detèmine nan ID fonksyon asistan si XDP sipòte fonksyon sa a oswa ou pa. Fonksyon nou se sipò:

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

Nouvo kalite pwogram BPF yo "defini" nan dosye a include/linux/bpf_types.h lè l sèvi avèk yon macro BPF_PROG_TYPE. Defini nan quotes paske li se yon definisyon lojik, ak nan tèm lang C definisyon an nan yon seri antye nan estrikti konkrè rive nan lòt kote. An patikilye, nan dosye a kernel/bpf/verifier.c tout definisyon nan dosye bpf_types.h yo itilize pou kreye yon seri estrikti 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
};

Sa vle di, pou chak kalite pwogram BPF, yo defini yon konsèy sou yon estrikti done nan kalite a struct bpf_verifier_ops, ki inisyalize ak valè a _name ## _verifier_ops, sa vle di, xdp_verifier_ops pou xdp. Estrikti xdp_verifier_ops detèmine pa nan dosye net/core/filter.c jan sa a:

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

Isit la nou wè fonksyon abitye nou an xdp_func_proto, ki pral kouri verifikatè a chak fwa li rankontre yon defi kèk fonksyon andedan yon pwogram BPF, gade verifier.c.

Ann gade kijan yon pwogram BPF ipotetik itilize fonksyon an bpf_get_smp_processor_id. Pou fè sa, nou reekri pwogram nan nan seksyon anvan nou an jan sa a:

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

Senbòl bpf_get_smp_processor_id detèmine pa в <bpf/bpf_helper_defs.h> bibliyotèk yo libbpf kòm

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

sa vle di bpf_get_smp_processor_id se yon pwent fonksyon ki gen valè 8, kote 8 se valè a BPF_FUNC_get_smp_processor_id kalite enum bpf_fun_id, ki defini pou nou nan dosye a vmlinux.h (dosye bpf_helper_defs.h nan nwayo a se yon script ki te pwodwi, kidonk nimewo "majik" yo ok). Fonksyon sa a pa pran okenn agiman epi li retounen yon valè kalite __u32. Lè nou kouri li nan pwogram nou an, clang jenere yon enstriksyon BPF_CALL "bon kalite" Ann konpile pwogram nan epi gade seksyon an xdp/simple:

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ llvm-objdump -D --section=xdp/simple xdp-simple.bpf.o

xdp-simple.bpf.o:       file format elf64-bpf

Disassembly of section xdp/simple:

0000000000000000 <simple>:
       0:       85 00 00 00 08 00 00 00 call 8
       1:       bf 01 00 00 00 00 00 00 r1 = r0
       2:       67 01 00 00 20 00 00 00 r1 <<= 32
       3:       77 01 00 00 20 00 00 00 r1 >>= 32
       4:       b7 00 00 00 02 00 00 00 r0 = 2
       5:       15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_2>
       6:       b7 00 00 00 01 00 00 00 r0 = 1

0000000000000038 <LBB0_2>:
       7:       95 00 00 00 00 00 00 00 exit

Nan premye liy lan nou wè enstriksyon yo call, paramèt IMM ki egal a 8, epi SRC_REG - zewo. Dapre akò ABI ki itilize pa verifikatè a, sa a se yon apèl pou fonksyon asistan nimewo uit. Yon fwa li te lanse, lojik la se senp. Retounen valè nan enskri r0 kopye nan r1 epi sou liy 2,3 li konvèti an tip u32 - 32 bit yo anwo yo otorize. Sou liy 4,5,6,7 nou retounen 2 (XDP_PASS) oswa 1 (XDP_DROP) selon si fonksyon asistan ki soti nan liy 0 a te retounen yon valè zewo oswa ki pa zewo.

Ann teste tèt nou: chaje pwogram nan epi gade pwodiksyon an bpftool prog dump xlated:

$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple &
[2] 10914

$ sudo bpftool p | grep simple
523: xdp  name simple  tag 44c38a10c657e1b0  gpl
        pids xdp-simple(10915)

$ sudo bpftool p d x id 523
int simple(void *ctx):
; if (bpf_get_smp_processor_id() != 0)
   0: (85) call bpf_get_smp_processor_id#114128
   1: (bf) r1 = r0
   2: (67) r1 <<= 32
   3: (77) r1 >>= 32
   4: (b7) r0 = 2
; }
   5: (15) if r1 == 0x0 goto pc+1
   6: (b7) r0 = 1
   7: (95) exit

Oke, verifikatè te jwenn kernel-helper ki kòrèk la.

Egzanp: pase agiman epi finalman kouri pwogram nan!

Tout fonksyon asistan nan nivo kouri gen yon pwototip

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

Paramèt nan fonksyon asistan yo pase nan rejis r1-r5, epi valè a retounen nan rejis la r0. Pa gen okenn fonksyon ki pran plis pase senk agiman, epi sipò pou yo pa espere ajoute nan lavni.

Ann pran yon gade nan nouvo asistan nwayo a ak fason BPF pase paramèt yo. Ann reekri xdp-simple.bpf.c jan sa a (rès liy yo pa chanje):

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

Pwogram nou an enprime nimewo CPU sou kote li ap kouri. Ann konpile li epi gade kòd la:

$ llvm-objdump -D --section=xdp/simple --no-show-raw-insn xdp-simple.bpf.o

0000000000000000 <simple>:
       0:       r1 = 10
       1:       *(u16 *)(r10 - 8) = r1
       2:       r1 = 8441246879787806319 ll
       4:       *(u64 *)(r10 - 16) = r1
       5:       r1 = 2334956330918245746 ll
       7:       *(u64 *)(r10 - 24) = r1
       8:       call 8
       9:       r1 = r10
      10:       r1 += -24
      11:       r2 = 18
      12:       r3 = r0
      13:       call 6
      14:       r0 = 2
      15:       exit

Nan liy 0-7 nou ekri fisèl la running on CPU%un, ak Lè sa a, sou liy 8 nou kouri youn nan abitye bpf_get_smp_processor_id. Sou liy 9-12 nou prepare agiman k ap ede yo bpf_printk - anrejistre r1, r2, r3. Poukisa gen twa nan yo epi yo pa de? Paske bpf_printksa a se yon anbalaj macro alantou vrè èd la bpf_trace_printk, ki bezwen pase gwosè fisèl fòma a.

Koulye a, an n ajoute yon koup nan liy yo xdp-simple.cse konsa ke pwogram nou an konekte ak koòdone la lo ak reyèlman te kòmanse!

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

Isit la nou itilize fonksyon an bpf_set_link_xdp_fd, ki konekte pwogram XDP-tip BPF nan koòdone rezo. Nou kode nimewo koòdone a lo, ki se toujou 1. Nou kouri fonksyon an de fwa nan premye detache ansyen pwogram nan si li te tache. Remake ke kounye a nou pa bezwen yon defi pause oswa yon bouk enfini: pwogram loader nou an pral sòti, men pwogram BPF la pa pral touye paske li konekte ak sous evènman an. Apre download siksè ak koneksyon, pwogram nan pral lanse pou chak pake rezo rive nan lo.

Ann telechaje pwogram nan epi gade koòdone a lo:

$ sudo ./xdp-simple
$ sudo bpftool p | grep simple
669: xdp  name simple  tag 4fca62e77ccb43d6  gpl
$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 669

Pwogram nou telechaje a gen ID 669 epi nou wè menm ID sou koòdone a lo. Nou pral voye yon koup de pakè nan 127.0.0.1 (demann + repons):

$ ping -c1 localhost

e kounye a, ann gade sa ki nan fichye vityèl debug la /sys/kernel/debug/tracing/trace_pipe, nan ki bpf_printk ekri mesaj li yo:

# cat /sys/kernel/debug/tracing/trace_pipe
ping-13937 [000] d.s1 442015.377014: bpf_trace_printk: running on CPU0
ping-13937 [000] d.s1 442015.377027: bpf_trace_printk: running on CPU0

De pakè yo te takte sou lo epi trete sou CPU0 - premye pwogram BPF ki pa gen okenn sans konplè nou an te travay!

Li se vo anyen sa bpf_printk Se pa pou anyen ke li ekri nan dosye debug la: sa a se pa èd ki gen plis siksè pou itilize nan pwodiksyon, men objektif nou se te montre yon bagay ki senp.

Aksè kat nan pwogram BPF

Egzanp: lè l sèvi avèk yon kat ki soti nan pwogram BPF la

Nan seksyon anvan yo, nou te aprann kijan pou kreye epi sèvi ak kat nan espas itilizatè, epi kounye a ann gade pati nwayo a. Ann kòmanse, kòm dabitid, ak yon egzanp. Ann reekri pwogram nou an xdp-simple.bpf.c jan sa a:

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

Nan kòmansman pwogram nan nou te ajoute yon definisyon kat woo: Sa a se yon etalaj 8-eleman ki estoke valè tankou u64 (nan C nou ta defini tankou yon etalaj tankou u64 woo[8]). Nan yon pwogram "xdp/simple" nou jwenn nimewo processeur aktyèl la nan yon varyab key ak Lè sa a, sèvi ak fonksyon an èd bpf_map_lookup_element nou jwenn yon konsèy sou antre ki koresponn lan nan etalaj la, ke nou ogmante pa youn. Tradui an Ris: nou kalkile estatistik sou ki CPU trete pake fèk ap rantre. Ann eseye kouri pwogram nan:

$ clang -O2 -g -c -target bpf -I libbpf/src/root/usr/include xdp-simple.bpf.c -o xdp-simple.bpf.o
$ bpftool gen skeleton xdp-simple.bpf.o > xdp-simple.skel.h
$ clang -O2 -g -I ./libbpf/src/root/usr/include/ -o xdp-simple xdp-simple.c ./libbpf/src/root/usr/lib64/libbpf.a -lelf -lz
$ sudo ./xdp-simple

Ann tcheke si li branche lo epi voye kèk pake:

$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 108

$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done

Koulye a, ann gade sa ki nan etalaj la:

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

Prèske tout pwosesis yo te trete sou CPU7. Sa a pa enpòtan pou nou, bagay prensipal la se ke pwogram nan travay epi nou konprann ki jan jwenn aksè nan kat nan pwogram BPF - lè l sèvi avèk хелперов bpf_mp_*.

Endèks mistik

Se konsa, nou ka jwenn aksè nan kat la nan pwogram nan BPF lè l sèvi avèk apèl tankou

val = bpf_map_lookup_elem(&woo, &key);

kote fonksyon asistan an sanble

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

men nou ap pase yon konsèy &woo nan yon estrikti san non struct { ... }...

Si nou gade nan pwogram asanble a, nou wè ke valè a &woo pa defini aktyèlman (liy 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
...

epi li genyen nan demenajman:

$ llvm-readelf -r xdp-simple.bpf.o | head -4

Relocation section '.relxdp/simple' at offset 0xe18 contains 1 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name
0000000000000020  0000002700000001 R_BPF_64_64            0000000000000000 woo

Men, si nou gade pwogram ki deja chaje a, nou wè yon konsèy sou kat ki kòrèk la (liy 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]
...

Kidonk, nou ka konkli ke nan moman an nan lanse pwogram loader nou an, lyen ki mennen nan &woo te ranplase pa yon bagay ki gen yon bibliyotèk libbpf. Premye nou pral gade pwodiksyon an strace:

$ sudo strace -e bpf ./xdp-simple
...
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, key_size=4, value_size=8, max_entries=8, map_name="woo", ...}, 120) = 4
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_XDP, prog_name="simple", ...}, 120) = 5

Nou wè sa libbpf te kreye yon kat woo ak Lè sa a, telechaje pwogram nou an simple. Ann pran yon gade pi pre nan ki jan nou chaje pwogram nan:

  • rele xdp_simple_bpf__open_and_load soti nan dosye xdp-simple.skel.h
  • ki lakòz xdp_simple_bpf__load soti nan dosye xdp-simple.skel.h
  • ki lakòz bpf_object__load_skeleton soti nan dosye libbpf/src/libbpf.c
  • ki lakòz bpf_object__load_xattr nan libbpf/src/libbpf.c

Dènye fonksyon an, pami lòt bagay, pral rele bpf_object__create_maps, ki kreye oswa ouvè kat ki deja egziste, ki fè yo tounen deskriptè dosye. (Sa a se kote nou wè BPF_MAP_CREATE nan pwodiksyon an strace.) Apre yo rele fonksyon an bpf_object__relocate epi se li ki enterese nou, paske nou sonje sa nou te wè a woo nan tablo demenajman an. Eksplore li, nou finalman jwenn tèt nou nan fonksyon an bpf_program__relocate, ki fè fas ak demenajman kat jeyografik:

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

Se konsa, nou pran enstriksyon nou yo

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

epi ranplase enskri sous la nan li ak BPF_PSEUDO_MAP_FD, ak premye IMM nan deskriptè fichye kat nou an epi, si li egal a, pou egzanp, 0xdeadbeef, Lè sa a, kòm yon rezilta nou pral resevwa enstriksyon an

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

Sa a se ki jan enfòmasyon kat yo transfere nan yon pwogram BPF espesifik chaje. Nan ka sa a, kat la ka kreye lè l sèvi avèk BPF_MAP_CREATE, ak louvri pa ID lè l sèvi avèk BPF_MAP_GET_FD_BY_ID.

Total, lè w ap itilize libbpf algorithm la se jan sa a:

  • pandan konpilasyon, dosye yo kreye nan tablo demenajman an pou lyen ki mennen nan kat yo
  • libbpf ouvri liv objè ELF, jwenn tout kat yo itilize epi kreye deskriptè fichye pou yo
  • deskriptè dosye yo chaje nan nwayo a kòm yon pati nan enstriksyon an LD64

Kòm ou ka imajine, gen plis k ap vini epi nou pral oblije gade nan nwayo a. Erezman, nou gen yon siy - nou te ekri siyifikasyon an BPF_PSEUDO_MAP_FD nan rejis sous la epi nou ka antere li, ki pral mennen nou nan sen pou tout sen yo - kernel/bpf/verifier.c, kote yon fonksyon ki gen yon non diferan ranplase yon deskriptè fichye ak adrès yon estrikti nan kalite 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;

(Ou ka jwenn tout kòd по ссылке). Se konsa, nou ka elaji algorithm nou an:

  • pandan y ap chaje pwogram nan, verifikatè tcheke itilizasyon kòrèk kat la epi ekri adrès estrikti ki koresponn lan struct bpf_map

Lè w ap telechaje binè ELF la lè l sèvi avèk libbpf Gen anpil plis pase, men nou pral diskite sou sa nan lòt atik.

Chaje pwogram ak kat san libbpf

Jan yo te pwomèt la, isit la se yon egzanp pou lektè ki vle konnen ki jan yo kreye ak chaje yon pwogram ki sèvi ak kat, san èd. libbpf. Sa a ka itil lè w ap travay nan yon anviwònman kote ou pa ka bati depandans, oswa sove chak ti jan, oswa ekri yon pwogram tankou ply, ki jenere kòd binè BPF sou vole.

Pou fè li pi fasil pou swiv lojik la, nou pral reekri egzanp nou an pou rezon sa yo xdp-simple. Kòd konplè ak yon ti kras elaji nan pwogram nan diskite nan egzanp sa a ka jwenn nan sa a esansyèl.

Lojik aplikasyon nou an se jan sa a:

  • kreye yon kat kalite BPF_MAP_TYPE_ARRAY lè l sèvi avèk kòmandman an BPF_MAP_CREATE,
  • kreye yon pwogram ki sèvi ak kat sa a,
  • konekte pwogram nan koòdone lo,

ki tradui nan moun kòm

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

Isit la map_create kreye yon kat jeyografik menm jan ak nou te fè nan premye egzanp sou apèl sistèm lan bpf - "kernel, tanpri fè m 'yon nouvo kat jeyografik nan fòm lan nan yon etalaj de 8 eleman tankou __u64 epi remèt mwen deskriptè fichye a":

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

Pwogram lan fasil pou chaje tou:

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

Pati nan difisil prog_load se definisyon pwogram BPF nou an kòm yon seri estrikti struct bpf_insn insns[]. Men, depi nou ap itilize yon pwogram ke nou genyen nan C, nou ka tronpe yon ti kras:

$ llvm-objdump -D --section xdp/simple xdp-simple.bpf.o

0000000000000000 <simple>:
       0:       85 00 00 00 08 00 00 00 call 8
       1:       63 0a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r0
       2:       bf a2 00 00 00 00 00 00 r2 = r10
       3:       07 02 00 00 fc ff ff ff r2 += -4
       4:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
       6:       85 00 00 00 01 00 00 00 call 1
       7:       b7 01 00 00 00 00 00 00 r1 = 0
       8:       15 00 04 00 00 00 00 00 if r0 == 0 goto +4 <LBB0_2>
       9:       61 01 00 00 00 00 00 00 r1 = *(u32 *)(r0 + 0)
      10:       07 01 00 00 01 00 00 00 r1 += 1
      11:       63 10 00 00 00 00 00 00 *(u32 *)(r0 + 0) = r1
      12:       b7 01 00 00 02 00 00 00 r1 = 2

0000000000000068 <LBB0_2>:
      13:       bf 10 00 00 00 00 00 00 r0 = r1
      14:       95 00 00 00 00 00 00 00 exit

An total, nou bezwen ekri 14 enstriksyon nan fòm estrikti tankou struct bpf_insn (konsèy: pran pil fatra a soti nan pi wo a, re-li seksyon an enstriksyon, louvri linux/bpf.h и linux/bpf_common.h epi eseye detèmine struct bpf_insn insns[] pou kont li):

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

Yon egzèsis pou moun ki pa t 'ekri sa a tèt yo - jwenn map_fd.

Gen yon lòt pati ki pa divilge ki rete nan pwogram nou an - xdp_attach. Malerezman, pwogram tankou XDP pa ka konekte lè l sèvi avèk yon apèl sistèm bpf. Moun ki te kreye BPF ak XDP yo te soti nan kominote Linux sou entènèt la, ki vle di yo te itilize youn ki pi abitye pou yo (men pa nòmal moun) koòdone pou kominike avèk nwayo a: priz netlink, Gade tou RFC3549. Fason ki pi senp pou aplike xdp_attach ap kopye kòd nan libbpf, sètadi, soti nan dosye a netlink.c, ki se sa nou te fè, diminye li yon ti kras:

Byenveni nan mond lan nan sipò netlink

Louvri yon kalite priz 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;
}

Nou li nan priz sa a:

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

Finalman, isit la se fonksyon nou an ki louvri yon priz epi voye yon mesaj espesyal ba li ki gen yon deskriptè dosye:

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

Se konsa, tout bagay pare pou tès la:

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

Ann wè si pwogram nou an te konekte ak lo:

$ ip l show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 xdpgeneric qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    prog/xdp id 160

Ann voye ping epi gade kat la:

$ for s in `seq 234`; do sudo ping -f -c 100 127.0.0.1 >/dev/null 2>&1; done
$ sudo bpftool m dump name woo
key: 00 00 00 00  value: 90 01 00 00 00 00 00 00
key: 01 00 00 00  value: 00 00 00 00 00 00 00 00
key: 02 00 00 00  value: 00 00 00 00 00 00 00 00
key: 03 00 00 00  value: 00 00 00 00 00 00 00 00
key: 04 00 00 00  value: 00 00 00 00 00 00 00 00
key: 05 00 00 00  value: 00 00 00 00 00 00 00 00
key: 06 00 00 00  value: 40 b5 00 00 00 00 00 00
key: 07 00 00 00  value: 00 00 00 00 00 00 00 00
Found 8 elements

Hurray, tout bagay ap mache. Remake byen ke kat jeyografik nou an parèt ankò nan fòm bytes. Sa a se akòz lefèt ke, kontrèman ak libbpf nou pa t chaje enfòmasyon tip (BTF). Men, nou pral pale plis sou sa a pwochen fwa.

Zouti Devlopman

Nan seksyon sa a, nou pral gade twous zouti pou pwomotè BPF minimòm lan.

An jeneral, ou pa bezwen anyen espesyal pou devlope pwogram BPF - BPF kouri sou nenpòt nwayo distribisyon desan, epi pwogram yo bati lè l sèvi avèk clang, ki ka apwovizyone nan pake a. Sepandan, akòz lefèt ke BPF se sou devlopman, nwayo a ak zouti yo toujou ap chanje, si ou pa vle ekri pwogram BPF lè l sèvi avèk metòd ansyen alamòd soti nan 2019, Lè sa a, ou pral gen konpile.

  • llvm/clang
  • pahole
  • nwayo li yo
  • bpftool

(Pou referans, seksyon sa a ak tout egzanp nan atik la te kouri sou Debian 10.)

llvm/clang

BPF se zanmitay ak LLVM epi, byenke dènyèman pwogram pou BPF ka konpile lè l sèvi avèk gcc, tout devlopman aktyèl fèt pou LLVM. Se poutèt sa, anvan tout bagay, nou pral bati vèsyon aktyèl la clang soti nan 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
... много времени спустя
$

Koulye a, nou ka tcheke si tout bagay te ansanm kòrèkteman:

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

(Enstriksyon asanble clang pran pa mwen nan bpf_devel_QA.)

Nou p ap enstale pwogram nou fèk konstwi yo, men pito jis ajoute yo PATHpa egzanp:

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

(Sa a ka ajoute nan .bashrc oswa nan yon dosye separe. Pèsonèlman, mwen ajoute bagay sa yo nan ~/bin/activate-llvm.sh epi lè sa nesesè mwen fè li . activate-llvm.sh.)

Pahole ak BTF

Sèvis piblik pahole itilize lè bati nwayo a pou kreye enfòmasyon debogaj nan fòma BTF. Nou pa pral antre nan detay nan atik sa a sou detay yo nan teknoloji BTF, lòt pase lefèt ke li se pratik epi nou vle sèvi ak li. Se konsa, si ou pral bati nwayo ou a, bati premye pahole (san yo pa pahole ou pa pral kapab bati nwayo a ak opsyon an CONFIG_DEBUG_INFO_BTF:

$ git clone https://git.kernel.org/pub/scm/devel/pahole/pahole.git
$ cd pahole/
$ sudo apt install cmake
$ mkdir build
$ cd build/
$ cmake -D__LIB=lib ..
$ make
$ sudo make install
$ which pahole
/usr/local/bin/pahole

Kernels pou fè eksperyans ak BPF

Lè m ap eksplore posiblite BPF yo, mwen vle rasanble nwayo pwòp mwen an. Sa a, jeneralman pale, pa nesesè, paske ou pral kapab konpile ak chaje pwogram BPF sou nwayo distribisyon an, sepandan, gen pwòp nwayo ou a pèmèt ou sèvi ak dènye karakteristik BPF yo, ki pral parèt nan distribisyon ou nan mwa nan pi bon. , oswa, tankou nan ka a nan kèk zouti debogaj pa pral pake nan tout nan fiti prévisible. Epitou, nwayo pwòp li yo fè li santi li enpòtan pou fè eksperyans ak kòd la.

Yo nan lòd yo bati yon nwayo ou bezwen, premyèman, nwayo a li menm, ak dezyèmman, yon dosye konfigirasyon nwayo. Pou fè eksperyans ak BPF nou ka itilize nòmal la vaniy nwayo oswa youn nan nwayo devlopman yo. Istorikman, devlopman BPF pran plas nan kominote a rezo Linux ak Se poutèt sa, tout chanjman pi bonè oswa pita ale nan David Miller, mentenan rezo Linux la. Tou depan de nati yo - modifye oswa nouvo karakteristik - chanjman rezo tonbe nan youn nan de nwayo - net oswa net-next. Chanjman pou BPF yo distribye nan menm fason an ant bpf и bpf-next, ki Lè sa a, pisin nan net ak net-next, respektivman. Pou plis detay, gade bpf_devel_QA и netdev-FAQ. Se konsa, chwazi yon nwayo ki baze sou gou ou ak bezwen estabilite nan sistèm w ap teste sou la (*-next nwayo yo se pi enstab nan sa yo ki nan lis la).

Li pi lwen pase sijè ki abòde lan atik sa a pou pale sou ki jan yo jere fichye konfigirasyon nwayo - li sipoze ke ou swa deja konnen ki jan fè sa, oswa pare pou aprann pou kont li. Sepandan, enstriksyon sa yo ta dwe plis oswa mwens ase pou ba ou yon sistèm travay BPF ki pèmèt.

Telechaje youn nan nwayo ki anwo yo:

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

Bati yon konfigirasyon minim k ap travay:

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

Pèmèt opsyon BPF nan dosye a .config nan pwòp chwa ou (gen plis chans CONFIG_BPF pral deja aktive depi systemd sèvi ak li). Men yon lis opsyon ki soti nan nwayo yo itilize pou atik sa a:

CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_LSM=y
CONFIG_BPF_SYSCALL=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_IPV6_SEG6_BPF=y
# CONFIG_NETFILTER_XT_MATCH_BPF is not set
# CONFIG_BPFILTER is not set
CONFIG_NET_CLS_BPF=y
CONFIG_NET_ACT_BPF=y
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_DEBUG_INFO_BTF=y

Lè sa a, nou ka fasilman rasanble ak enstale modil yo ak nwayo a (nan chemen an, ou ka rasanble nwayo a lè l sèvi avèk nouvo rasanbleman an. clangpa ajoute CC=clang):

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

ak rdemare ak nouvo nwayo a (mwen itilize pou sa a kexec soti nan pake a kexec-tools):

v=5.8.0-rc6+ # если вы пересобираете текущее ядро, то можно делать v=`uname -r`
sudo kexec -l -t bzImage /boot/vmlinuz-$v --initrd=/boot/initrd.img-$v --reuse-cmdline &&
sudo kexec -e

bpftool

Sèvis piblik ki pi souvan itilize nan atik la pral sèvis piblik la bpftool, apwovizyone kòm yon pati nan nwayo Linux la. Devlopè BPF ekri epi kenbe l pou devlopè BPF epi yo ka itilize pou jere tout kalite objè BPF - chaje pwogram, kreye ak edite kat, eksplore lavi ekosistèm BPF, elatriye. Ou ka jwenn dokimantasyon nan fòm kòd sous pou paj man nan nwayo a oswa, deja konpile, sou nèt la.

Nan moman sa a ekri bpftool vini pare-fè sèlman pou RHEL, Fedora ak Ubuntu (gade, pou egzanp, fil sa a, ki rakonte istwa a fini nan anbalaj bpftool nan Debian). Men, si ou te deja bati nwayo ou a, Lè sa a, bati bpftool fasil tankou tat:

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

$

(Isit la ${linux} - sa a se anyè nwayo ou a.) Apre egzekite kòmandman sa yo bpftool yo pral kolekte nan yon anyè ${linux}/tools/bpf/bpftool epi li ka ajoute nan chemen an (premye nan itilizatè a root) oswa jis kopye nan /usr/local/sbin.

Kolekte bpftool li pi bon yo sèvi ak lèt ​​la clang, reyini jan sa dekri pi wo a, epi tcheke si li reyini kòrèkteman - lè l sèvi avèk, pou egzanp, lòd la

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

ki pral montre ki karakteristik BPF yo aktive nan nwayo ou a.

By wout la, lòd anvan an ka kouri kòm

# bpftool f p k

Sa a se fè pa analoji ak sèvis piblik ki soti nan pake a iproute2, kote nou ka, pou egzanp, di ip a s eth0 olye pou yo ip addr show dev eth0.

Konklizyon

BPF pèmèt ou soulye yon pis efektivman mezire ak sou-a-vole chanje fonksyonalite a nan nwayo a. Sistèm nan te vin gen anpil siksè, nan pi bon tradisyon yo nan UNIX: yon mekanis senp ki pèmèt ou (re)pwogram nwayo a pèmèt yon gwo kantite moun ak òganizasyon fè eksperyans. Epi, byenke eksperyans yo, osi byen ke devlopman nan enfrastrikti BPF nan tèt li, yo byen lwen soti nan fini, sistèm nan deja gen yon ABI ki estab ki pèmèt ou bati serye, ak pi enpòtan, lojik biznis efikas.

Mwen ta renmen remake ke, nan opinyon mwen, teknoloji a te vin tèlman popilè paske, sou yon bò, li kapab jwe (ka achitekti a nan yon machin dwe konprann plis oswa mwens nan yon aswè), ak nan lòt men an, yo rezoud pwoblèm ki pa t 'kapab rezoud (bèl) anvan aparans li. De eleman sa yo ansanm fòse moun yo fè eksperyans ak rèv, ki mennen nan Aparisyon nan pi plis ak plis solisyon inovatè.

Atik sa a, byenke pa patikilyèman kout, se sèlman yon entwodiksyon nan mond lan nan BPF epi li pa dekri karakteristik "avanse" ak pati enpòtan nan achitekti a. Plan pou pi devan se yon bagay tankou sa a: pwochen atik la pral yon apèsi sou kalite pwogram BPF (gen 5.8 kalite pwogram sipòte nan nwayo 30 la), Lè sa a, nou pral finalman gade nan ki jan yo ekri aplikasyon BPF reyèl lè l sèvi avèk pwogram trase nwayo. kòm yon egzanp, Lè sa a, li lè pou yon kou plis pwofondè sou achitekti BPF, ki te swiv pa egzanp sou rezo BPF ak aplikasyon pou sekirite.

Atik anvan yo nan seri sa a

  1. BPF pou ti piti yo, pati zewo: BPF klasik

Lyen

  1. Gid referans BPF ak XDP — dokiman sou BPF soti nan Cilium, oswa plis jisteman soti nan Daniel Borkman, youn nan kreyatè yo ak moun ki kenbe BPF. Sa a se youn nan premye deskripsyon serye yo, ki diferan de lòt yo nan ke Danyèl konnen egzakteman ki sa l ap ekri sou epi pa gen okenn erè la. An patikilye, dokiman sa a dekri kijan pou travay ak pwogram BPF nan kalite XDP ak TC lè l sèvi avèk sèvis piblik byen li te ye. ip soti nan pake a iproute2.

  2. Dokimantasyon/rezo/filter.txt — fichye orijinal ak dokimantasyon pou BPF klasik ak Lè sa a, pwolonje. Yon bon lekti si ou vle fouye nan lang asanble ak detay teknik achitekti.

  3. Blog sou BPF soti nan facebook. Li se mete ajou raman, men byen, kòm Alexei Starovoitov (otè eBPF) ak Andrii Nakryiko - (mentè) ekri la. libbpf).

  4. Sekrè bpftool. Yon fil twitter amizan soti nan Quentin Monnet ak egzanp ak sekrè nan lè l sèvi avèk bpftool.

  5. Plonje nan BPF: yon lis materyèl lekti. Yon lis jeyan (epi toujou kenbe) lyen ki mennen nan dokiman BPF ki soti nan Quentin Monnet.

Sous: www.habr.com

Add nouvo kòmantè