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
里插入一条记录。 - 在
fexit
do_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