Search code examples
csocketskernelnetlink

Use netlink to get inode from socket on older kernel


I wrote a piece of code to get a list of sockets and their inodes using sock_diag netlink. Here is the send code:

struct {
  nlmsghdr nlh;
  inet_diag_req_v2 id_req;
} req = {
  .nlh = {
    .nlmsg_len = sizeof(req),
    .nlmsg_type = SOCK_DIAG_BY_FAMILY,
    .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP
  },
  .id_req = {
    .sdiag_family = AF_INET,
    .sdiag_protocol = IPPROTO_TCP,
    .idiag_ext = uint8_t(0),
    .pad = uint8_t(0),
    .idiag_states = UINT32_MAX // Everything
  }
};
struct sockaddr_nl nladdr = {
  .nl_family = AF_NETLINK
};
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
};

// Send message to kernel
for (;;) {
  if (sendmsg(query_fd_, &msg, 0) < 0) {
    if (errno == EINTR)
      continue;

    perror("sendmsg");
    return false;
  }
  return;
}

Unfortunately, one of the machines we use for building is still running RHEL6.8 (2.6.32-642) which means it doesn't have <linux/sock_diag.h>, SOCK_DIAG_BY_FAMILY or inet_diag_req_v2 struct.

Looking through the kernel headers and man pages of the older kernel I can see we have the option of inet_diag_req which is fairly close to the updated struct. I tried a direct replace like so:

#define SOCK_DIAG_BY_FAMILY 20

struct {
      nlmsghdr nlh;
      inet_diag_req id_req;
    } req = {
      .nlh = {
         .nlmsg_len = sizeof(req),
         .nlmsg_type = SOCK_DIAG_BY_FAMILY,
         .nlmsg_flags = NLM_F_REQUEST
      },
      .id_req = {
        .idiag_family = AF_INET,
        .idiag_src_len = uint8_t(0),
        .idiag_dst_len = uint8_t(0),
        .idiag_ext = uint8_t(0),
        .id = {},
        .idiag_states = UINT32_MAX // Everything
      }
    };
.
.

The message I get back states NLMSG_ERROR: No such file or directory.

I am struggling to find ANY examples of what I am trying to do. Is it possible to retrieve a list of sockets based on protocol and family using the older inet_diag_req message and if so, any idea what I am doing wrong?


Solution

  • The code I posted actually does work as expected but has to be built and run on the the older machine (kernel < 3). I booted a real RH6.8 machine, built and run the code and I get the correct response but on a machine with kernel >= 3 it was returning the error. I had misunderstood docker and thought my container had its own kernel but was in fact using the hosts which contributed to some of the confusion.

    Summary:

    kernel < 3 use inet_diag_req.

    kernel >= 3 use inet_diag_req_v2.