{
    "mode": "info",
    "parameter": "SOCK_DIAG",
    "section": "",
    "url": "https://www.chedong.com/phpMan.php/info/SOCK_DIAG/json",
    "generated": "2026-07-05T23:22:47Z",
    "synopsis": "#include <sys/socket.h>\n#include <linux/sockdiag.h>\n#include <linux/unixdiag.h> /* for UNIX domain sockets */\n#include <linux/inetdiag.h> /* for IPv4 and IPv6 sockets */\ndiagsocket = socket(AFNETLINK, sockettype, NETLINKSOCKDIAG);",
    "sections": {
        "NAME": {
            "content": "sockdiag - obtaining information about sockets\n",
            "subsections": []
        },
        "SYNOPSIS": {
            "content": "#include <sys/socket.h>\n#include <linux/sockdiag.h>\n#include <linux/unixdiag.h> /* for UNIX domain sockets */\n#include <linux/inetdiag.h> /* for IPv4 and IPv6 sockets */\n\ndiagsocket = socket(AFNETLINK, sockettype, NETLINKSOCKDIAG);\n",
            "subsections": []
        },
        "DESCRIPTION": {
            "content": "The  sockdiag netlink subsystem provides a mechanism for obtaining in-\nformation about sockets of various address families  from  the  kernel.\nThis subsystem can be used to obtain information about individual sock-\nets or request a list of sockets.\n\nIn the request, the caller can specify additional information it  would\nlike to obtain about the socket, for example, memory information or in-\nformation specific to the address family.\n\nWhen requesting a list of sockets, the caller can specify filters  that\nwould be applied by the kernel to select a subset of sockets to report.\nFor now, there is only the ability to filter  sockets  by  state  (con-\nnected, listening, and so on.)\n\nNote  that  sockdiag reports only those sockets that have a name; that\nis, either sockets bound explicitly with bind(2) or sockets  that  were\nautomatically  bound  to an address (e.g., by connect(2)).  This is the\nsame  set  of   sockets   that   is   available   via   /proc/net/unix,\n/proc/net/tcp, /proc/net/udp, and so on.\n\nRequest\nThe   request  starts  with  a  struct  nlmsghdr  header  described  in\nnetlink(7) with nlmsgtype field set  to  SOCKDIAGBYFAMILY.   It  is\nfollowed  by a header specific to the address family that starts with a\ncommon part shared by all address families:\n\nstruct sockdiagreq {\nu8 sdiagfamily;\nu8 sdiagprotocol;\n};\n\nThe fields of this structure are as follows:\n\nsdiagfamily\nAn address family.  It should be set  to  the  appropriate  AF*\nconstant.\n\nsdiagprotocol\nDepends  on  sdiagfamily.   It should be set to the appropriate\nIPPROTO* constant for AFINET and AFINET6, and to 0 otherwise.\n\nIf the  nlmsgflags  field  of  the  struct  nlmsghdr  header  has  the\nNLMFDUMP  flag  set,  it  means  that  a list of sockets is being re-\nquested; otherwise it is a query about an individual socket.\n\nResponse\nThe response starts with a struct nlmsghdr header and is followed by an\narray  of  objects  specific to the address family.  The array is to be\naccessed with the standard NLMSG* macros from the netlink(3) API.\n\nEach object is the NLA (netlink attributes) list that is to be accessed\nwith the RTA* macros from rtnetlink(3) API.\n\nUNIX domain sockets\nFor  UNIX  domain  sockets  the request is represented in the following\nstructure:\n\nstruct unixdiagreq {\nu8    sdiagfamily;\nu8    sdiagprotocol;\nu16   pad;\nu32   udiagstates;\nu32   udiagino;\nu32   udiagshow;\nu32   udiagcookie[2];\n};\n\nThe fields of this structure are as follows:\n\nsdiagfamily\nThe address family; it should be set to AFUNIX.\n\nsdiagprotocol\npad    These fields should be set to 0.\n\nudiagstates\nThis is a bit mask that defines  a  filter  of  sockets  states.\nOnly  those  sockets  whose  states are in this mask will be re-\nported.  Ignored when querying for an individual  socket.   Sup-\nported values are:\n\n1 << TCPESTABLISHED\n\n1 << TCPLISTEN\n\nudiagino\nThis  is an inode number when querying for an individual socket.\nIgnored when querying for a list of sockets.\n\nudiagshow\nThis is a set of flags defining what kind of information to  re-\nport.   Each requested kind of information is reported back as a\nnetlink attribute as described below:\n\nUDIAGSHOWNAME\nThe attribute reported  in  answer  to  this  request  is\nUNIXDIAGNAME.   The payload associated with this attri-\nbute is the pathname to which the socket was bound (a se-\nquence of bytes up to UNIXPATHMAX length).\n\nUDIAGSHOWVFS\nThe  attribute  reported  in  answer  to  this request is\nUNIXDIAGVFS.  The payload associated with  this  attri-\nbute is represented in the following structure:\n\nstruct unixdiagvfs {\nu32 udiagvfsdev;\nu32 udiagvfsino;\n};\n\nThe fields of this structure are as follows:\n\nudiagvfsdev\nThe  device  number  of  the corresponding on-disk\nsocket inode.\n\nudiagvfsino\nThe inode  number  of  the  corresponding  on-disk\nsocket inode.\n\nUDIAGSHOWPEER\nThe  attribute  reported  in  answer  to  this request is\nUNIXDIAGPEER.  The payload associated with this  attri-\nbute  is  a u32 value which is the peer's inode number.\nThis attribute is reported for connected sockets only.\n\nUDIAGSHOWICONS\nThe attribute reported  in  answer  to  this  request  is\nUNIXDIAGICONS.  The payload associated with this attri-\nbute is an array of u32 values which are inode  numbers\nof  sockets  that  has  passed  the  connect(2) call, but\nhasn't been processed with accept(2) yet.  This attribute\nis reported for listening sockets only.\n\nUDIAGSHOWRQLEN\nThe  attribute  reported  in  answer  to  this request is\nUNIXDIAGRQLEN.  The payload associated with this attri-\nbute is represented in the following structure:\n\nstruct unixdiagrqlen {\nu32 udiagrqueue;\nu32 udiagwqueue;\n};\n\nThe fields of this structure are as follows:\n\nudiagrqueue\nFor  listening sockets: the number of pending con-\nnections.  The length of the array associated with\nthe UNIXDIAGICONS response attribute is equal to\nthis value.\n\nFor established sockets: the amount of data in in-\ncoming queue.\n\nudiagwqueue\nFor  listening  sockets:  the backlog length which\nequals to the value passed as the second  argument\nto listen(2).\n\nFor  established  sockets:  the  amount  of memory\navailable for sending.\n\nUDIAGSHOWMEMINFO\nThe attribute reported  in  answer  to  this  request  is\nUNIXDIAGMEMINFO.   The payload associated with this at-\ntribute is an array of u32 values  described  below  in\nthe subsection \"Socket memory information\".\n\nThe  following attributes are reported back without any specific\nrequest:\n\nUNIXDIAGSHUTDOWN\nThe payload associated with this attribute is u8  value\nwhich represents bits of shutdown(2) state.\n\nudiagcookie\nThis  is an array of opaque identifiers that could be used along\nwith udiagino to specify an individual socket.  It  is  ignored\nwhen querying for a list of sockets, as well as when all its el-\nements are set to -1.\n\nThe response to a query for UNIX domain sockets is  represented  as  an\narray of\n\nstruct unixdiagmsg {\nu8    udiagfamily;\nu8    udiagtype;\nu8    udiagstate;\nu8    pad;\nu32   udiagino;\nu32   udiagcookie[2];\n};\n\nfollowed by netlink attributes.\n\nThe fields of this structure are as follows:\n\nudiagfamily\nThis field has the same meaning as in struct unixdiagreq.\n\nudiagtype\nThis  is  set  to  one of SOCKPACKET, SOCKSTREAM, or SOCKSEQ-\nPACKET.\n\nudiagstate\nThis is set to one of TCPLISTEN or TCPESTABLISHED.\n\npad    This field is set to 0.\n\nudiagino\nThis is the socket inode number.\n\nudiagcookie\nThis is an array of opaque identifiers that  could  be  used  in\nsubsequent queries.\n\nIPv4 and IPv6 sockets\nFor  IPv4 and IPv6 sockets, the request is represented in the following\nstructure:\n\nstruct inetdiagreqv2 {\nu8    sdiagfamily;\nu8    sdiagprotocol;\nu8    idiagext;\nu8    pad;\nu32   idiagstates;\nstruct inetdiagsockid id;\n};\n\nwhere struct inetdiagsockid is defined as follows:\n\nstruct inetdiagsockid {\nbe16  idiagsport;\nbe16  idiagdport;\nbe32  idiagsrc[4];\nbe32  idiagdst[4];\nu32   idiagif;\nu32   idiagcookie[2];\n};\n\nThe fields of struct inetdiagreqv2 are as follows:\n\nsdiagfamily\nThis should be set to either AFINET or  AFINET6  for  IPv4  or\nIPv6 sockets respectively.\n\nsdiagprotocol\nThis  should  be  set to one of IPPROTOTCP, IPPROTOUDP, or IP-\nPROTOUDPLITE.\n\nidiagext\nThis is a set of flags defining what kind of  extended  informa-\ntion  to report.  Each requested kind of information is reported\nback as a netlink attribute as described below:\n\nINETDIAGTOS\nThe payload associated with  this  attribute  is  a  u8\nvalue which is the TOS of the socket.\n\nINETDIAGTCLASS\nThe  payload  associated  with  this  attribute is a u8\nvalue which is the TClass of the  socket.   IPv6  sockets\nonly.   For LISTEN and CLOSE sockets, this is followed by\nINETDIAGSKV6ONLY attribute with associated u8 payload\nvalue meaning whether the socket is IPv6-only or not.\n\nINETDIAGMEMINFO\nThe payload associated with this attribute is represented\nin the following structure:\n\nstruct inetdiagmeminfo {\nu32 idiagrmem;\nu32 idiagwmem;\nu32 idiagfmem;\nu32 idiagtmem;\n};\n\nThe fields of this structure are as follows:\n\nidiagrmem  The amount of data in the receive queue.\n\nidiagwmem  The amount of data that is queued by TCP  but\nnot yet sent.\n\nidiagfmem  The amount of memory scheduled for future use\n(TCP only).\n\nidiagtmem  The amount of data in send queue.\n\nINETDIAGSKMEMINFO\nThe payload associated with this attribute is an array of\nu32  values  described  below in the subsection \"Socket\nmemory information\".\n\nINETDIAGINFO\nThe payload associated with this attribute is specific to\nthe  address family.  For TCP sockets, it is an object of\ntype struct tcpinfo.\n\nINETDIAGCONG\nThe payload associated with this attribute  is  a  string\nthat  describes  the  congestion  control algorithm used.\nFor TCP sockets only.\n\npad    This should be set to 0.\n\nidiagstates\nThis is a bit mask that defines a filter of socket states.  Only\nthose  sockets  whose  states are in this mask will be reported.\nIgnored when querying for an individual socket.\n\nid     This is a socket ID object that is used  in  dump  requests,  in\nqueries  about  individual sockets, and is reported back in each\nresponse.  Unlike UNIX domain sockets, IPv4 and IPv6 sockets are\nidentified using addresses and ports.  All values are in network\nbyte order.\n\nThe fields of struct inetdiagsockid are as follows:\n\nidiagsport\nThe source port.\n\nidiagdport\nThe destination port.\n\nidiagsrc\nThe source address.\n\nidiagdst\nThe destination address.\n\nidiagif\nThe interface number the socket is bound to.\n\nidiagcookie\nThis is an array of opaque identifiers that could be used  along\nwith  other  fields  of  this structure to specify an individual\nsocket.  It is ignored when querying for a list of  sockets,  as\nwell as when all its elements are set to -1.\n\nThe  response  to a query for IPv4 or IPv6 sockets is represented as an\narray of\n\nstruct inetdiagmsg {\nu8    idiagfamily;\nu8    idiagstate;\nu8    idiagtimer;\nu8    idiagretrans;\n\nstruct inetdiagsockid id;\n\nu32   idiagexpires;\nu32   idiagrqueue;\nu32   idiagwqueue;\nu32   idiaguid;\nu32   idiaginode;\n};\n\nfollowed by netlink attributes.\n\nThe fields of this structure are as follows:\n\nidiagfamily\nThis is the same field as in struct inetdiagreqv2.\n\nidiagstate\nThis denotes socket state as in struct inetdiagreqv2.\n\nidiagtimer\nFor TCP sockets, this field describes the type of timer that  is\ncurrently  active  for the socket.  It is set to one of the fol-\nlowing constants:\n\n0      no timer is active\n1      a retransmit timer\n2      a keep-alive timer\n3      a TIMEWAIT timer\n4      a zero window probe timer\n\nFor non-TCP sockets, this field is set to 0.\n\nidiagretrans\nFor idiagtimer values 1, 2, and 4, this field contains the num-\nber of retransmits.  For other idiagtimer values, this field is\nset to 0.\n\nidiagexpires\nFor TCP sockets that have an active timer, this field  describes\nits  expiration  time  in milliseconds.  For other sockets, this\nfield is set to 0.\n\nidiagrqueue\nFor listening sockets: the number of pending connections.\n\nFor other sockets: the amount of data in the incoming queue.\n\nidiagwqueue\nFor listening sockets: the backlog length.\n\nFor other sockets: the amount of memory available for sending.\n\nidiaguid\nThis is the socket owner UID.\n\nidiaginode\nThis is the socket inode number.\n\nSocket memory information\nThe payload associated with UNIXDIAGMEMINFO  and  INETDIAGSKMEMINFO\nnetlink attributes is an array of the following u32 values:\n\nSKMEMINFORMEMALLOC\nThe amount of data in receive queue.\n\nSKMEMINFORCVBUF\nThe receive socket buffer as set by SORCVBUF.\n\nSKMEMINFOWMEMALLOC\nThe amount of data in send queue.\n\nSKMEMINFOSNDBUF\nThe send socket buffer as set by SOSNDBUF.\n\nSKMEMINFOFWDALLOC\nThe amount of memory scheduled for future use (TCP only).\n\nSKMEMINFOWMEMQUEUED\nThe amount of data queued by TCP, but not yet sent.\n\nSKMEMINFOOPTMEM\nThe  amount  of  memory allocated for the socket's service needs\n(e.g., socket filter).\n\nSKMEMINFOBACKLOG\nThe amount of packets in the backlog (not yet processed).\n",
            "subsections": []
        },
        "VERSIONS": {
            "content": "NETLINKINETDIAG was introduced in Linux 2.6.14 and supported  AFINET\nand   AFINET6   sockets  only.   In  Linux  3.3,  it  was  renamed  to\nNETLINKSOCKDIAG and extended to support AFUNIX sockets.\n\nUNIXDIAGMEMINFO and INETDIAGSKMEMINFO were introduced in Linux 3.6.\n",
            "subsections": []
        },
        "CONFORMING TO": {
            "content": "The NETLINKSOCKDIAG API is Linux-specific.\n",
            "subsections": []
        },
        "EXAMPLES": {
            "content": "The following example program prints inode number, peer's inode number,\nand name of all UNIX domain sockets in the current namespace.\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <linux/sockdiag.h>\n#include <linux/unixdiag.h>\n\nstatic int\nsendquery(int fd)\n{\nstruct sockaddrnl nladdr = {\n.nlfamily = AFNETLINK\n};\nstruct\n{\nstruct nlmsghdr nlh;\nstruct unixdiagreq udr;\n} req = {\n.nlh = {\n.nlmsglen = sizeof(req),\n.nlmsgtype = SOCKDIAGBYFAMILY,\n.nlmsgflags = NLMFREQUEST | NLMFDUMP\n},\n.udr = {\n.sdiagfamily = AFUNIX,\n.udiagstates = -1,\n.udiagshow = UDIAGSHOWNAME | UDIAGSHOWPEER\n}\n};\nstruct iovec iov = {\n.iovbase = &req,\n.iovlen = sizeof(req)\n};\nstruct msghdr msg = {\n.msgname = &nladdr,\n.msgnamelen = sizeof(nladdr),\n.msgiov = &iov,\n.msgiovlen = 1\n};\n\nfor (;;) {\nif (sendmsg(fd, &msg, 0) < 0) {\nif (errno == EINTR)\ncontinue;\n\nperror(\"sendmsg\");\nreturn -1;\n}\n\nreturn 0;\n}\n}\n\nstatic int\nprintdiag(const struct unixdiagmsg *diag, unsigned int len)\n{\nif (len < NLMSGLENGTH(sizeof(*diag))) {\nfputs(\"short response\\n\", stderr);\nreturn -1;\n}\nif (diag->udiagfamily != AFUNIX) {\nfprintf(stderr, \"unexpected family %u\\n\", diag->udiagfamily);\nreturn -1;\n}\n\nunsigned int rtalen = len - NLMSGLENGTH(sizeof(*diag));\nunsigned int peer = 0;\nsizet pathlen = 0;\nchar path[sizeof(((struct sockaddrun *) 0)->sunpath) + 1];\n\nfor (struct rtattr *attr = (struct rtattr *) (diag + 1);\nRTAOK(attr, rtalen); attr = RTANEXT(attr, rtalen)) {\nswitch (attr->rtatype) {\ncase UNIXDIAGNAME:\nif (!pathlen) {\npathlen = RTAPAYLOAD(attr);\nif (pathlen > sizeof(path) - 1)\npathlen = sizeof(path) - 1;\nmemcpy(path, RTADATA(attr), pathlen);\npath[pathlen] = '\\0';\n}\nbreak;\n\ncase UNIXDIAGPEER:\nif (RTAPAYLOAD(attr) >= sizeof(peer))\npeer = *(unsigned int *) RTADATA(attr);\nbreak;\n}\n}\n\nprintf(\"inode=%u\", diag->udiagino);\n\nif (peer)\nprintf(\", peer=%u\", peer);\n\nif (pathlen)\nprintf(\", name=%s%s\", *path ? \"\" : \"@\",\n*path ? path : path + 1);\n\nputchar('\\n');\nreturn 0;\n}\n\nstatic int\nreceiveresponses(int fd)\n{\nlong buf[8192 / sizeof(long)];\nstruct sockaddrnl nladdr = {\n.nlfamily = AFNETLINK\n};\nstruct iovec iov = {\n.iovbase = buf,\n.iovlen = sizeof(buf)\n};\nint flags = 0;\n\nfor (;;) {\nstruct msghdr msg = {\n.msgname = &nladdr,\n.msgnamelen = sizeof(nladdr),\n.msgiov = &iov,\n.msgiovlen = 1\n};\n\nssizet ret = recvmsg(fd, &msg, flags);\n\nif (ret < 0) {\nif (errno == EINTR)\ncontinue;\n\nperror(\"recvmsg\");\nreturn -1;\n}\nif (ret == 0)\nreturn 0;\n\nconst struct nlmsghdr *h = (struct nlmsghdr *) buf;\n\nif (!NLMSGOK(h, ret)) {\nfputs(\"!NLMSGOK\\n\", stderr);\nreturn -1;\n}\n\nfor (; NLMSGOK(h, ret); h = NLMSGNEXT(h, ret)) {\nif (h->nlmsgtype == NLMSGDONE)\nreturn 0;\n\nif (h->nlmsgtype == NLMSGERROR) {\nconst struct nlmsgerr *err = NLMSGDATA(h);\n\nif (h->nlmsglen < NLMSGLENGTH(sizeof(*err))) {\nfputs(\"NLMSGERROR\\n\", stderr);\n} else {\nerrno = -err->error;\nperror(\"NLMSGERROR\");\n}\n\nreturn -1;\n}\n\nif (h->nlmsgtype != SOCKDIAGBYFAMILY) {\nfprintf(stderr, \"unexpected nlmsgtype %u\\n\",\n(unsigned) h->nlmsgtype);\nreturn -1;\n}\n\nif (printdiag(NLMSGDATA(h), h->nlmsglen))\nreturn -1;\n}\n}\n}\n\nint\nmain(void)\n{\nint fd = socket(AFNETLINK, SOCKRAW, NETLINKSOCKDIAG);\n\nif (fd < 0) {\nperror(\"socket\");\nreturn 1;\n}\n\nint ret = sendquery(fd) || receiveresponses(fd);\n\nclose(fd);\nreturn ret;\n}\n",
            "subsections": []
        },
        "SEE ALSO": {
            "content": "netlink(3), rtnetlink(3), netlink(7), tcp(7)\n",
            "subsections": []
        },
        "COLOPHON": {
            "content": "This  page  is  part of release 5.10 of the Linux man-pages project.  A\ndescription of the project, information about reporting bugs,  and  the\nlatest     version     of     this    page,    can    be    found    at\nhttps://www.kernel.org/doc/man-pages/.\n\nLinux                             2020-11-01                      SOCKDIAG(7)",
            "subsections": []
        }
    },
    "summary": "sockdiag - obtaining information about sockets",
    "flags": [],
    "examples": [
        "The following example program prints inode number, peer's inode number,",
        "and name of all UNIX domain sockets in the current namespace.",
        "#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/sockdiag.h>",
        "#include <linux/unixdiag.h>",
        "static int",
        "sendquery(int fd)",
        "struct sockaddrnl nladdr = {",
        ".nlfamily = AFNETLINK",
        "};",
        "struct",
        "struct nlmsghdr nlh;",
        "struct unixdiagreq udr;",
        "} req = {",
        ".nlh = {",
        ".nlmsglen = sizeof(req),",
        ".nlmsgtype = SOCKDIAGBYFAMILY,",
        ".nlmsgflags = NLMFREQUEST | NLMFDUMP",
        "},",
        ".udr = {",
        ".sdiagfamily = AFUNIX,",
        ".udiagstates = -1,",
        ".udiagshow = UDIAGSHOWNAME | UDIAGSHOWPEER",
        "};",
        "struct iovec iov = {",
        ".iovbase = &req,",
        ".iovlen = sizeof(req)",
        "};",
        "struct msghdr msg = {",
        ".msgname = &nladdr,",
        ".msgnamelen = sizeof(nladdr),",
        ".msgiov = &iov,",
        ".msgiovlen = 1",
        "};",
        "for (;;) {",
        "if (sendmsg(fd, &msg, 0) < 0) {",
        "if (errno == EINTR)",
        "continue;",
        "perror(\"sendmsg\");",
        "return -1;",
        "return 0;",
        "static int",
        "printdiag(const struct unixdiagmsg *diag, unsigned int len)",
        "if (len < NLMSGLENGTH(sizeof(*diag))) {",
        "fputs(\"short response\\n\", stderr);",
        "return -1;",
        "if (diag->udiagfamily != AFUNIX) {",
        "fprintf(stderr, \"unexpected family %u\\n\", diag->udiagfamily);",
        "return -1;",
        "unsigned int rtalen = len - NLMSGLENGTH(sizeof(*diag));",
        "unsigned int peer = 0;",
        "sizet pathlen = 0;",
        "char path[sizeof(((struct sockaddrun *) 0)->sunpath) + 1];",
        "for (struct rtattr *attr = (struct rtattr *) (diag + 1);",
        "RTAOK(attr, rtalen); attr = RTANEXT(attr, rtalen)) {",
        "switch (attr->rtatype) {",
        "case UNIXDIAGNAME:",
        "if (!pathlen) {",
        "pathlen = RTAPAYLOAD(attr);",
        "if (pathlen > sizeof(path) - 1)",
        "pathlen = sizeof(path) - 1;",
        "memcpy(path, RTADATA(attr), pathlen);",
        "path[pathlen] = '\\0';",
        "break;",
        "case UNIXDIAGPEER:",
        "if (RTAPAYLOAD(attr) >= sizeof(peer))",
        "peer = *(unsigned int *) RTADATA(attr);",
        "break;",
        "printf(\"inode=%u\", diag->udiagino);",
        "if (peer)",
        "printf(\", peer=%u\", peer);",
        "if (pathlen)",
        "printf(\", name=%s%s\", *path ? \"\" : \"@\",",
        "*path ? path : path + 1);",
        "putchar('\\n');",
        "return 0;",
        "static int",
        "receiveresponses(int fd)",
        "long buf[8192 / sizeof(long)];",
        "struct sockaddrnl nladdr = {",
        ".nlfamily = AFNETLINK",
        "};",
        "struct iovec iov = {",
        ".iovbase = buf,",
        ".iovlen = sizeof(buf)",
        "};",
        "int flags = 0;",
        "for (;;) {",
        "struct msghdr msg = {",
        ".msgname = &nladdr,",
        ".msgnamelen = sizeof(nladdr),",
        ".msgiov = &iov,",
        ".msgiovlen = 1",
        "};",
        "ssizet 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 (!NLMSGOK(h, ret)) {",
        "fputs(\"!NLMSGOK\\n\", stderr);",
        "return -1;",
        "for (; NLMSGOK(h, ret); h = NLMSGNEXT(h, ret)) {",
        "if (h->nlmsgtype == NLMSGDONE)",
        "return 0;",
        "if (h->nlmsgtype == NLMSGERROR) {",
        "const struct nlmsgerr *err = NLMSGDATA(h);",
        "if (h->nlmsglen < NLMSGLENGTH(sizeof(*err))) {",
        "fputs(\"NLMSGERROR\\n\", stderr);",
        "} else {",
        "errno = -err->error;",
        "perror(\"NLMSGERROR\");",
        "return -1;",
        "if (h->nlmsgtype != SOCKDIAGBYFAMILY) {",
        "fprintf(stderr, \"unexpected nlmsgtype %u\\n\",",
        "(unsigned) h->nlmsgtype);",
        "return -1;",
        "if (printdiag(NLMSGDATA(h), h->nlmsglen))",
        "return -1;",
        "int",
        "main(void)",
        "int fd = socket(AFNETLINK, SOCKRAW, NETLINKSOCKDIAG);",
        "if (fd < 0) {",
        "perror(\"socket\");",
        "return 1;",
        "int ret = sendquery(fd) || receiveresponses(fd);",
        "close(fd);",
        "return ret;"
    ],
    "see_also": [
        {
            "name": "netlink",
            "section": "3",
            "url": "https://www.chedong.com/phpMan.php/man/netlink/3/json"
        },
        {
            "name": "rtnetlink",
            "section": "3",
            "url": "https://www.chedong.com/phpMan.php/man/rtnetlink/3/json"
        },
        {
            "name": "netlink",
            "section": "7",
            "url": "https://www.chedong.com/phpMan.php/man/netlink/7/json"
        },
        {
            "name": "tcp",
            "section": "7",
            "url": "https://www.chedong.com/phpMan.php/man/tcp/7/json"
        }
    ]
}