SOCK_DIAG - Linux手册页

时间:2019-08-20 18:02:02  来源:igfitidea点击:

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

另外参见

netlink(3),rtnetlink(3),netlink(7),tcp(7)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/