SOCK_DIAG - Linux手册页
Linux程序员手册 第7部分
更新日期: 2020-06-09
名称
sock_diag-获取有关套接字的信息
语法
#include <sys/socket.h> #include <linux/sock_diag.h> #include <linux/unix_diag.h> /* for UNIX domain sockets */ #include <linux/inet_diag.h> /* for IPv4 and IPv6 sockets */ diag_socket = socket(AF_NETLINK, socket_type, NETLINK_SOCK_DIAG);
说明
sock_diag netlink子系统提供了一种从内核获取有关各种地址系列套接字的信息的机制。该子系统可用于获取有关各个套接字的信息或请求套接字列表。
在请求中,调用者可以指定要获取的有关套接字的其他信息,例如,内存信息或特定于地址系列的信息。
请求套接字列表时,调用者可以指定内核将应用的过滤器,以选择要报告的套接字子集。目前,只能按状态(连接,监听等)筛选套接字。
请注意,sock_diag仅报告那些具有名称的套接字。也就是说,要么是用bind(2)显式绑定的套接字,要么是自动绑定到某个地址的套接字(例如,通过connect(2))。这是通过/ proc / net / unix,/ proc / net / tcp,/ proc / net / udp等可用的同一组套接字。
Request
该请求以netlink(7)中描述的struct nlmsghdr标头开始,其中nlmsg_type字段设置为SOCK_DIAG_BY_FAMILY。其后是特定于地址系列的标头,该标头以所有地址系列共享的公共部分开头:
struct sock_diag_req { __u8 sdiag_family; __u8 sdiag_protocol; };
此结构的字段如下:
- sdiag_family
- 地址族。应该将其设置为适当的AF_ *常量。
- sdiag_protocol
- 取决于sdiag_family。对于AF_INET和AF_INET6,应将其设置为适当的IPPROTO_ *常数,否则应设置为0。
如果struct nlmsghdr标头的nlmsg_flags字段设置了NLM_F_DUMP标志,则意味着正在请求套接字列表;否则,它是有关单个套接字的查询。
Response
响应以struct nlmsghdr标头开始,后跟特定于地址族的对象数组。可以使用netlink(3)API中的标准NLMSG_ *宏访问该数组。
每个对象都是NLA(网络链接属性)列表,将使用rtnetlink(3)API中的RTA_ *宏对其进行访问。
UNIX domain sockets
对于UNIX域套接字,请求以以下结构表示:
struct unix_diag_req { __u8 sdiag_family; __u8 sdiag_protocol; __u16 pad; __u32 udiag_states; __u32 udiag_ino; __u32 udiag_show; __u32 udiag_cookie[2]; };
此结构的字段如下:
- sdiag_family
- 地址族;它应该设置为AF_UNIX。
sdiag_protocol
- pad这些字段应设置为0。
- udiag_states
- 这是一个定义套接字状态过滤器的位掩码。仅报告状态在此掩码中的那些套接字。查询单个套接字时被忽略。支持的值为:
- 1 <<
TCP_ESTABLISHED
1个
- udiag_ino
- 查询单个套接字时,这是一个索引节点号。查询套接字列表时被忽略。
- udiag_show
- This is a set of flags defining what kind of information to report.
Each requested kind of information is reported back as a netlink
attribute as described below:
- UDIAG_SHOW_NAME
- 为响应此请求而报告的属性是UNIX_DIAG_NAME。与此属性关联的有效负载是套接字绑定到的路径名(最大UNIX_PATH_MAX长度的字节序列)。
- UDIAG_SHOW_VFS
- 为响应此请求而报告的属性是UNIX_DIAG_VFS。与该属性关联的有效负载以以下结构表示:
struct unix_diag_vfs { __u32 udiag_vfs_dev; __u32 udiag_vfs_ino; };
- The fields of this structure are as follows:
- udiag_vfs_dev
- 相应的磁盘套接字inode的设备号。
- udiag_vfs_ino
- 相应的磁盘套接字inode的inode编号。
- UDIAG_SHOW_PEER
- 为响应此请求而报告的属性是UNIX_DIAG_PEER。与该属性关联的有效负载是__u32值,它是对等方的inode编号。仅针对已连接的套接字报告此属性。
- UDIAG_SHOW_ICONS
- 为响应此请求而报告的属性是UNIX_DIAG_ICONS。与该属性关联的有效负载是__u32值的数组,这些值是已通过connect(2)调用但尚未用accept(2)处理的套接字的索引节点号。仅针对侦听套接字报告此属性。
- UDIAG_SHOW_RQLEN
- 为响应此请求而报告的属性是UNIX_DIAG_RQLEN。与该属性关联的有效负载以以下结构表示:
struct unix_diag_rqlen { __u32 udiag_rqueue; __u32 udiag_wqueue; };
- The fields of this structure are as follows:
- udiag_rqueue
- 对于侦听套接字:挂起的连接数。与UNIX_DIAG_ICONS响应属性关联的数组的长度等于该值。
- 对于已建立的套接字:传入队列中的数据量。
- udiag_wqueue
- 对于侦听套接字:积压长度,该长度等于作为第二个参数传递给listen(2)的值。
- 对于已建立的套接字:可用于发送的内存量。
- UDIAG_SHOW_MEMINFO
- 为响应此请求而报告的属性是UNIX_DIAG_MEMINFO。与该属性关联的有效负载是__u32值的数组,下面将在"套接字内存信息"小节中对其进行描述。
无需任何特定请求即可报告以下属性:
- UNIX_DIAG_SHUTDOWN
- 与该属性关联的有效负载是__u8值,它表示shutdown(2)状态的位。
- udiag_cookie
- 这是不透明标识符的数组,可以与udiag_ino一起使用以指定单个套接字。在查询套接字列表以及其所有元素都设置为-1时,将忽略它。
对UNIX域套接字查询的响应表示为一个数组
struct unix_diag_msg { __u8 udiag_family; __u8 udiag_type; __u8 udiag_state; __u8 pad; __u32 udiag_ino; __u32 udiag_cookie[2]; };
其次是netlink属性。
此结构的字段如下:
- udiag_family
- 该字段与struct unix_diag_req中的含义相同。
- udiag_type
- 设置为SOCK_PACKET,SOCK_STREAM或SOCK_SEQPACKET中的一种。
- udiag_state
- 设置为TCP_LISTEN或TCP_ESTABLISHED之一。
- pad
- 该字段设置为0。
- udiag_ino
- 这是套接字索引号。
- udiag_cookie
- 这是不透明标识符的数组,可以在后续查询中使用。
IPv4 and IPv6 sockets
对于IPv4和IPv6套接字,请求以以下结构表示:
struct inet_diag_req_v2 { __u8 sdiag_family; __u8 sdiag_protocol; __u8 idiag_ext; __u8 pad; __u32 idiag_states; struct inet_diag_sockid id; };
struct inet_diag_sockid的定义如下:
struct inet_diag_sockid { __be16 idiag_sport; __be16 idiag_dport; __be32 idiag_src[4]; __be32 idiag_dst[4]; __u32 idiag_if; __u32 idiag_cookie[2]; };
struct inet_diag_req_v2的字段如下:
- sdiag_family
- 对于IPv4或IPv6套接字,应分别将其设置为AF_INET或AF_INET6。
- sdiag_protocol
- 应将其设置为IPPROTO_TCP,IPPROTO_UDP或IPPROTO_UDPLITE之一。
- idiag_ext
- This is a set of flags defining what kind of extended information to report.
Each requested kind of information is reported back as a netlink attribute
as described below:
- INET_DIAG_TOS
- 与此属性关联的有效负载是__u8值,它是套接字的TOS。
- INET_DIAG_TCLASS
- 与该属性关联的有效载荷是__u8值,它是套接字的TClass。仅IPv6套接字。对于LISTEN和CLOSE套接字,紧随其后的是INET_DIAG_SKV6ONLY属性,该属性带有关联的__u8有效负载值,表示该套接字是否仅为IPv6。
- INET_DIAG_MEMINFO
- 与该属性关联的有效负载以以下结构表示:
struct inet_diag_meminfo { __u32 idiag_rmem; __u32 idiag_wmem; __u32 idiag_fmem; __u32 idiag_tmem; };
- The fields of this structure are as follows:
- idiag_rmem
- 接收队列中的数据量。
- idiag_wmem
- TCP排队但尚未发送的数据量。
- idiag_fmem
- 计划将来使用的内存量(仅TCP)。
- idiag_tmem
- 发送队列中的数据量。
- INET_DIAG_SKMEMINFO
- 与该属性关联的有效负载是__u32值的数组,下面将在"套接字内存信息"小节中对其进行描述。
- INET_DIAG_INFO
- 与该属性关联的有效负载特定于地址族。对于TCP套接字,它是struct tcp_info类型的对象。
- INET_DIAG_CONG
- 与该属性关联的有效载荷是描述所用拥塞控制算法的字符串。仅用于TCP套接字。
- pad
- 应该设置为0。
- idiag_states
- 这是一个定义套接字状态过滤器的位掩码。仅报告状态在此掩码中的那些套接字。查询单个套接字时被忽略。
- id
- 这是一个套接字ID对象,用于转储请求,有关单个套接字的查询中,并在每个响应中报告。与UNIX域套接字不同,IPv4和IPv6套接字使用地址和端口来标识。所有值均按网络字节顺序。
struct inet_diag_sockid的字段如下:
- idiag_sport
- 源端口。
- idiag_dport
- 目的端口。
- idiag_src
- 源地址。
- idiag_dst
- 目的地址。
- idiag_if
- 套接字绑定到的接口号。
- idiag_cookie
- 这是不透明标识符的数组,可以与该结构的其他字段一起使用以指定单个套接字。在查询套接字列表以及其所有元素都设置为-1时,将忽略它。
对IPv4或IPv6套接字查询的响应表示为
struct inet_diag_msg { __u8 idiag_family; __u8 idiag_state; __u8 idiag_timer; __u8 idiag_retrans; struct inet_diag_sockid id; __u32 idiag_expires; __u32 idiag_rqueue; __u32 idiag_wqueue; __u32 idiag_uid; __u32 idiag_inode; };
其次是netlink属性。
此结构的字段如下:
- idiag_family
- 这与struct inet_diag_req_v2中的字段相同。
- idiag_state
- 这表示套接字状态,如struct inet_diag_req_v2中所示。
- idiag_timer
- 对于TCP套接字,此字段描述了套接字当前处于活动状态的计时器类型。设置为以下常量之一:
- 0
- 没有计时器处于活动状态
- 1
- 重传计时器
- 2
- 保持活动计时器
- 3
- TIME_WAIT计时器
- 4
- 零窗口探测计时器
- 对于非TCP套接字,此字段设置为0。
- idiag_retrans
- 对于idiag_timer值1、2和4,此字段包含重新发送的次数。对于其他idiag_timer值,此字段设置为0。
- idiag_expires
- 对于具有活动计时器的TCP套接字,此字段描述其终止时间(以毫秒为单位)。对于其他套接字,此字段设置为0。
- idiag_rqueue
- 对于侦听套接字:挂起的连接数。
- 对于其他套接字:传入队列中的数据量。
- idiag_wqueue
- 对于侦听套接字:积压长度。
- 对于其他套接字:可用于发送的内存量。
- idiag_uid
- 这是套接字所有者的UID。
- idiag_inode
- 这是套接字索引号。
Socket memory information
与UNIX_DIAG_MEMINFO和INET_DIAG_SKMEMINFO网络链接属性关联的有效负载是以下__u32值的数组:
- SK_MEMINFO_RMEM_ALLOC
- 接收队列中的数据量。
- SK_MEMINFO_RCVBUF
- 由SO_RCVBUF设置的接收套接字缓冲区。
- SK_MEMINFO_WMEM_ALLOC
- 发送队列中的数据量。
- SK_MEMINFO_SNDBUF
- 由SO_SNDBUF设置的发送套接字缓冲区。
- SK_MEMINFO_FWD_ALLOC
- 计划将来使用的内存量(仅TCP)。
- SK_MEMINFO_WMEM_QUEUED
- TCP排队但尚未发送的数据量。
- SK_MEMINFO_OPTMEM
- 为套接字的服务需求分配的内存量(例如套接字过滤器)。
- SK_MEMINFO_BACKLOG
- 积压中的数据包数量(尚未处理)。
版本
NETLINK_INET_DIAG在Linux 2.6.14中引入,仅支持AF_INET和AF_INET6套接字。在Linux 3.3中,它已重命名为NETLINK_SOCK_DIAG,并已扩展为支持AF_UNIX套接字。
在Linux 3.6中引入了UNIX_DIAG_MEMINFO和INET_DIAG_SKMEMINFO。
遵循规范
NETLINK_SOCK_DIAG API是特定于Linux的。
示例
以下示例程序显示当前名称空间中的索引节点编号,对等节点的索引节点编号和所有UNIX域套接字的名称。
#include <errno.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <linux/sock_diag.h> #include <linux/unix_diag.h> static int send_query(int fd) { struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct { struct nlmsghdr nlh; struct unix_diag_req udr; } req = { .nlh = { .nlmsg_len = sizeof(req), .nlmsg_type = SOCK_DIAG_BY_FAMILY, .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP }, .udr = { .sdiag_family = AF_UNIX, .udiag_states = -1, .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER } }; struct iovec iov = { .iov_base = &req, .iov_len = sizeof(req) }; struct msghdr msg = { .msg_name = (void *) &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1 }; for (;;) { if (sendmsg(fd, &msg, 0) < 0) { if (errno == EINTR) continue; perror("sendmsg"); return -1; } return 0; } } static int print_diag(const struct unix_diag_msg *diag, unsigned int len) { if (len < NLMSG_LENGTH(sizeof(*diag))) { fputs("short response\n", stderr); return -1; } if (diag->udiag_family != AF_UNIX) { fprintf(stderr, "unexpected family %u\n", diag->udiag_family); return -1; } struct rtattr *attr; unsigned int rta_len = len - NLMSG_LENGTH(sizeof(*diag)); unsigned int peer = 0; size_t path_len = 0; char path[sizeof(((struct sockaddr_un *) 0)->sun_path) + 1]; for (attr = (struct rtattr *) (diag + 1); RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) { switch (attr->rta_type) { case UNIX_DIAG_NAME: if (!path_len) { path_len = RTA_PAYLOAD(attr); if (path_len > sizeof(path) - 1) path_len = sizeof(path) - 1; memcpy(path, RTA_DATA(attr), path_len); path[path_len] = '##代码##'; } break; case UNIX_DIAG_PEER: if (RTA_PAYLOAD(attr) >= sizeof(peer)) peer = *(unsigned int *) RTA_DATA(attr); break; } } printf("inode=%u", diag->udiag_ino); if (peer) printf(", peer=%u", peer); if (path_len) printf(", name=%s%s", *path ? "" : "@", *path ? path : path + 1); putchar('\n'); return 0; } static int receive_responses(int fd) { long buf[8192 / sizeof(long)]; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; int flags = 0; for (;;) { struct msghdr msg = { .msg_name = (void *) &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1 }; ssize_t ret = recvmsg(fd, &msg, flags); if (ret < 0) { if (errno == EINTR) continue; perror("recvmsg"); return -1; } if (ret == 0) return 0; const struct nlmsghdr *h = (struct nlmsghdr *) buf; if (!NLMSG_OK(h, ret)) { fputs("!NLMSG_OK\n", stderr); return -1; } for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) { if (h->nlmsg_type == NLMSG_DONE) return 0; if (h->nlmsg_type == NLMSG_ERROR) { const struct nlmsgerr *err = NLMSG_DATA(h); if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) { fputs("NLMSG_ERROR\n", stderr); } else { errno = -err->error; perror("NLMSG_ERROR"); } return -1; } if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) { fprintf(stderr, "unexpected nlmsg_type %u\n", (unsigned) h->nlmsg_type); return -1; } if (print_diag(NLMSG_DATA(h), h->nlmsg_len)) return -1; } } } int main(void) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); if (fd < 0) { perror("socket"); return 1; } int ret = send_query(fd) || receive_responses(fd); close(fd); return ret; }
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。