我正在尝试编写一个程序(用 C 语言),作为其功能之一,它会遍历 procfs 来查找打开的套接字并确定目标/源端口/地址等内容(类似于 netstat 和 lsof 所做的)。但是,一旦找到套接字文件,我不确定要使用哪个系统调用。例如,假设我已经打电话readlink
并/proc/123/fd/4
回来了socket:[56789]
。我可以用这些信息做什么,仅使用系统 API 来获取套接字的详细信息?
我尝试过继续跑步strace
,netstat
但不清楚发生了什么。我看到了一个read
,/proc/123/fdinfo/4
但我不明白那是什么意思。
例如,fdinfo
一个打开的连接(到 127.0.0.1:5000 的 TCP 连接)的文件显示
pos: 0
flags: 04002
mnt_id: 9
答案1
/proc/pid/fdinfo/fd
给出有关文件描述符的属性及其背后的打开文件描述的信息,并记录在过程(5)手册页。
它没有特定于套接字的信息。
历史上,在 Linux 上,有关套接字的信息可以从 中的文本文件获取/proc/net
,每个文件对应一个地址族和协议(例如/proc/net/unix
,/proc/net/udp6
...)。
但现在有一个替代方案网络链接更适合编程使用且更可靠(特别是/proc/net/unix
无法可靠地解析,因为它会破坏带有换行符的套接字文件)且功能丰富。
这记录在网络链接(7)手册页,特别是套接字诊断信息袜子诊断(7)(inet_diag
2011年从其他家族更名并扩展到其他家族本身tcpdiag
2005年起更名、扩建本身添加在2.5.2.12002 年)。另请参阅 kernel.org 上的信息:
它本身就是一个基于套接字的 API,应用程序使用它与内核进行通信。与其他套接字一样,您使用socket()
系统调用来创建套接字(作为AF_NETLINK
地址族),使用一些setsockopt()
s 来调整它和sendmsg()
/recvmsg()
发送/接收信息。
这就是ss
(from iproute2
) 使用的。虽然netstat
(from net-tools
),但lsfd
(from util-linux
) 似乎仍在使用/proc/net/...
文件接口。lsof
也可以在 Linux 上使用它作为其+E
选项。
较新版本strace
可以解码在这些套接字上发送和接收的消息,因此您可以使用以下命令查看它的运行情况ss
:
$ strace -qqe network ss -lt
socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_SOCK_DIAG) = 3
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [32768], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [1048576], 4) = 0
setsockopt(3, SOL_NETLINK, NETLINK_EXT_ACK, [1], 4) = 0
bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0
getsockname(3, {sa_family=AF_NETLINK, nl_pid=9352, nl_groups=00000000}, [12]) = 0
sendmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base=[{nlmsg_len=72, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_REQUEST|NLM_F_DUMP, nlmsg_seq=123456, nlmsg_pid=0}, {sdiag_family=AF_INET, sdiag_protocol=IPPROTO_TCP, idiag_ext=0, idiag_states=1<<TCP_CLOSE|1<<TCP_LISTEN, id={idiag_sport=htons(0), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[0, 0]}}], iov_len=72}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 72
recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base=NULL, iov_len=0}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_TRUNC}, MSG_PEEK|MSG_TRUNC) = 812
recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base=[[{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(4330), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8331, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=5, idiag_uid=997, idiag_inode=18774}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 5094], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(3306), idiag_dport=htons(0), idiag_src=inet_addr("127.0.0.1"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8332, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=80, idiag_uid=121, idiag_inode=4823}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 3733], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, freebind=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(22), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8333, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=128, idiag_uid=0, idiag_inode=2707}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 3774], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(44321), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8334, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=5, idiag_uid=0, idiag_inode=18553}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 5012], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(44322), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8335, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=128, idiag_uid=0, idiag_inode=18613}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 5135], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(44323), idiag_dport=htons(0), idiag_src=inet_addr("0.0.0.0"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8336, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=128, idiag_uid=0, idiag_inode=18614}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 5135], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]], [{nlmsg_len=116, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=123456, nlmsg_pid=9352}, {idiag_family=AF_INET, idiag_state=TCP_LISTEN, idiag_timer=0, idiag_retrans=0, id={idiag_sport=htons(631), idiag_dport=htons(0), idiag_src=inet_addr("127.0.0.1"), idiag_dst=inet_addr("0.0.0.0"), idiag_if=0, idiag_cookie=[8337, 0]}, idiag_expires=0, idiag_rqueue=0, idiag_wqueue=4096, idiag_uid=0, idiag_inode=3811}, [[{nla_len=5, nla_type=INET_DIAG_SHUTDOWN}, 0], [{nla_len=12, nla_type=INET_DIAG_CGROUP_ID}, 3610], [{nla_len=6, nla_type=INET_DIAG_SOCKOPT}, {is_icsk=1, mc_loop=1, mc_all=1}]]]], iov_len=32768}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 812
答案2
您首先需要阅读/proc/net/tcp
哪些列表,针对您的网络命名空间,所有打开的 TCP/IP 连接的详细信息(查看/proc/net/tcp6
TCP/IPv6 连接)。此信息包括关联的索引节点号。该文件中的示例行是
2: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 19691 1 0000000000000000 100 0 0 10 0
这00000000:0016
意味着套接字正在侦听0.0.0.0
端口 22。我们知道这是一个侦听套接字,因为对等地址 ( 00000000:0000
) 全部为零。我们可以看到这个套接字的索引节点是19691。
一旦掌握了这些号码,您就可以将它们与 中的列表进行匹配/proc/<pid>/fd
。例如,socket:[12345]
表示套接字位于 inode 12345。更好的是,您可以调用stat
并/proc/<pid>/fd/4
从对象中提取 inode 编号struct stat
。