A Perkenalan singket pikeun BPF na eBPF

Halo, Habr! Kami hoyong nginpokeun ka anjeun yén kami nuju nyiapkeun buku pikeun dileupaskeun."Linux Observability kalawan BPF".

A Perkenalan singket pikeun BPF na eBPF
Kusabab mesin virtual BPF terus mekar tur aktip dipaké dina praktekna, kami geus narjamahkeun pikeun anjeun hiji artikel ngajéntrékeun kamampuhan utama na kaayaan kiwari.

Dina taun-taun ayeuna, alat sareng téknik pemrograman janten langkung populer pikeun ngimbangan watesan kernel Linux dina kasus dimana pamrosésan pakét kinerja tinggi diperyogikeun. Salah sahiji téknik anu paling populér tina jinis ieu disebut bypass kernel (kernel bypass) sarta ngidinan, bypassing lapisan jaringan kernel, pikeun ngalakukeun sagala processing pakét ti spasi pamaké. Bypassing kernel ogé ngalibatkeun ngadalikeun kartu jaringan tina spasi pamaké. Kalayan kecap sanésna, nalika damel sareng kartu jaringan, urang ngandelkeun supir spasi pamaké.

Ku mindahkeun kadali pinuh tina kartu jaringan ka program pamaké-spasi, urang ngurangan overhead kernel (konteks switching, processing lapisan jaringan, interrupts, jsb), nu rada penting lamun ngajalankeun di speeds 10Gb / s atawa saluhureuna. Kernel bypass ditambah kombinasi fitur séjén (processing bets) jeung tuning kinerja ati-ati (akuntansi NUMA, isolasi CPU, jsb) pakait jeung dasar-dasar ngolah jaringan-kinerja tinggi dina spasi pamaké. Meureun conto exemplary tina pendekatan anyar ieu ngolah pakét téh DPDK ti Intel (Data Plane Development Kit), sanaos aya alat sareng téknik anu terkenal, kalebet Cisco's VPP (Vector Packet Processing), Netmap sareng, tangtosna, Snabb.

Pangatur interaksi jaringan dina rohangan pangguna ngagaduhan sababaraha kalemahan:

  • Kernel OS mangrupa lapisan abstraksi pikeun sumber hardware. Kusabab program spasi pamaké kudu ngatur sumberdaya maranéhanana langsung, maranéhna ogé kudu ngatur hardware sorangan. Ieu sering hartosna kedah program supir anjeun nyalira.
  • Kusabab urang nyerah spasi kernel sagemblengna, urang ogé nyerah sakabéh pungsi jaringan disadiakeun ku kernel. Program ruang angkasa pangguna kedah nerapkeun deui fitur anu parantos disayogikeun ku kernel atanapi sistem operasi.
  • Program dijalankeun dina modeu sandbox, anu sacara serius ngabatesan interaksi sareng nyegah aranjeunna ngahijikeun sareng bagian sistem operasi anu sanés.

Intina, nalika jaringan dina rohangan pamaké, gains kinerja kahontal ku mindahkeun processing pakét ti kernel ka spasi pamaké. XDP ngalakukeun sabalikna: éta mindahkeun program jaringan tina rohangan pangguna (filter, solver, routing, jsb) kana rohangan kernel. XDP ngamungkinkeun urang pikeun ngalakukeun fungsi jaringan pas pakét pencét antarmuka jaringan sareng sateuacan ngamimitian naék kana subsistem jaringan kernel. Hasilna, laju ngolah pakét ningkat sacara signifikan. Nanging, kumaha kernel ngamungkinkeun pangguna pikeun ngaéksekusi programna dina rohangan kernel? Sateuacan ngawalon patarosan ieu, hayu urang tingali naon BPF.

BPF sareng eBPF

Sanaos nami ngabingungkeun, BPF (Berkeley Packet Filtering), saleresna, modél mesin virtual. Mesin virtual ieu asalna dirancang pikeun nanganan panyaring pakét, ku kituna namina.

Salah sahiji alat anu paling kasohor ngagunakeun BPF nyaéta tcpdump. Nalika nangkep pakét nganggo tcpdump pamaké éta bisa nangtukeun hiji éksprési pikeun nyaring pakét. Ngan pakét anu cocog sareng ekspresi ieu bakal direbut. Contona, ungkapan "tcp dst port 80” nujul kana sagala pakét TCP anjog kana port 80. compiler nu bisa shorten ekspresi ieu ku jalan ngarobah kana 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

Ieu naon program di luhur dasarna ngalakukeun:

  • Parentah (000): Ngamuat pakét dina offset 12, salaku kecap 16-bit, kana accumulator nu. Offset 12 pakait jeung étertipe pakét.
  • Parentah (001): ngabandingkeun nilai dina accumulator kalawan 0x86dd, nyaeta, kalawan nilai ethertype pikeun IPv6. Upami hasilna leres, maka counter program angkat ka instruksi (002), sareng upami henteu, teras ka (006).
  • Parentah (006): ngabandingkeun nilai jeung 0x800 (nilai ethertype pikeun IPv4). Upami jawabanna leres, programna angkat ka (007), upami henteu, teras ka (015).

Sareng saterasna dugi ka program panyaring pakét ngabalikeun hasil. Ieu biasana Boolean. Ngabalikeun nilai non-enol (parentah (014)) hartina pakét éta ditarima, sarta balik nilai enol (parentah (015)) hartina pakét teu ditarima.

Mesin virtual BPF sareng bytecode na diajukeun ku Steve McCann sareng Van Jacobson dina ahir 1992 nalika makalahna diterbitkeun. BSD Packet Filter: Arsitéktur Anyar pikeun Pamaké-Level Packet Capture, téhnologi ieu munggaran dibere dina konferensi Usenix dina usum 1993.

Kusabab BPF mangrupakeun mesin virtual, éta ngahartikeun lingkungan dimana program ngajalankeun. Salian bytecode, éta ogé ngahartikeun modél mémori angkatan (paréntah beban sacara implisit diterapkeun kana bets), registers (A sareng X; accumulator sareng indéks registers), neundeun mémori scratch, sareng counter program implisit. Narikna, bytecode BPF dimodelkeun saatos Motorola 6502 ISA. Salaku Steve McCann recalled di na laporan paripurna di Sharkfest '11, anjeunna wawuh jeung ngawangun 6502 ti poé SMA na programming on Apple II, jeung pangaweruh ieu dipangaruhan karyana ngarancang BPF bytecode.

Pangrojong BPF dilaksanakeun dina kernel Linux dina versi v2.5 sareng anu langkung luhur, tambihan utamina ku usaha Jay Schullist. Kodeu BPF tetep teu robih dugi ka 2011, nalika Eric Dumaset ngadesain ulang juru basa BPF pikeun ngajalankeun dina modeu JIT (Sumber: JIT pikeun saringan pakét). Saatos ieu, kernel, tinimbang napsirkeun bytecode BPF, tiasa langsung ngarobih program BPF kana arsitéktur target: x86, ARM, MIPS, jsb.

Engké, dina 2014, Alexey Starovoitov ngajukeun mékanisme JIT anyar pikeun BPF. Kanyataanna, JIT anyar ieu janten arsitéktur basis BPF anyar sareng disebut eBPF. Jigana duanana VMs coexisted pikeun sawatara waktu, tapi ayeuna pakét nyaring dilaksanakeun dumasar kana eBPF. Nyatana, dina seueur conto dokuméntasi modéren, BPF dipikaharti janten eBPF, sareng BPF klasik ayeuna katelah cBPF.

eBPF ngalegaan mesin virtual BPF klasik ku sababaraha cara:

  • Dumasar kana arsitektur 64-bit modern. eBPF migunakeun registers 64-bit sarta ngaronjatkeun jumlah registers sadia tina 2 (accumulator sarta X) ka 10. eBPF nyadiakeun ogé opcodes tambahan (BPF_MOV, BPF_JNE, BPF_CALL ...).
  • Dipisahkeun tina subsistem lapisan jaringan. BPF dihijikeun kana modél data angkatan. Kusabab ieu dipaké pikeun panyaring pakét, kode na ieu lokasina di subsistem nu nyadiakeun komunikasi jaringan. Tapi, mesin virtual eBPF henteu deui dihijikeun kana modél data sareng tiasa dianggo pikeun tujuan naon waé. Janten, ayeuna program eBPF tiasa dihubungkeun sareng tracepoint atanapi kprobe. Ieu muka jalan pikeun instrumentasi eBPF, analisa kinerja, sareng seueur kasus pamakean anu sanés dina konteks subsistem kernel sanés. Ayeuna kode eBPF aya dina jalur sorangan: kernel / bpf.
  • toko data global disebut Maps. Peta mangrupikeun toko nilai konci anu ngamungkinkeun pertukaran data antara rohangan pangguna sareng rohangan kernel. eBPF nyayogikeun sababaraha jinis peta.
  • Fungsi sekundér. Khususna, pikeun nulis ulang pakét, ngitung checksum, atanapi clone pakét. Pungsi ieu dijalankeun di jero kernel sareng sanés program rohangan-pamaké. Anjeun ogé tiasa nelepon sistem tina program eBPF.
  • Tungtung telepon. Ukuran program dina eBPF dugi ka 4096 bait. Fitur panggero buntut ngamungkinkeun hiji program eBPF pikeun mindahkeun kontrol ka program eBPF anyar sahingga bypass watesan ieu (nepi ka 32 program bisa numbu ku cara kieu).

eBPF: conto

Aya sababaraha conto pikeun eBPF dina sumber kernel Linux. Éta sadia dina sampel / bpf /. Pikeun nyusun conto ieu, kantun lebetkeun:

$ sudo make samples/bpf/

Kuring moal nulis conto anyar pikeun eBPF sorangan, tapi bakal ngagunakeun salah sahiji sampel sadia dina sampel / bpf /. Kuring gé kasampak di sababaraha bagian kode jeung ngajelaskeun kumaha gawéna. Salaku conto, kuring milih program tracex4.

Sacara umum, unggal conto dina sampel/bpf/ diwangun ku dua file. Dina hal ieu:

  • tracex4_kern.c, ngandung kode sumber pikeun dieksekusi dina kernel salaku bytecode eBPF.
  • tracex4_user.c, ngandung program ti rohangan pamaké.

Dina hal ieu, urang kudu compile tracex4_kern.c mun eBPF bytecode. Ayeuna di gcc euweuh backend pikeun eBPF. Untungna, clang tiasa kaluaran bytecode eBPF. Makefile ngagunakeun clang pikeun kompilasi tracex4_kern.c kana file obyék.

Kuring disebutkeun di luhur yén salah sahiji fitur paling narik tina eBPF nyaéta peta. tracex4_kern netepkeun hiji peta:

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 mangrupa salah sahiji loba jenis kartu ditawarkeun ku eBPF. Dina hal ieu, éta ngan hash a. Anjeun oge bisa geus noticed hiji iklan SEC("maps"). SEC mangrupikeun makro anu dianggo pikeun nyiptakeun bagian énggal tina file binér. Sabenerna, dina conto tracex4_kern dua bagian deui didefinisikeun:

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

Dua pungsi ieu ngamungkinkeun anjeun mupus éntri tina peta (kprobe/kmem_cache_free) jeung tambahkeun entri anyar kana peta (kretprobe/kmem_cache_alloc_node). Sadaya nami fungsi anu ditulis dina hurup kapital pakait sareng makro anu didefinisikeun dina bpf_helpers.h.

Upami kuring ngalungkeun bagian-bagian file obyék, kuring kedah ningali yén bagian-bagian énggal ieu parantos didefinisikeun:

$ 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

Aya ogé tracex4_user.c, program utama. Dasarna, program ieu ngadangukeun acara kmem_cache_alloc_node. Nalika kajadian sapertos kitu, kode eBPF anu saluyu dieksekusi. Kode ngaheéat atribut IP obyék kana peta, sarta obyék ieu lajeng looped ngaliwatan program utama. conto:

$ 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

Kumaha program rohangan pangguna sareng program eBPF aya hubunganana? Dina initialization tracex4_user.c ngamuat file obyék tracex4_kern.o ngagunakeun fungsi 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;
}

Bari ngalakukeun load_bpf_file usik didefinisikeun dina file eBPF ditambahkeun kana /sys/kernel/debug/tracing/kprobe_events. Ayeuna urang ngadangukeun acara ieu sareng program urang tiasa ngalakukeun hiji hal nalika éta kajantenan.

$ 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

Sadaya program sanés dina sampel/bpf/ disusun sami. Aranjeunna salawasna ngandung dua file:

  • XXX_kern.c: program eBPF.
  • XXX_user.c: program utama.

Program eBPF ngaidentipikasi peta sareng fungsi anu aya hubunganana sareng bagian. Nalika kernel ngaluarkeun kajadian tina jinis anu tangtu (contona, tracepoint), fungsi kabeungkeut dieksekusi. Kartu nyadiakeun komunikasi antara program kernel jeung program spasi pamaké.

kacindekan

Artikel ieu ngabahas BPF sareng eBPF sacara umum. Kuring terang aya seueur inpormasi sareng sumber daya ngeunaan eBPF ayeuna, janten kuring bakal nyarankeun sababaraha sumber deui pikeun diajar salajengna

Abdi nyarankeun maca:

sumber: www.habr.com

Tambahkeun komentar