Network Tracing

TCP Packet Okuma - HTTP

Önceki alıştırmada TCP bağlantı tamamlanmasını takip etmek için kprobe’ları kullandık. Şimdi gönderilen HTTP verisini yakalayacağız.

sendto() syscall’ına bağlanabilirdik, ancak bu aynı zamanda DEBUG_* çağrılarını da içeren vsock transfer’lerini de yakalar, bu da oldukça zahmetli bir deneyim yaratır.

Ayrıca bu, kprobe bölümüne hiç uymaz!

Bunun yerine yalnızca TCP trafiği için çağrılan tcp_sendmsg() fonksiyonuna bağlanalım.

tcp_sendmsg’den veri okuma

tcp_sendmsg’in imzasını net/ipv4/tcp.c dosyasında bulabiliriz:

int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);

Userspace buffer’a msg parametresi üzerinden erişebiliriz, ancak bu biraz karmaşıktır.

struct msghdr’nin basitleştirilmiş tanımı:

struct msghdr {
    struct iov_iter msg_iter;  // Iterator containing buffer info
    // ...
};

struct iov_iter {
    union {
        struct iovec __ubuf_iovec;  // Userspace buffer base/len
        // ...
    };
};

struct iovec {
    void *iov_base;
    u64   iov_len;
};

Yani msg->msg_iter->__ubuf_iovec.iov_base içinde saklanan adresi kullanarak userspace buffer’ı okuyacağız.

Pointer’ları doğrudan dereference edemeyeceğimizi unutmayın, bu yüzden her dolaylama seviyesini manuel olarak okumanız gerekecek. Her pointer’ın address space’ini göz önünde bulundurun.

HTTP request yapısı

HTTP request’leri metin tabanlıdır:

POST /api/data HTTP/1.1
Host: example.com
Authorization: Bearer secret_token_here
Content-Type: application/json

Header’lar \r\n (CRLF) ile ayrılır. Her header Name: Value formatındadır.

Geçmişte, eBPF’te herhangi bir string tabanlı protokol üzerinde işlem yapmak son derece zahmetliydi. Şanslısınız, Haziran 2025’ten bu yana bazı string operasyonlarını doğrudan yapabiliyoruz.

Bu alıştırmada iki fonksiyon kullanacağız:

  • bpf_strstr(char *haystack, char *needle): Substring bulur, index veya hata durumunda negatif değer döndürür
  • bpf_strchr(char* str, char c): Karakter bulur, index veya hata durumunda negatif değer döndürür

Örneğin,

int auth_pos = bpf_strstr(buf, "Authorization: Bearer ");

auth_pos, bulunursa needle’ın konumunu içerecektir.

Biraz matematik ile token’ın nerede başlaması gerektiğini bulabilir ve oradan satır sonuna (\r ile işaretlenmiş) kadar okuyabilirsiniz.

Satır sonuna ulaşmak için şunu kullanabilirsiniz:

int token_pos = bpf_strchr(&buf[token_start], '\r');

Bu size token_ptr’den başlayarak ilk \r’nin konumunu verecektir.

Görev

Bir program Authorization: Bearer <token> header’ı ile HTTP request’i yapıyor.

Yapmanız gereken

Header’daki token’ı gönderin.

Quick reference
Extract the Nth parameter from kprobe context
Function PT_REGS_PARM{1-8} Full documentation
Args:
struct pt_regs* ctxkprobe context containing CPU registers
Returns value of Nth function parameter
Read string from user space into kernel buffer
Function bpf_probe_read_user_str Full documentation
Args:
void* dstkernel buffer to read into
u32 sizemaximum bytes to read
const void* srcuser space pointer to string
On success, returns number of bytes read (including null terminator)
On error, returns negative error code
Read bytes from user space into kernel buffer
Function bpf_probe_read_user Full documentation
Args:
void* dstkernel buffer to read into
u32 sizebytes to read
const void* srcuser space pointer
On success, returns 0
On error, returns negative error code
Read bytes from kernel space into kernel buffer
Function bpf_probe_read_kernel Full documentation
Args:
void* dstkernel buffer to read into
u32 sizebytes to read
const void* srckernel space pointer
On success, returns 0
On error, returns negative error code
Read string from kernel space into kernel buffer
Function bpf_probe_read_kernel_str Full documentation
Args:
void* dstkernel buffer to read into
u32 sizemaximum bytes to read
const void* srckernel space pointer to string
On success, returns number of bytes read (including null terminator)
On error, returns negative error code
Run your code to see execution events here