eBPF Talk: 在 bpf 里将 fd 和 sock 关联起来
文章目录
对于 socket 编程,在用户态看到的只能是 fd;而在内核态,大部分时候看到的是 sock。
fd 和 sock 之间的关系,是在内核态的 syscall 里建立的;所以,在 connect 和 accept 等 syscall 里,可以看到 fd 找 sock 的过程。
TL;DR 通过 hook connect 和 accept syscall,就可以将 fd 和 sock 关联起来。
connect syscall
先来看看 connect syscall 里是如何找到 sock 的。
|
|
处理逻辑:
- 通过
fdget获取fd对应的struct fd; - 通过
fd_file获取struct file; - 通过
sock_from_file()获取struct socket。
而 sock_from_file() 的实现如下:
|
|
accept syscall
再来看看 accept syscall 里是如何找到 sock 的。
|
|
处理逻辑:
- 通过
fdget获取fd对应的struct fd; - 通过
fd_file获取struct file; - 通过
sock_from_file()获取struct socket; - 通过
sock_alloc()创建新的struct socket; - 通过
sock_alloc_file()创建新的struct file; - 通过
ops->accept()等待新连接的建立; - 通过
fd_install()将新的struct file关联到fd。 - 最后返回新的
fd。
hook connect syscal 拿到 fd 以及五元组信息
在 connect syscall 结束后,fd 对应的 sock 里便有了五元组信息。
|
|
- 在
fentry__sys_connect里,向fd_info_map里插入一条记录。 - 在
fentry__sys_connect_file里,将file更新到fd_info_map的记录里。 - 在
fexit__sys_connect里,从fd_info_map里查找记录,然后打印五元组信息。
通过跟 inet_sock_set_state tracepoint 作对比,可以看到 __sys_connect 里的 sock 信息和 inet_sock_set_state 里的 sk 信息是一致的。
|
|
hook accept syscall 拿到新连接的 fd 以及五元组信息
在 accept syscall 结束后,返回了新的 fd;而且这个 fd 对应的 sock 里保存了新连接的五元组信息。
|
|
- 在
fentry__sys_accept4里,向fd_info_map里插入一条记录。 - 在
fexitdo_accept里,将file和newfile更新到fd_info_map的记录里。 - 在
fexit__sys_accept4里,从fd_info_map里查找记录,然后打印五元组信息。
通过跟 inet_sock_set_state tracepoint 作对比,可以看到 __sys_accept4 里的 sock 信息和 inet_sock_set_state 里的 sk 信息是一致的。
|
|
总结
通过 hook connect 和 accept syscall,可以将 fd 和 sock 关联起来;然后通过 fd 对应的 sock,就可以拿到五元组信息。
P.S. 本文的代码在 learn-by-example fd-socket。
文章作者 Leon Hwang
上次更新 2024-12-09