Tere, Habr! Teavitame, et meil on valmimas raamat "".

Kuna BPF virtuaalmasin jÀtkab evolutsiooni ja seda rakendatakse aktiivselt praktikas, oleme teie jaoks tÔlkinud artikli, mis kirjeldab selle pÔhivÔimekusi ja praegust seisundit.
Viimastel aastatel on populaarsust kogunud programmeerimistööriistade komplektid ja tehnikad, mis on mĂ”eldud Linuxi tuuma piirangute kompenseerimiseks olukordades, kus on vaja kĂ”rgjĂ”udlusega paketitöötlust. Ăks sellistest populaarsetest tehnikatest on tuuma ĂŒmbersĂ”it (kernel bypass), mis vĂ”imaldab, mööda minnes tuumast, teostada kogu paketitöötlust kasutajaruumi tasandilt. Tuuma ĂŒmbersĂ”it eeldab ka vĂ”rkaartide haldamist kasutajaruumi tasandilt. TeisisĂ”nu, vĂ”rkaardi kasutamisel toetume me juhenditele kasutajaruumi tasandilt.
Kuna me anname tĂ€ieliku kontrolli vĂ”rkaardi ĂŒle kasutajaruumi programmale, vĂ€hendame tuuma tööga seotud kulusid (konttekstivahetus, vĂ”rktaseme töötlemine, katkestused jne), mis on piisavalt oluline töötades kiirusel 10 Gb/s vĂ”i rohkem. Tuuma ĂŒmbersĂ”it koos teiste vĂ”imetega (paketiprosessimine) ja hoolikas jĂ”udluse hÀÀlestamine (NUMA arvestus, CPU isoleerimine, jne.) vastavad kĂ”rge jĂ”udlusega vĂ”rgu töötlemise aluspĂ”hjadele kasutajaruumi tasandil. Ăks nĂ€ide sellisest uuest paketitöötluse lĂ€henemisviisist on Inteli poolt (Andmeplaani arenduskits), ehkki ka teisi laialdaselt tuntud tööriistu ja tehnikaid, nagu Cisco VPP (Vector Packet Processing), Netmap ja loomulikult .
Kasutajaruumi vÔrgu interaktsioonide korraldamisel on mitmeid puudusi:
- OS-i tuum on riistvararesursside abstraktsioonitasand. Kuna kasutajaruumi programm ei halda oma ressursse otse, peab ta juhtima ka oma riistvara. Sageli tÀhendab see vajadust oma juhendite programmeerimise jÀrele.
- Kuna me loobume tĂ€ielikult tuumast, loobume ka igasugusest vĂ”rgufunktsionaalsusest, mida tuum pakub. Kasutajaruumi programmid peavad uuesti rakendama need funktsioonid, mida tuum vĂ”i operatsioonisĂŒsteem vĂ”ib-olla juba pakuvad.
- Programmid töötavad liivakastireĆŸiimis, mis tĂ”siselt piirab nende suhtlemisvĂ”imet ning takistab neil integreerimist teiste operatsioonisĂŒsteemi osadega.
Tegelikult saavutatakse vĂ”rgus suhtlemisel kasutajaruumi korraldamisel jĂ”udluse tĂ”us pakettide töötlemise edasiviimisega tuumast kasutajaruumi. XDP teeb tĂ€pselt vastupidist: viib vĂ”rguprogrammid (filtrid, teisendajad, marsruudid jne) kasutajaruumist tuuma. XDP vĂ”imaldab meil teostada vĂ”rgufunktsiooni kohe, kui paketid jĂ”uavad vĂ”rguliidesesse ja enne kui need hakkavad liikuma tuuma vĂ”rgu alamsĂŒsteemi. Tulemuseks on pakettide töötlemise kiirus mĂ€rkimisvÀÀrselt suurenenud. Kuidas aga tuum vĂ”imaldab kasutajatel oma programme tuumas kĂ€ivitada? Enne sellele kĂŒsimusele vastamist vaatame, mis on BPF.
BPF ja eBPF
Kuigi BPF (Berkeley Packet Filtering) nimi ei pruugi olla kĂ”ige arusaadavam â see on tegelikult virtuaalmasina mudel. See virtuaalmasin loodi algselt pakettide filtreerimise jaoks, seega ka nimi.
Ăks tuntumaid tööriistu, mis kasutab BPF-i, on tcpdump. Pakettide pĂŒĂŒdmise korral tcpdump vĂ”ib kasutaja mÀÀrata filtriavalduse pakettide filtreerimiseks. PĂŒĂŒda saab ainult need paketid, mis vastavad sellele avaldusele. NĂ€iteks avaldus "tcp dst port 80" kĂ€sitleb kĂ”iki TCP-pakette, mis jĂ”uavad pordile 80. Kompilaator vĂ”ib seda avaldust lĂŒhendada, muutes selle BPF-i baitkoodiks.
$ sudo tcpdump -d "tcp dst port 80"
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 6
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 15
(004) ldh [56]
(005) jeq #0x50 jt 14 jf 15
(006) jeq #0x800 jt 7 jf 15
(007) ldb [23]
(008) jeq #0x6 jt 9 jf 15
(009) ldh [20]
(010) jset #0x1fff jt 15 jf 11
(011) ldxb 4*([14]&0xf)
(012) ldh [x + 16]
(013) jeq #0x50 jt 14 jf 15
(014) ret #262144
(015) ret #0
See, mida eelnevalt nimetatud programm pÔhimÔtteliselt teeb:
- KĂ€sk (000): laadib paketi nihkega 12, 16-bitise sĂ”nana akumulaatorisse. Nihke 12 vastab paketi ethetĂŒĂŒbile.
- KĂ€sk (001): vĂ”rreldakse akumulaatoris olevat vÀÀrtust 0x86dd-ga, see tĂ€hendab, et IPv6 ethetĂŒĂŒbivÀÀrtusega. Kui tulemus on tĂ”ene, siis lĂ€heb programmi counter kĂ€sule (002), ja kui ei â siis (006).
- Juhend (006): vÔrdleb vÀÀrtust 0x800 (ethertype-vÀÀrtus IPv4 jaoks). Kui vastus on tÔene, jÀtkab programm (007) juurde, muul juhul (015) juurde.
Ja nii edasi, kuni pakettide filtreerimissĂŒsteem tagastab tulemuse. TĂŒĂŒpiliselt on see boolean. Mitte-null vÀÀrtuse tagastamine (juhend (014)) tĂ€hendab, et pakett sobib, samas kui null vÀÀrtuse tagastamine (juhend (015)) tĂ€hendab, et pakett ei sobi.
Virtuaalne BPF-masin ja selle baitkood pakkusid Steve McCan ja Van Jacobson 1992. aasta lÔpus, kui ilmus nende artikel , esmakordselt tutvustati seda tehnoloogiat Usenixi konverentsil talvel 1993.
Kuna BPF on virtuaalne masin, mÀÀratleb see keskkonna, kus programmid kÀivitatakse. Lisaks baitkoodile mÀÀratleb see ka mÀlumudeli pakettide jaoks (laadimisjuhised rakendatakse kaudselt paketile), register A ja X; akumulaator ja indeksi register, skratch-mÀlu salvestus ja kaudne programmi loenduri. Huvitaval kombel on BPF baitkood mudeldatud Motorola 6502 ISA jÀrgi. Nagu meenutas Steve McCann oma Sharkfest '11-l, oli ta 6502 kogumisega tuttav juba keskkoolist, kui programmeeris Apple II peal, ja need teadmised mÔjutasid tema tööd BPF baitkoodi projekteerimisel.
BPF-i toetamine on rakendatud Linuxi tuumas versioonis v2.5 ja kĂ”rgemates, peamiselt Jay Schullisti jĂ”upingutustega. BPF kood jĂ€i ilma tĂ”siste muudatusteta kuni 2011. aastani, mil Eric Dumazet ĂŒmber tegi BPF tĂ”lgendi töötamiseks JIT-reĆŸiimis (Allikas: ). PĂ€rast seda vĂ”is kernel BPF baitkoodi tĂ”lgendamise asemel otse BPF programme sihtarhitektuuriks, nagu x86, ARM, MIPS jne.
Hiljem 2014. aastal pakkus Aleksei Starovoitov vÀlja uue JIT-mehhanismi BPF jaoks. Tegelikult muutus see uus JIT uue alusena BPF ja sai nimeks eBPF. Arvan, et mÔnda aega eksisteerisid mÔlemad virtuaalsed masinad kÔrvuti, kuid praegu pÔhineb pakettide filtreerimine eBPF-l. Tegelikult mÔistetakse paljudes tÀnapÀevastes dokumentides BPF all eBPF-d, samas kui klassikaline BPF on tÀnapÀeval tuntud kui cBPF.
eBPF laieneb klassikalise BPF virtuaalse masina mÔnes osas:
- Tugineb 64-bit arhitektuuridel. eBPF kasutab 64-bitiseid registreid ja suurendab saadaval olevate registreid arvu kahest (akumulaator ja X) kĂŒmneni. eBPF-s on samuti saadaval tĂ€iendavad kĂ€sud (BPF_MOV, BPF_JNE, BPF_CALLâŠ).
- Eraldatud vĂ”rgualaste sĂŒsteemide alamkonstruktsioonist. BPF oli seotud pakettide andmemudeliga. Kuna seda kasutati pakettide filtreerimiseks, oli selle kood seotud sĂŒsteemiga, mis haldab vĂ”rguĂŒhendusi. Kuid eBPF virtuaalne masin ei ole enam andmemudeliga seotud ja seda saab kasutada igasugustel eesmĂ€rkidel. NĂŒĂŒd saab eBPF programmi ĂŒhendatud tracepointi vĂ”i kprobe'iga. See avab tee eBPF instrumenteerimisele, jĂ”udluse analĂŒĂŒsile ja paljudele muudele rakendustele teiste sĂŒdamiku alamkonstruktsioonide kontekstis. NĂŒĂŒd asub eBPF kood oma rada: kernel/bpf.
- Globaalne andmete ladustamine nimega Kaardid. Kaardid on vĂ”tme-vÀÀrtuse tĂŒĂŒpi ladustamised, mis vĂ”imaldavad andmete vahetamist kasutajaruumi ja tuuma ruumi vahel. eBPF-s pakutakse mitut tĂŒĂŒpi kaarte.
- Abifunktsioonid. EelkĂ”ige paketi ĂŒmberkirjutamiseks, kontrollsummat arvutamiseks vĂ”i paketi kloonimiseks. Need funktsioonid tĂ€idetakse tuumas ja ei kuulu kasutajaruumi programmide alla. Lisaks saab eBPF programmide kaudu teostada sĂŒsteemi kutsungeid.
- LĂ”ppkutsed. eBPF programmi suurus on piiratud 4096 baitiga. LĂ”ppkutsumise vĂ”imalus vĂ”imaldab eBPF programmi suunata uuele eBPF programmile ja seelĂ€bi selle piirangu ĂŒle saada (sel viisil saab siduda kuni 32 programmi).
eBPF: nÀide
Linuxi tuuma allikates on mitmeid nÀiteid eBPF jaoks. Need on saadaval aadressil samples/bpf/. Nende nÀidiste kompileerimiseks sisestage lihtsalt:
$ sudo make samples/bpf/
Ma ei hakka uut eBPF nĂ€idet ise kirjutama, vaid kasutan ĂŒhte nĂ€idist, mis on saadaval aadressil samples/bpf/. Vaatan lĂ€bi mĂ”ned koodilĂ”igud ja selgitan, kuidas see töötab. NĂ€iteks valisin programmi tracex4.
Ăldiselt koosneb iga nĂ€ide aadressil samples/bpf/ kahest failist. Antud juhul:
tracex4_kern.c, sisaldab allika koodi, mis peab tuumas töötama eBPF bait-koodina.tracex4_user.c, sisaldab programmi kasutajaruumi.
Sellisel juhul peame selle kompileerima tracex4_kern.c eBPF баĐčŃ-ĐșĐŸĐŽ. Praegu puudub gcc eBPF jaoks serveripool. Ănneks clang saab genereerida eBPF bytecode. kasutab clang kompileerimiseks tracex4_kern.c objektifaili.
Mainisin varem, et ĂŒks eBPF-i huvitavamaid funktsioone on kaardid. tracex4_kern mÀÀratleb ĂŒhe kaardi:
struct pair {
u64 val;
u64 ip;
};
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(long),
.value_size = sizeof(struct pair),
.max_entries = 1000000,
}; BPF_MAP_TYPE_HASH â ĂŒks paljusid eBPF-i pakutavaid kaarti tĂŒĂŒpe. KĂ€esoleval juhul on see lihtsalt hash. Samuti olete vĂ”inud mĂ€rgata kuulutust SEC("maps"). SEC on makro, mida kasutatakse uue jaos loomiseks binaarfailis. Tegelikult mÀÀratletakse nĂ€ites tracex4_kern veel kaks jaotust:
SEC("kprobe/kmem_cache_free")
int bpf_prog1(struct pt_regs *ctx)
{
long ptr = PT_REGS_PARM2(ctx);
bpf_map_delete_elem(&my_map, &ptr);
return 0;
}
SEC("kretprobe/kmem_cache_alloc_node")
int bpf_prog2(struct pt_regs *ctx)
{
long ptr = PT_REGS_RC(ctx);
long ip = 0;
\\ saame kmem_cache_alloc_node() kutsuja IP-aadressi
BPF_KRETPROBE_READ_RET_IP(ip, ctx);
struct pair v = {
.val = bpf_ktime_get_ns(),
.ip = ip,
};
bpf_map_update_elem(&my_map, &ptr, &v, BPF_ANY);
return 0;
} Need kaks funktsiooni vÔimaldavad kaardilt kustutada kirje (kprobe/kmem_cache_free) ja lisada kaardile uus kirje (kretprobe/kmem_cache_alloc_node). KÔik suurtÀhtedega kirjutatud funktsioonide nimed vastavad makrodele, mis on mÀÀratletud .
Kui ma prindin objekti faili sektsioonid, peaksin nÀgema, et need uued sektsioonid on juba mÀÀratletud:
$ objdump -h tracex4_kern.o
tracex4_kern.o: faili formaat elf64-little
Sektsioonid:
Idx Nimi Suurus VMA LMA Faili off Algn
0 .text 00000000 0000000000000000 0000000000000000 00000040 2**2
SISU, ALLOC, LAADIDA, LOE KESKMINE, KOOD
1 kprobe/kmem_cache_free 00000048 0000000000000000 0000000000000000 00000040 2**3
SISU, ALLOC, LAADIDA, RELOC, LOE KESKMINE, KOOD
2 kretprobe/kmem_cache_alloc_node 000000c0 0000000000000000 0000000000000000 00000088 2**3
SISU, ALLOC, LAADIDA, RELOC, LOE KESKMINE, KOOD
3 kaardid 0000001c 0000000000000000 0000000000000000 00000148 2**2
SISU, ALLOC, LAADIDA, ANDMED
4 litsents 00000004 0000000000000000 0000000000000000 00000164 2**0
SISU, ALLOC, LAADIDA, ANDMED
5 versioon 00000004 0000000000000000 0000000000000000 00000168 2**2
SISU, ALLOC, LAADIDA, ANDMED
6 .eh_frame 00000050 0000000000000000 0000000000000000 00000170 2**3
SISU, ALLOC, LAADIDA, RELOC, LOE KESKMINE, ANDMED
Seal on ka , peamine programm. PĂ”himĂ”tteliselt kuulab see programm sĂŒndmusi kmem_cache_alloc_node. Kui selline sĂŒndmus toimub, kĂ€ivitub vastav eBPF kood. Kood salvestab objekti IP-attribuudi kaardile ja seejĂ€rel vĂ€ljastatakse see objekt peamise programmi kaudu pĂ€ripĂ€eva. NĂ€ide:
$ sudo .\/tracex4
obj 0xffff8d6430f60a00 on 2 sekundi vana, suleti ip-lt ffffffff9891ad90
obj 0xffff8d6062ca5e00 on 23 sekundi vana, suleti ip-lt ffffffff98090e8f
obj 0xffff8d5f80161780 on 6 sekundi vana, suleti ip-lt ffffffff98090e8f
Kuidas on seotud kasutaja ruumi programm ja eBPF programm? Alguses tracex4_user.c laeb objekti faili tracex4_kern.o NtUnmapViewOfSection lae_bpf_fail.
int main(int ac, char **argv)
{
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
char filename[256];
int i;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)");
return 1;
}
if (load_bpf_file(filename)) {
printf("%s", bpf_log_buf);
return 1;
}
for (i = 0; ; i++) {
print_old_objects(map_fd[1]);
sleep(1);
}
return 0;
} Teostamisel sonde, mis on mÀÀratletud eBPF failis, lisatakse /sys/kernel/debug/tracing/kprobe_events. NĂŒĂŒd kuuleme neid sĂŒndmusi ja meie programm vĂ”ib midagi teha, kui need juhtuvad.
$ sudo cat /sys/kernel/debug/tracing/kprobe_events
p:kprobes/kmem_cache_free kmem_cache_free
r:kprobes/kmem_cache_alloc_node kmem_cache_alloc_node
KĂ”ik ĂŒlejÀÀnud programmid sample/bpf/ on ĂŒles ehitatud sarnaselt. Igas neist on alati kaks faili:
XXX_kern.c: eBPF programm.XXX_user.c: peaprogramm.
eBPF programm defineerib kaardid ja funktsioonid, mis on seotud sektsiooniga. Kui tuumik vĂ€ljastab teatud tĂŒĂŒpi sĂŒndmuse (nt tracepoint), siis kĂ€ivitatakse seotud funktsioonid. Kaardid vĂ”imaldavad andmete vahetamist tuumaprogrammi ja kasutajaruumi programmi vahel.
KokkuvÔte
Selles artiklis vaadeldi BPF-i ja eBPF-i ĂŒldiselt. Ma tean, et tĂ€napĂ€eval on palju teavet ja ressursse eBPF-i kohta, seetĂ”ttu soovitan veel mĂ”ned materjalid edasiseks uurimiseks
Soovitan lugeda:
- Jonathan Corbet. Sissejuhatus BPF-i ja arutelu selle evolutsioonist eBPF-iks.
- Brendan Gregg. Artikkel LWN.net-ist. Brendan kirjutab sageli eBPF-i teemal sÀutse ja hoiab endal nimekirja ressurssidest selle teema kohta .
- Julia Evans. Kommentaarid Sucakra Sharma esitusele âThe BSD Packet Filter: A New Architecture for User-level Packet Captureâ. Kommentaarid on head ja aitavad tĂ”eliselt slaididesse sĂŒveneda.
- Ferris Ellis. Pikk artikkel, millel on , kuid tasub lugeda. Ăks parimaid artikleid eBPF-ist, mida olen kohanud.
Allikas: habr.com
