需改ebpf程序探测内核,测试性能,验证想法,更新笔记。

This commit is contained in:
1iaan
2026-02-13 10:14:41 +00:00
parent 68bb4b3f9c
commit c72314291a
16 changed files with 560 additions and 230 deletions

View File

@@ -1,80 +1,133 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_endian.h>
#include "replica.h"
char LICENSE[] SEC("license") = "Dual BSD/GPL";
#define FLAG_SSYNC_HAPPENED 0
#define TARGET_PORT 8888
/* ================= BPF Maps ================= */
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} events SEC(".maps");
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, 1);
} flags SEC(".maps");
/* __completed_cmd(const uint8_t *cmd, size_t len, unsigned long long seq); */
SEC("uprobe//home/lian/share/9.1-kvstore/kvstore:__completed_cmd")
int BPF_KPROBE(handle_completed_cmd,
const __u8 *cmd, size_t len, __u64 seq)
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 26); // 64MB
} rb SEC(".maps");
/* ================= Helper Functions ================= */
// 无需 process filter改用 socket port filter
/* ================= Kernel Hooks (TCP Layer) ================= */
/*
* 使用 kprobe 挂载 tcp_rcv_established
* 此时 skb 包含完整的 TCP 包Header + Payload数据在内核态。
*/
SEC("kprobe/tcp_rcv_established")
int BPF_KPROBE(trace_tcp_rcv, struct sock *sk, struct sk_buff *skb)
{
struct replica_event evt = {};
__u32 copy_len;
// 1. 检查 SSYNC 标志是否已开启 (只在全量同步后开始抓包)
__u32 flag_key = FLAG_SSYNC_HAPPENED;
__u32 *flag_val = bpf_map_lookup_elem(&flags, &flag_key);
if (!flag_val || *flag_val == 0)
return 0;
evt.type = EVENT_COMPLETED_CMD;
evt.complete.seq = seq;
// 2. 过滤端口 8888
// sk->sk_num 存储的是 Host Byte Order 的本地端口
__u16 lport = BPF_CORE_READ(sk, __sk_common.skc_num);
if (lport != TARGET_PORT)
return 0;
copy_len = len;
if (copy_len > MAX_CMD_LEN)
copy_len = MAX_CMD_LEN;
// 3. 计算数据长度
// 在 tcp_rcv_established 中skb->len 是 (TCP Header + Data) 的长度
// skb->data 指向 TCP Header 的起始位置
unsigned int skb_len = BPF_CORE_READ(skb, len);
// 读取 TCP Header 长度 (doff 字段,单位是 4字节)
// 需要读取 skb->data 指向的内存的前几个字节来获取 doff
unsigned char *skb_data = BPF_CORE_READ(skb, data);
// 读取 TCP Header 的第 12 个字节 (包含 Data Offset)
// Offset 12: Data Offset (4 bits) | Reserved (3 bits) | NS (1 bit)
unsigned char doff_byte;
if (bpf_probe_read_kernel(&doff_byte, 1, skb_data + 12) < 0)
return 0;
unsigned int tcp_hdr_len = (doff_byte >> 4) * 4;
// 计算 Payload 长度
if (skb_len <= tcp_hdr_len)
return 0; // 只有 ACK 没有数据,或者是控制包
evt.complete.len = copy_len;
unsigned int payload_len = skb_len - tcp_hdr_len;
if (cmd)
bpf_probe_read_user(evt.complete.cmd, copy_len, cmd);
// 4. 准备 RingBuffer 数据
struct replica_event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
bpf_perf_event_output(ctx, &events,
BPF_F_CURRENT_CPU,
&evt, sizeof(evt));
e->type = EVENT_COMPLETED_CMD;
// 截断超长数据
if (payload_len > MAX_CMD_LEN)
e->complete.len = MAX_CMD_LEN;
else
e->complete.len = payload_len;
// 5. 核心修改:使用 bpf_probe_read_kernel 读取数据
// 数据起始位置 = skb->data + tcp_hdr_len
if (bpf_probe_read_kernel(&e->complete.cmd[0], e->complete.len, skb_data + tcp_hdr_len) < 0) {
bpf_ringbuf_discard(e, 0);
return 0;
}
bpf_ringbuf_submit(e, 0);
return 0;
}
/* __ssync(const uint8_t *ip, uint32_t ip_len, int port, unsigned long long seq); */
/* ================= Uprobe Hooks================= */
SEC("uprobe//home/lian/share/9.1-kvstore/kvstore:__ssync")
int BPF_KPROBE(handle_ssync,
const __u8 *ip, __u32 ip_len, int port, __u64 seq)
const __u8 *ip, __u32 ip_len, int port, __u64 seq_unused)
{
struct replica_event evt = {};
__u32 key = FLAG_SSYNC_HAPPENED;
__u32 val = 1;
bpf_map_update_elem(&flags, &key, &val, BPF_ANY);
evt.type = EVENT_SSYNC;
evt.sync.seq = seq;
evt.sync.port = port;
struct replica_event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e) return 0;
e->type = EVENT_SSYNC;
e->sync.port = port;
__u32 copy_len = ip_len;
if (copy_len > sizeof(evt.sync.ip))
copy_len = sizeof(evt.sync.ip);
if (copy_len > sizeof(e->sync.ip)) copy_len = sizeof(e->sync.ip);
if (ip) bpf_probe_read_user(e->sync.ip, copy_len, ip);
if (ip)
bpf_probe_read_user(evt.sync.ip, copy_len, ip);
bpf_perf_event_output(ctx, &events,
BPF_F_CURRENT_CPU,
&evt, sizeof(evt));
bpf_ringbuf_submit(e, 0);
return 0;
}
/* __sready(void); */
SEC("uprobe//home/lian/share/9.1-kvstore/kvstore:__sready")
int BPF_KPROBE(handle_sready)
{
struct replica_event evt = {};
struct replica_event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e) return 0;
evt.type = EVENT_SREADY;
bpf_perf_event_output(ctx, &events,
BPF_F_CURRENT_CPU,
&evt, sizeof(evt));
e->type = EVENT_SREADY;
bpf_ringbuf_submit(e, 0);
return 0;
}