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);
}