Una Breve Introduzione à BPF è eBPF

Ehi Habr! Vi informemu chì avemu preparatu per liberà un libru "Osservabilità Linux cù BPF".

Una Breve Introduzione à BPF è eBPF
Cume a macchina virtuale BPF cuntinueghja à evoluzione è hè attivamente utilizata in a pratica, avemu traduttu un articulu per voi chì descrive e so caratteristiche principali è u statu attuale.

Nta l'ultimi anni, l'arnesi è e tecniche di prugrammazione anu acquistatu pupularità per cumpensà e limitazioni di u kernel Linux in i casi induve u processu di pacchetti d'altu rendiment hè necessariu. Unu di i metudi più populari di stu tipu hè chjamatu bypass core (bypass di u kernel) è permette, saltendu a strata di a rete di u kernel, per realizà tutte e processazioni di pacchetti da u spaziu di l'utilizatori. Bypassing u kernel implica ancu a gestione di a carta di rete da spaziu utilizatori. In altri palori, quandu u travagliu cù una carta di rete, avemu a basa di u driver spaziu utilizatori.

Trasferendu u cuntrollu tutale di a carta di rete à un prugramma di u spaziu di l'utilizatori, riducemu l'overhead di u kernel (cambiatori di cuntestu, trasfurmazioni di strati di rete, interruzioni, etc.), chì hè abbastanza impurtante quandu si corre à velocità di 10 Gb / s o più altu. Bypassendu u kernel più una cumminazione di altre caratteristiche (trasfurmazioni batch) è una sintonizazione attenta di a prestazione (Contabilità NUMA, Isolazione CPU, etc.) s'adattanu à i principii di a rete di u spaziu di l'utilizatori d'altu rendiment. Forsi un esempiu esemplariu di stu novu approcciu à u processamentu di pacchetti hè DPDK da Intel (Kit di sviluppu di u pianu di dati), ancu s'ellu ci sò altre arnesi è tecniche ben cunnisciute, cumprese VPP da Cisco (Vector Packet Processing), Netmap è, sicuru, snabb.

L'urganizazione di l'interazzione di rete in u spaziu di l'utilizatori hà una quantità di svantaghji:

  • Un kernel OS hè una capa di astrazione per risorse hardware. Perchè i prugrammi di u spaziu di l'utilizatori anu da gestisce i so risorse direttamente, anu ancu da gestisce u so propiu hardware. Questu spessu significa prugrammà i vostri propri drivers.
  • Siccomu rinuncemu cumplettamente u spaziu di u kernel, rinunciamu ancu tutte e funziunalità di rete furnite da u kernel. I prugrammi di u spaziu di l'utilizatori anu da ripiglià e funzioni chì ponu esse digià furnite da u kernel o u sistema operatore.
  • I prugrammi travaglianu in un modu sandbox, chì limita seriamente a so interazzione è impedisce l'integrazione cù altre parti di u sistema operatore.

In essenza, quandu si mette in rete in u spaziu di l'utilizatori, i guadagni di rendiment sò ottenuti movendu l'elaborazione di pacchetti da u kernel à u spaziu di l'utilizatori. XDP face esattamente u cuntrariu: si move i prugrammi di rete da u spaziu di l'utilizatori (filtri, cunvertitori, routing, etc.) à l'area di u kernel. XDP ci permette di eseguisce a funzione di a rete appena u pacchettu tocca l'interfaccia di a rete è prima di cumincià à viaghjà finu à u subsistema di rete di u kernel. In u risultatu, a velocità di trasfurmazioni di pacchetti hè significativamente aumentata. Tuttavia, cumu u kernel permette à l'utilizatori di eseguisce i so prugrammi in u spaziu di u kernel? Prima di risponde à sta quistione, fighjemu ciò chì hè BPF.

BPF è eBPF

Malgradu u nome micca cumplettamente chjaru, BPF (Packet Filtering, Berkeley) hè, in fattu, un mudellu di macchina virtuale. Sta macchina virtuale hè stata urigginariamente pensata per trattà u filtru di pacchetti, da quì u nome.

Unu di i strumenti più cunnisciuti chì utilizanu BPF hè tcpdump. Quandu catturà pacchetti cù tcpdump l'utilizatore pò specificà una espressione per u filtru di pacchetti. Solu i pacchetti chì currispondenu à sta espressione seranu catturati. Per esempiu, l'espressione "tcp dst port 80” si riferisce à tutti i pacchetti TCP chì arrivanu à u portu 80. U compilatore pò scurzà sta espressione cunvertisce in BPF bytecode.

$ 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

Questu hè basicamente ciò chì u prugramma sopra face:

  • Istruzzioni (000): Carica u pacchettu à l'offset 12, cum'è una parolla di 16 bit, in l'accumulatore. Offset 12 currisponde à l'ethertype di u pacchettu.
  • Instruction (001): compara u valore in l'accumulatore cù 0x86dd, vale à dì cù u valore di l'ethertype per IPv6. Se u risultatu hè veru, u cuntatore di u prugramma passa à l'istruzzioni (002), è se no, allora à (006).
  • Instruction (006): compara u valore cù 0x800 (valore etertype per IPv4). Se a risposta hè vera, u prugramma passa à (007), se no, allora à (015).

È cusì, finu à chì u prugramma di filtru di pacchetti torna un risultatu. Di solitu hè booleanu. Riturnà un valore micca zero (istruzzioni (014)) significa chì u pacchettu currisponde, è rinvià cero (istruzzioni (015)) significa chì u pacchettu ùn hà micca currispundenza.

A macchina virtuale BPF è u so bytecode sò stati pruposti da Steve McCann è Van Jacobson à a fini di u 1992 quandu u so documentu hè statu fora. Filtru di Pacchetti BSD: Nova architettura per a cattura di pacchetti à livellu d'utilizatore, Per a prima volta sta tecnulugia hè stata presentata à a cunferenza Usenix in l'invernu di u 1993.

Perchè BPF hè una macchina virtuale, definisce l'ambiente in quale i prugrammi currianu. In più di bytecode, definisce ancu un mudellu di memoria di pacchettu (istruzzioni di carica sò implicitamente appiicati à un pacchettu), registri (A è X; registri di accumulatore è indici), almacenamiento di memoria di scratch, è un contatore di prugramma implicitu. Curiosamente, u bytecode BPF hè statu modelatu dopu à u Motorola 6502 ISA. Cum'è Steve McCann hà ricurdatu in u so rapportu plenaria à Sharkfest '11, era familiarizatu cù a custruzzione 6502 da u liceu quandu programava nantu à l'Apple II, è sta cunniscenza hà influenzatu u so travagliu cuncependu u bytecode BPF.

U supportu BPF hè implementatu in u kernel Linux in a versione v2.5 è più tardi, aghjuntu principalmente da Jay Schullist. U codice BPF ùn hè micca cambiatu finu à u 2011, quandu Eric Dumaset hà riprogettatu l'interprete BPF per travaglià in modu JIT (Fonte: JIT per i filtri di pacchetti). Dopu quì, invece di interpretà u bytecode BPF, u kernel puderia cunvertisce direttamente i prugrammi BPF à l'architettura di destinazione: x86, ARM, MIPS, etc.

In seguitu, in u 2014, Alexei Starovoitov prupone un novu mecanismu JIT per BPF. In fatti, sta nova JIT divintò una nova architettura basatu in BPF è hè stata chjamata eBPF. Pensu chì e duie VM coesistevanu per qualchì tempu, ma u filtru di pacchetti hè attualmente implementatu nantu à eBPF. In fatti, in parechji esempi di documentazioni muderni, BPF hè chjamatu eBPF, è BPF classicu hè cunnisciutu oghje cum'è cBPF.

eBPF estende a classica macchina virtuale BPF in parechje manere:

  • Si basa nantu à l'architetture muderne di 64-bit. eBPF usa registri 64-bit è aumenta u numeru di registri dispunibuli da 2 (accumulatore è X) à 10. eBPF furnisce ancu opcodes supplementari (BPF_MOV, BPF_JNE, BPF_CALL...).
  • Staccatu da u sottosistema di a capa di rete. BPF hè stata ligata à u mudellu di dati batch. Siccomu hè stata utilizata per filtrà i pacchetti, u so codice era in u sottosistema chì furnisce interazzione di rete. Tuttavia, a macchina virtuale eBPF ùn hè più ligata à un mudellu di dati è pò esse usata per ogni scopu. Allora, avà u prugramma eBPF pò esse cunnessu à tracepoint o à kprobe. Questu apre a porta à l'instrumentazione eBPF, l'analisi di rendiment, è parechji altri casi d'usu in u cuntestu di altri sottosistemi di kernel. Avà u codice eBPF si trova in u so propiu percorsu: kernel/bpf.
  • I magazzini di dati globali chjamati Maps. E carte sò magazzini di valore chjave chì furnisce u scambiu di dati trà u spaziu di l'utilizatori è u spaziu di u kernel. eBPF furnisce parechji tippi di carte.
  • Funzioni secundariu. In particulare, per sovrascrive un pacchettu, calculà un checksum, o clone un pacchettu. Queste funzioni funzionanu in u kernel è ùn appartenenu micca à i prugrammi di u spaziu di l'utilizatori. Inoltre, e chjama di u sistema ponu esse fatte da i prugrammi eBPF.
  • Finisce e chjama. A dimensione di u prugramma in eBPF hè limitata à 4096 bytes. A funzione di chjama finale permette à un prugramma eBPF di trasfiriri u cuntrollu à un novu prugramma eBPF è cusì sguassate sta limitazione (finu à i prugrammi 32 ponu esse incatenati in questu modu).

Esempiu eBPF

Ci sò parechji esempi per eBPF in i fonti di u kernel Linux. Sò dispunibuli à samples/bpf/. Per cumpilà questi esempi, basta à scrive:

$ sudo make samples/bpf/

Ùn scriveraghju micca un novu esempiu per eBPF, ma aduprà unu di i campioni dispunibuli in samples/bpf/. Fighjuleraghju alcune parti di u codice è spiegheraghju cumu funziona. Per esempiu, aghju sceltu u prugramma tracex4.

In generale, ognunu di l'esempii in samples/bpf/ si compone di dui schedari. In stu casu:

  • tracex4_kern.c, cuntene u codice fonte per esse eseguitu in u kernel cum'è eBPF bytecode.
  • tracex4_user.c, cuntene un prugramma da u spaziu di l'utilizatori.

In questu casu, avemu bisognu di cumpilà tracex4_kern.c à eBPF bytecode. À u mumentu in gcc ùn ci hè micca parte di u servitore per eBPF. Fortunatamente, clang pò pruduce eBPF bytecode. Makefile usi clang compilà tracex4_kern.c à u schedariu d'ughjettu.

Aghju dettu sopra chì una di e funzioni più interessanti di eBPF hè e carte. tracex4_kern definisce una mappa:

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 hè unu di i tanti tipi di carte offerti da eBPF. In questu casu, hè solu un hash. Pudete ancu avè nutatu l'annunziu SEC("maps"). SEC hè una macro utilizata per creà una nova sezione di un schedariu binariu. In fatti, in l'esempiu tracex4_kern Dui altri sezzioni sò definiti:

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;

    // получаем ip-адрес вызывающей стороны kmem_cache_alloc_node() 
    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;
}   

Queste duie funzioni permettenu di sguassà una entrata da a mappa (kprobe/kmem_cache_free) è aghjunghje una nova entrata à a mappa (kretprobe/kmem_cache_alloc_node). Tutti i nomi di funzioni scritti in lettere maiuscule currispondenu à macros definite in bpf_helpers.h.

Se dump e rùbbriche di u schedariu d'ughjettu, duverebbe vede chì sti novi rùbbriche sò digià definiti:

$ objdump -h tracex4_kern.o

tracex4_kern.o: file format elf64-little

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 0000000000000000 0000000000000000 00000040 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 kprobe/kmem_cache_free 00000048 0000000000000000 0000000000000000 00000040 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 kretprobe/kmem_cache_alloc_node 000000c0 0000000000000000 0000000000000000 00000088 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 maps 0000001c 0000000000000000 0000000000000000 00000148 2**2
CONTENTS, ALLOC, LOAD, DATA
4 license 00000004 0000000000000000 0000000000000000 00000164 2**0
CONTENTS, ALLOC, LOAD, DATA
5 version 00000004 0000000000000000 0000000000000000 00000168 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .eh_frame 00000050 0000000000000000 0000000000000000 00000170 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

Ci hè ancu tracex4_user.c, prugramma principale. In fondu, stu prugramma ascolta l'avvenimenti kmem_cache_alloc_node. Quandu un tali avvenimentu accade, u codice eBPF currispondente hè eseguitu. U codice salva l'attributu IP di l'ughjettu à una mappa, è dopu l'ughjettu hè in loop per u prugramma principale. Esempiu:

$ sudo ./tracex4
obj 0xffff8d6430f60a00 is 2sec old was allocated at ip ffffffff9891ad90
obj 0xffff8d6062ca5e00 is 23sec old was allocated at ip ffffffff98090e8f
obj 0xffff8d5f80161780 is 6sec old was allocated at ip ffffffff98090e8f

Cumu sò u prugramma di u spaziu di l'utilizatori è u prugramma eBPF? À l'inizializazione tracex4_user.c carica u schedariu d'ughjettu tracex4_kern.o usendu a funzione load_bpf_file.

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

Mentre facia load_bpf_file e sonde definite in u schedariu eBPF sò aghjuntu à /sys/kernel/debug/tracing/kprobe_events. Avà ascoltemu questi avvenimenti è u nostru prugramma pò fà qualcosa quandu succede.

$ 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

Tutti l'altri prugrammi in sample/bpf/ sò strutturati in modu simile. Sempre cuntenenu dui schedari:

  • XXX_kern.c: prugramma eBPF.
  • XXX_user.c: prugramma principale.

U prugramma eBPF definisce e carte è e funzioni assuciate cù una sezione. Quandu u kernel emette un avvenimentu di un certu tipu (per esempiu, tracepoint), e funzioni ligate sò eseguite. Maps furnisce a cumunicazione trà un prugramma di kernel è un prugramma di spaziu d'utilizatori.

cunchiusioni

In questu articulu, BPF è eBPF sò stati discututi in termini generale. Sapemu chì ci sò assai infurmazioni è risorse nantu à eBPF oghje, cusì vi ricumanderaghju uni pochi di più materiali per più studiu.

Vi cunsigliu di leghje:

Source: www.habr.com

Add a comment