You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BPF探测listen_sock的qlen失败求助:tcp_synack_timer等函数调用问题

解决kprobe探测TCP SYN队列qlen字段失败的问题

咱们先拆解你遇到的问题,分析可能的原因,再给出几个可行的替代方案:

一、先排查现有代码的问题

1. 函数探测失败的常见原因

你提到已经在available_filtered_functions里找到了目标函数,但还是探测不到,大概率是这两个原因:

  • 函数被编译器内联优化:内核编译时如果开启了CONFIG_OPTIMIZE_INLINING,很多小函数会被内联到调用者中,导致kprobe无法挂钩(因为符号对应的独立函数代码不存在)。你可以用bpftool probe list --functions | grep tcp_synack_timer确认,如果输出为空,基本可以确定是被内联了。

  • 结构体类型转换错误:你的第二段代码里的结构体层级解析完全错了,导致访问非法内存,程序可能静默失败。正确的结构体关联逻辑是:
    struct sock → struct inet_connection_sock(包含icsk_accept_queue成员)→ struct request_sock_queue(包含listen_opt成员)→ struct listen_sock(包含qlen字段)

    修正后的代码应该是这样:

    #!/usr/local/bin/bpftrace
    #include <net/sock.h>
    #include <net/inet_connection_sock.h>
    #include <net/request_sock.h>
    
    kprobe:inet_csk_reqsk_queue_prune {
        $sk = (struct sock *)arg0;
        $inet = (struct inet_connection_sock *)$sk;
        $rsq = &$inet->icsk_accept_queue;  // 正确获取request_sock_queue指针
        $ls = $rsq->listen_opt;            // 从队列中取出listen_sock
        if ($ls != 0) {  // 避免空指针访问导致的探测失败
            printf("-------------------\n");
            printf("inet_csk_reqsk_queue_prune: %s pid: %d\n", comm, pid);
            printf("listen_sock qlen: %d\n", $ls->qlen);
            printf("--------------------\n");
        }
    }
    

二、替代探测方案

如果上述修正后还是无法正常探测,试试下面这些更稳定的方法:

1. 换用更适合的TCP函数探测点

有些TCP核心函数的探测成功率更高,且能直接获取SYN队列长度:

  • tcp_v4_syn_recv_sock:处理SYN包并创建request_sock时触发,可在kretprobe中获取返回的request_sock,进而关联到listen_sock:
    #!/usr/local/bin/bpftrace
    #include <net/sock.h>
    #include <net/request_sock.h>
    
    kretprobe:tcp_v4_syn_recv_sock {
        $req = (struct request_sock *)retval;
        if ($req != 0) {
            $ls = $req->rsk_listener;
            printf("SYN queue qlen after new SYN: %d\n", $ls->qlen);
        }
    }
    
  • inet_csk_accept:应用层调用accept()时触发,可直接读取listen_sock的qlen:
    #!/usr/local/bin/bpftrace
    #include <net/sock.h>
    #include <net/inet_connection_sock.h>
    #include <net/request_sock.h>
    
    kprobe:inet_csk_accept {
        $sk = (struct sock *)arg0;
        $inet = (struct inet_connection_sock *)$sk;
        $rsq = &$inet->icsk_accept_queue;
        $ls = $rsq->listen_opt;
        if ($ls != 0) {
            printf("Accept triggered, current SYN queue qlen: %d\n", $ls->qlen);
        }
    }
    

2. 用fprobe替代kprobe(内核5.15+支持)

fprobe是内核5.15新增的探测机制,支持探测内联函数和更多隐藏符号。如果你的内核版本足够新,可以试试:

#!/usr/local/bin/bpftrace
#include <net/sock.h>
#include <net/inet_connection_sock.h>
#include <net/request_sock.h>

fprobe:tcp_synack_timer {
    // tcp_synack_timer的参数是struct sock *sk
    $sk = (struct sock *)arg0;
    $inet = (struct inet_connection_sock *)$sk;
    $rsq = &$inet->icsk_accept_queue;
    $ls = $rsq->listen_opt;
    if ($ls != 0) {
        printf("tcp_synack_timer triggered, SYN queue qlen: %d\n", $ls->qlen);
    }
}

3. 直接监控qlen字段的内存变化

如果函数探测都不行,还可以监控listen_sock中qlen字段的读写操作。首先需要获取字段的内存偏移量:

bpftool btf dump file /sys/kernel/btf/vmlinux format c | grep -n "qlen" | grep "struct listen_sock"

假设偏移量是0x10,可以用原子操作的探测点来监控:

#!/usr/local/bin/bpftrace
kprobe:__atomic_add_fetch {
    // 过滤操作的内存地址是否匹配listen_sock的qlen字段偏移
    if (arg1 == 0x10) {  // 替换为实际获取到的偏移量
        $qlen = *(int *)arg0;
        printf("SYN queue qlen updated to: %d\n", $qlen);
    }
}

这种方法精度稍低,需要结合业务场景过滤无关操作,但在极端情况下可以作为备选。

三、验证探测有效性

每次修改代码后,用bpftrace -v运行,查看是否有could not attach probe或内存访问错误的提示。另外,可以用hping3或tcpdump发送SYN包触发探测点,验证输出是否符合预期。

内容的提问来源于stack exchange,提问作者Panagiotis Papoulidis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 16:18:14