Linuxでcからnetstatっぽいことをしたかった

詳しい話はまた後日ということで、とりあえず今回はソースと実行結果だけ貼り付けます。
netlinkの資料は比較的少ないので、カーネルソースを追いながら試行錯誤してみました。
TCPDIAG_GETSOCKでNLM_F_DUMPを指定するとソケットを列挙できるようです。
sportやdportを指定すると条件にマッチしたソケットだけ列挙してくれるので結構便利そう。

wbuf.req.id.idiag_sport=htons(3306);

とかするとMySQLの接続数を調べることができたりなんかして。

ntest.c

#include<stdio.h>
#include<sys/socket.h>
#include<linux/netlink.h>
#include<linux/inet_diag.h>
#include<net/tcp_states.h>

int scount(int n)
{
  int r=0;
  int len;
  static int  seq=1;
  static char rbuf[65535];
  struct {
    struct nlmsghdr      nlh;
    struct inet_diag_req req;
  } wbuf;
  struct nlmsghdr      *nlh;
  struct inet_diag_msg *msg;
  wbuf.nlh.nlmsg_len          = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(wbuf.req)));
  wbuf.nlh.nlmsg_type         = TCPDIAG_GETSOCK;
  wbuf.nlh.nlmsg_flags        = NLM_F_REQUEST | NLM_F_DUMP;
  wbuf.nlh.nlmsg_seq          = seq++;
  wbuf.req.idiag_family       = AF_INET;
  wbuf.req.idiag_src_len      = 0;
  wbuf.req.idiag_dst_len      = 0;
  wbuf.req.idiag_ext          = 0;
  wbuf.req.idiag_states       = TCPF_ESTABLISHED;
  wbuf.req.idiag_dbs          = 0;
  wbuf.req.id.idiag_sport     = 0;
  wbuf.req.id.idiag_dport     = 0;
  wbuf.req.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
  wbuf.req.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
  len = write(n, &wbuf, wbuf.nlh.nlmsg_len);
  if(len < 0){
    printf("netlink write error!!\n");
    return(-1);
  }
  while(1){
    len=read(n, rbuf, sizeof(rbuf));
    for(nlh=(struct nlmsghdr *)rbuf;NLMSG_OK(nlh, len);nlh=NLMSG_NEXT(nlh, len)){
      if(nlh->nlmsg_seq != wbuf.nlh.nlmsg_seq)
        continue;
      msg=(struct inet_diag_msg *)((char *)nlh + sizeof(struct nlmsghdr));
      if(nlh->nlmsg_type == NLMSG_ERROR){
        fprintf(stderr,"netlink msg error\n");
        return(-1);
      }
      if(nlh->nlmsg_type == NLMSG_DONE){
        return(r);
      }
      /*
      printf("%02d: state=%02d sport=%05d dport=%05d\n", 
        r, msg->idiag_state, 
        ntohs(msg->id.idiag_sport), 
        ntohs(msg->id.idiag_dport));
      */
      r++;
    }
  }
  return(0);
}

int main(int argc, char *argv[])
{
  int c;
  int n;

  n = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
  if(n == -1){
    printf("Can't Open NetLink!!\n");
    return(0);
  }

  c = scount(n);
  if(c == -1)
    return(1);
  printf("%d\n",c);
  return(0);
}

実行結果

$ ./ntest
7
$ netstat --inet -n | grep ESTABLISHED | wc -l
7