BPF 例子 - 观测某个 tcp 连接的状态变化

给2个参数, 分别是 IP 和端口, 观察符合找个条件的tcp 连接的状态变化.

这个例子中本想同时对比 IP 和 port, 但是对于IP 遇到一个问题.

常见的 IPv4 是这么写的(字符串): 145.23.45.23
$sk->__sk_common.skc_daddr 拿到的地址 (unsigned int32): 0X91172d17.
使用 ntop 函数转换后是一个 inet_t, 虽然文档死硬说是 Sting 表示形式: inet_t = “145.23.45.23”

那么为了比较2个 IP 地址, 你想把 0X91172d17 转成 inet_t = “145.23.45.23”, 但是还不能比, 不能用 String 和 inet_t 比较.
要么把 145.23.45.23 通过 pton 转成 uint8[], 然后再通过强转 (uint32)pton("145.23.45.23")) 再和 0X91172d17 比较.

关键的关键是 pton 的参数一定要是 字符串常量, 变量不行, 因为它要在编译时知道类型. TMD.

#!/usr/bin/env bpftrace

#ifndef BPFTRACE_HAVE_BTF
#include <linux/socket.h>
#include <net/sock.h>
#else
#include <sys/socket.h>
#endif

BEGIN
{
    @host = "127.0.0.1";
    @port = (uint16)80;
    if ("" != str($1)) {
        @host = str($1);
    }
    if ("" != str($2)) {
        @port = (uint16)$2;
    }

    printf("looking for tcp connection related to : %s:%d\n", @host, @port);
    printf("%39s:%-6s %39s:%-6s %-10s -> %-10s\n", "src IP", "src port", "dest ip", "dest port", "old state", "new state");
    @states[1] = "ESTABLISHED";
    @states[2] = "SYN_SENT";
    @states[3] = "SYN_RECV";
    @states[4] = "FIN_WAIT1";
    @states[5] = "FIN_WAIT2";
    @states[6] = "TIME_WAIT";
    @states[7] = "CLOSE";
    @states[8] = "CLOSE_WAIT";
    @states[9] = "LAST_ACK";
    @states[10] = "LISTEN";
    @states[11] = "CLOSING";
    @states[12] = "NEW_SYN_RECV";
}

kfunc:vmlinux:tcp_set_state {
    $sk = ((struct sock *) args->sk);
    $inet_family = $sk->__sk_common.skc_family;
    if ($inet_family == AF_INET) {
      $daddr = ntop($sk->__sk_common.skc_daddr);
      $saddr = ntop($sk->__sk_common.skc_rcv_saddr);
    } else {
      $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8);
      $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8);
    }
    $lport = $sk->__sk_common.skc_num;
    $dport = $sk->__sk_common.skc_dport;

    $dport = bswap($dport);

    if (($dport == @port) || ( $lport == @port)) {
        $curState = @states[args.state];
        $key = str($dport);
        $oldState = @keyMap[$daddr, $key];
        if ($oldState == "") {
            $oldState = "NONE";
        }
        @keyMap[$daddr, $key] = $curState;

        printf("%39s:%-6d %39s:%-6d %-10s -> %-10s\n", $saddr, $lport, $daddr, $dport, $oldState, $curState);
    }
}

END {
    clear(@states);
    clear(@keyMap);
    clear(@host);
    clear(@port);
}

标签: none

添加新评论