Men-debug jaringan menggunakan eBPF (RHEL 8 Beta)

Semua dengan liburan yang lalu!

Kami memutuskan untuk mencurahkan artikel pertama kami setelah liburan ke Linux, yaitu untuk kursus Administrator Linux kami yang luar biasa, yang kami miliki dalam kelompok kursus yang paling dinamis, yaitu, dengan materi dan praktik yang paling relevan. Yah dan, karenanya, kami menawarkan artikel menarik dan pelajaran terbuka .

Diposting oleh Matteo Croce
Judul asli: debugging jaringan dengan eBPF (RHEL 8 Beta)

Pendahuluan

Jaringan adalah pengalaman yang menyenangkan, tetapi masalah tidak selalu dihindari. Pemecahan masalah bisa rumit, seperti mencoba mereproduksi perilaku yang salah yang terjadi "di lapangan."

Untungnya, ada alat yang dapat membantu dengan ini: ruang nama jaringan, mesin virtual, tc dan netfilter . Pengaturan jaringan sederhana dapat direproduksi menggunakan ruang nama jaringan dan perangkat veth, sedangkan pengaturan yang lebih kompleks memerlukan menghubungkan mesin virtual dengan jembatan perangkat lunak dan menggunakan alat jaringan standar, seperti iptables atau tc , untuk mensimulasikan perilaku yang salah. Jika ada masalah dengan respons ICMP yang dihasilkan ketika server SSH iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable , iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable di namespace yang benar dapat membantu menyelesaikan masalah.

Artikel ini menjelaskan cara memecahkan masalah jaringan yang kompleks dengan eBPF (extended BPF) , versi lanjutan dari Berkeley Packet Filter. eBPF adalah teknologi yang relatif baru, proyek ini pada tahap awal, sehingga dokumentasi dan SDK belum siap. Tapi mari kita berharap untuk perbaikan, terutama karena XDP (eXpress Data Path) dikirimkan bersama Red Hat Enterprise Linux 8 Beta , yang dapat Anda unduh dan jalankan sekarang.

eBPF tidak akan menyelesaikan semua masalah, tetapi masih merupakan alat debugging jaringan yang kuat yang perlu diperhatikan. Saya yakin itu akan memainkan peran yang sangat penting di masa depan jaringan.



Masalah

Saya sedang men-debug masalah jaringan Open vSwitch (OVS) yang melibatkan instalasi yang sangat kompleks: beberapa paket TCP tersebar dan dikirim dalam urutan yang salah, dan bandwidth mesin virtual turun dari stabil 6 Gb / s menjadi berfluktuasi 2-4 Gb / s. Analisis menunjukkan bahwa paket TCP pertama dari setiap koneksi dengan flag PSH dikirim dalam urutan yang salah: hanya yang pertama dan hanya satu per koneksi.

Saya mencoba mereproduksi pengaturan ini dengan dua mesin virtual dan, setelah banyak artikel bantuan dan permintaan pencarian, saya menemukan bahwa iptables atau nftables tidak dapat memanipulasi flag TCP, sementara tc dapat, tetapi hanya dengan menimpa flag dan mengganggu koneksi baru dan TCP secara umum.

Dimungkinkan untuk menyelesaikan masalah dengan kombinasi iptables , conntrack , dan tc , tetapi saya memutuskan bahwa ini adalah pekerjaan hebat bagi eBPF.

Apa itu eBPF?

eBPF adalah versi yang disempurnakan dari Berkeley Packet Filter. Dia membawa banyak perbaikan ke BPF. Secara khusus, ini memungkinkan Anda untuk menulis di memori, dan tidak hanya membaca, sehingga paket tidak hanya dapat difilter, tetapi juga diedit.

Seringkali eBPF hanya disebut BPF, dan BPF itu sendiri disebut cBPF (klasik (klasik) BPF), sehingga kata "BPF" dapat digunakan untuk berarti kedua versi, tergantung pada konteksnya: dalam artikel ini saya selalu berbicara tentang versi yang diperluas.

β€œUnder the hood” eBPF memiliki mesin virtual yang sangat sederhana yang dapat mengeksekusi fragmen kecil bytecode dan mengedit beberapa buffer memori. Ada batasan dalam eBPF yang melindunginya dari penggunaan jahat:

  • Siklus dilarang sehingga program selalu berakhir pada waktu tertentu;
  • Itu hanya dapat mengakses memori melalui stack and scratch buffer;
  • Hanya fungsi kernel yang diizinkan yang bisa dipanggil.

Suatu program dapat dimuat ke dalam kernel dengan berbagai cara menggunakan debugging dan tracing . Dalam kasus kami, eBPF tertarik untuk bekerja dengan subsistem jaringan. Ada dua cara untuk menggunakan program eBPF:

  • Terhubung melalui XDP ke awal jalur RX kartu jaringan fisik atau virtual;
  • Terhubung melalui tc ke qdisc dalam input atau output.

Untuk membuat program eBPF untuk menghubungkan, cukup tulis kode C dan konversikan menjadi bytecode. Berikut ini adalah contoh sederhana menggunakan XDP:

 SEC("prog") int xdp_main(struct xdp_md *ctx) { void *data_end = (void *)(uintptr_t)ctx->data_end; void *data = (void *)(uintptr_t)ctx->data; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct icmphdr *icmph = (struct icmphdr *)(iph + 1); /* sanity check needed by the eBPF verifier */ if (icmph + 1 > data_end) return XDP_PASS; /* matched a pong packet */ if (eth->h_proto != ntohs(ETH_P_IP) || iph->protocol != IPPROTO_ICMP || icmph->type != ICMP_ECHOREPLY) return XDP_PASS; if (iph->ttl) { /* save the old TTL to recalculate the checksum */ uint16_t *ttlproto = (uint16_t *)&iph->ttl; uint16_t old_ttlproto = *ttlproto; /* set the TTL to a pseudorandom number 1 < x < TTL */ iph->ttl = bpf_get_prandom_u32() % iph->ttl + 1; /* recalculate the checksum; otherwise, the IP stack will drop it */ csum_replace2(&iph->check, old_ttlproto, *ttlproto); } return XDP_PASS; } char _license[] SEC("license") = "GPL"; 

Cuplikan di atas, tanpa include ekspresi, helper dan kode opsional, adalah program XDP yang mengubah TTL dari balasan gema ICMP yang diterima, yaitu pong, dengan nomor acak. Fungsi utama mendapatkan struktur xdp_md , yang berisi dua petunjuk awal dan akhir paket.

Untuk mengkompilasi kode kami ke dalam bytecode eBPF, diperlukan kompiler dengan dukungan yang sesuai. Dentang mendukungnya dan membuat bytecode eBPF dengan menetapkan bpf sebagai target pada waktu kompilasi:

 $ clang -O2 -target bpf -c xdp_manglepong.c -o xdp_manglepong.o 

Perintah di atas membuat file yang, pada pandangan pertama, tampak seperti file objek normal, tetapi setelah diperiksa lebih dekat, ternyata tipe komputer yang ditentukan adalah Linux eBPF, dan bukan tipe asli sistem operasi:

 $ readelf -h xdp_manglepong.o ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Linux BPF <--- HERE [...] 

Setelah menerima pembungkus file objek biasa, program eBPF siap untuk mengunduh dan terhubung ke perangkat melalui XDP. Ini dapat dilakukan dengan menggunakan ip dari paket iproute2 dengan sintaks berikut:

 # ip -force link set dev wlan0 xdp object xdp_manglepong.o verbose 

Perintah ini menentukan antarmuka target wlan0 dan, berkat opsi-force, menimpa kode eBPF yang sudah ada yang telah dimuat. Setelah memuat byBode eBPF, sistem berperilaku sebagai berikut:

 $ ping -c10 192.168.85.1 PING 192.168.85.1 (192.168.85.1) 56(84) bytes of data. 64 bytes from 192.168.85.1: icmp_seq=1 ttl=41 time=0.929 ms 64 bytes from 192.168.85.1: icmp_seq=2 ttl=7 time=0.954 ms 64 bytes from 192.168.85.1: icmp_seq=3 ttl=17 time=0.944 ms 64 bytes from 192.168.85.1: icmp_seq=4 ttl=64 time=0.948 ms 64 bytes from 192.168.85.1: icmp_seq=5 ttl=9 time=0.803 ms 64 bytes from 192.168.85.1: icmp_seq=6 ttl=22 time=0.780 ms 64 bytes from 192.168.85.1: icmp_seq=7 ttl=32 time=0.847 ms 64 bytes from 192.168.85.1: icmp_seq=8 ttl=50 time=0.750 ms 64 bytes from 192.168.85.1: icmp_seq=9 ttl=24 time=0.744 ms 64 bytes from 192.168.85.1: icmp_seq=10 ttl=42 time=0.791 ms --- 192.168.85.1 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 125ms rtt min/avg/max/mdev = 0.744/0.849/0.954/0.082 ms 

Setiap paket melewati eBPF, yang pada akhirnya membuat beberapa perubahan dan memutuskan apakah akan menjatuhkan paket atau melewatkannya.

Bagaimana eBPF Dapat Membantu

Kembali ke masalah jaringan asli, kami ingat bahwa perlu menandai beberapa flag TCP, satu per koneksi, dan iptables atau tc tidak dapat melakukan ini. Menulis kode untuk skenario ini tidak sulit sama sekali: mengkonfigurasi dua mesin virtual yang terhubung oleh jembatan OVS, dan cukup menghubungkan eBPF ke salah satu perangkat VM virtual.

Kedengarannya seperti solusi yang bagus, tetapi perlu diingat bahwa XDP hanya mendukung pemrosesan paket yang diterima, dan menghubungkan eBPF ke jalur rx dari mesin virtual penerima tidak akan memiliki efek pada sakelar.

Untuk mengatasi masalah ini, eBPF harus dimuat menggunakan tc dan terhubung ke jalur output VM, karena tc dapat memuat dan menghubungkan program-program eBPF ke qdisk. Untuk menandai paket yang meninggalkan host, eBPF harus terhubung ke output qdisk.

Saat memuat program eBPF, ada beberapa perbedaan antara XDP dan tc API: secara default, nama bagian yang berbeda, jenis struktur argumen fungsi utama, nilai pengembalian yang berbeda. Tapi ini bukan masalah. Di bawah ini adalah cuplikan dari program yang menandai TCP saat bergabung dengan tindakan tc:

 #define RATIO 10 SEC("action") int bpf_main(struct __sk_buff *skb) { void *data = (void *)(uintptr_t)skb->data; void *data_end = (void *)(uintptr_t)skb->data_end; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct tcphdr *tcphdr = (struct tcphdr *)(iph + 1); /* sanity check needed by the eBPF verifier */ if ((void *)(tcphdr + 1) > data_end) return TC_ACT_OK; /* skip non-TCP packets */ if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) return TC_ACT_OK; /* incompatible flags, or PSH already set */ if (tcphdr->syn || tcphdr->fin || tcphdr->rst || tcphdr->psh) return TC_ACT_OK; if (bpf_get_prandom_u32() % RATIO == 0) tcphdr->psh = 1; return TC_ACT_OK; } char _license[] SEC("license") = "GPL"; 

Kompilasi menjadi bytecode dilakukan seperti yang ditunjukkan pada contoh XDP di atas menggunakan yang berikut:

 clang -O2 -target bpf -c tcp_psh.c -o tcp_psh.o 

Tetapi unduhannya berbeda:

 # tc qdisc add dev eth0 clsact # tc filter add dev eth0 egress matchall action bpf object-file tcp_psh.o 

Sekarang eBPF dimuat di tempat yang tepat dan paket yang meninggalkan VM ditandai. Setelah memeriksa paket yang diterima di VM kedua, kita akan melihat yang berikut:



tcpdump mengkonfirmasi bahwa kode eBPF baru berfungsi, dan sekitar 1 dari setiap 10 paket TCP memiliki flag PSH yang ditetapkan. Hanya 20 baris kode-C yang diperlukan untuk secara selektif menandai paket TCP yang meninggalkan mesin virtual, mereproduksi kesalahan yang terjadi "dalam pertempuran", dan semua tanpa mengkompilasi ulang atau bahkan memulai kembali! Ini sangat menyederhanakan verifikasi perbaikan Open vSwitch , yang tidak mungkin dicapai dengan alat lain.

Kesimpulan

eBPF adalah teknologi yang cukup baru, dan masyarakat memiliki pendapat yang jelas tentang implementasinya. Perlu juga dicatat bahwa proyek berbasis eBPF, seperti bpfilter , menjadi lebih populer, dan sebagai hasilnya, banyak pemasok peralatan mulai menerapkan dukungan eBPF langsung pada kartu jaringan.

eBPF tidak akan menyelesaikan semua masalah, jadi jangan menyalahgunakannya, tetapi tetap saja itu merupakan alat yang sangat kuat untuk debugging jaringan dan patut mendapat perhatian. Saya yakin itu akan memainkan peran penting di masa depan jaringan.

AKHIR

Kami menunggu komentar Anda di sini, dan kami juga mengundang Anda untuk mengunjungi pelajaran terbuka kami, di mana jika Anda dapat mengajukan pertanyaan juga.

Source: https://habr.com/ru/post/id436528/


All Articles