SOCK_DIAG(7)

ИМЯ
sock_diag - возвращает информацию о сокетах

ОБЗОР
#include <sys/socket.h>
#include <linux/sock_diag.h>
#include <linux/unix_diag.h> /* для доменных сокетов UNIX */
#include <linux/inet_diag.h> /* для сокетов IPv4 и IPv6 */

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 и т. п.

  Запрос
Запрос начинается с заголовка struct nlmsghdr, описанного в netlink(7), в котором поле nlmsg_type равно SOCK_DIAG_BY_FAMILY. За ним следует заголовок, соответствующий семейству адресов, начинающийся с общей части, используемой для всех семейств адресов:

    struct sock_diag_req {
        __u8 sdiag_family;
        __u8 sdiag_protocol;
    };

Поля этой структуры имеют следующее назначение:

sdiag_family
    Семейство адресов. Должна быть указана соответствующая константа AF_*.

sdiag_protocol
    Зависит от sdiag_family. Должна быть указана соответствующая константа IPPROTO_* при AF_INET и AF_INET6, и 0 в остальных случаях.

Если в поле nlmsg_flags заголовка struct nlmsghdr установлен флаг NLM_F_DUMP, то это означает, что запрашивается список сокетов; в противном случае запрашивается информация об конкретном сокете.

  Ответ
Ответ начинается с заголовка struct nlmsghdr, за которым следует массив объектов, соответствующих семейству адресов. Массив доступен через стандартные макросы из NLMSG_* программного интерфейса netlink(3).

Каждый объект представляет собой список NLA (атрибутов netlink), которые доступны через макросы RTA_* программного интерфейса rtnetlink(3).

  Доменные сокеты UNIX
Запрос доменных сокетов 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 << TCP_LISTEN

udiag_ino
    Номер иноды при запросе конкретного сокета. Игнорируется, если запрашивается список сокетов.

udiag_show
    Набор флагов, определяющий тип возвращаемой информации. Каждый запрашиваемый тип информации возвращается в виде атрибута netlink, описанного ниже:

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

        Поля этой структуры имеют следующее назначение:

        udiag_vfs_dev
            Номер устройства, соответствующего иноде сокета на диске.

        udiag_vfs_ino
            Номер иноды, соответствующей иноде сокета на диске.

    UDIAG_SHOW_PEER
        Атрибут, возвращаемый в ответе на этот запрос — UNIX_DIAG_PEER. Полезные данные, связанные с этим атрибутом — значение __u32, представляющее номер иноды ответной стороны. Данный атрибут возвращается только для соединённых сокетов.

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

        Поля этой структуры имеют следующее назначение:

        udiag_rqueue
            Для слушающих сокетов: количество ожидающих подключений. Этому значению равна длина массива, связанного с атрибутом ответа UNIX_DIAG_ICONS.

            Для сокетов с установленным соединением: количество данных во входящей очереди.

        udiag_wqueue
            Для слушающих сокетов: длина недодела (backlog), равная значению, переданному во втором аргументе 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 и IPv6
Запрос сокетов 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
    Должно быть равно AF_INET или AF_INET6 для сокетов IPv4 или IPv6, соответственно.

sdiag_protocol
    Должно быть равно IPPROTO_TCP, IPPROTO_UDP или IPPROTO_UDPLITE.

idiag_ext
    Набор флагов, определяющий тип возвращаемой расширенной информации. Каждый запрашиваемый тип информации возвращается в виде атрибута netlink, описанного ниже:

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

        Поля этой структуры имеют следующее назначение:

        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  Идентификатор объекта сокета, использующийся при запросах дампа, в опросах отдельных сокетов и возвращается в каждом ответе. В отличие от доменных сокетов 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
    Для слушающих сокетов: длина недодела (backlog).

    Для других сокетов: количество памяти, доступной при отправке.

idiag_uid
    Идентификатор пользователя, владеющего сокетом.

idiag_inode
    Номер иноды сокета.

  Информация о памяти сокетов
Полезные данные, связанные с атрибутами netlink 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.

Программный интерфейс UNIX_DIAG_MEMINFO и INET_DIAG_SKMEMINFO появился в Linux 3.6.

СООТВЕТСТВИЕ СТАНДАРТАМ
Программный интерфейс NETLINK_SOCK_DIAG есть только в 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("короткий ответ\n", stderr);
            return -1;
        }
        if (diag->udiag_family != AF_UNIX) {
            fprintf(stderr, "неожиданное семейство %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] = '\0';
                }
                break;
            case UNIX_DIAG_PEER:
                if (RTA_PAYLOAD(attr) >= sizeof(peer))
                    peer = *(unsigned int *) RTA_DATA(attr);
                break;
            }
        }
        printf("инода=%u", diag->udiag_ino);
        if (peer)
            printf(", другая сторона=%u", peer);
        if (path_len)
            printf(", имя=%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, "неожиданный 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)