首页 > 代码库 > linux netlink套接字实现类似ss命令 ,统计套接字以及TCP信息

linux netlink套接字实现类似ss命令 ,统计套接字以及TCP信息

参考了 ss的源代码

以及 netlink相关资料:http://blog.csdn.net/scdxmoe/article/details/27711205


实现结果为:

gcc netlink_dig_530_7.c -o netlink_dig_530_7
./netlink_dig_530_7

state      family     l.addr     l.port       r.addr     r.rport   
LISTEN     AF_INET   localhost      53         0.0.0.0        0         
LISTEN     AF_INET   (null)         21         0.0.0.0        0         
LISTEN     AF_INET   (null)         22         0.0.0.0        0         
LISTEN     AF_INET   (null)         22         0.0.0.0        0         
LISTEN     AF_INET   localhost      631        0.0.0.0        0         
LISTEN     AF_INET   (null)         12865      0.0.0.0        0         
ESTAB      AF_INET   ubuntu.local   59208      91.189.89.134  80        
ESTAB      AF_INET   ubuntu.local   22         192.168.0.248  9689      
ESTAB      AF_INET   ubuntu.local   22         192.168.0.248  9295      
ESTAB      AF_INET   ubuntu.local   35531      91.189.94.25   80        
ESTAB      AF_INET   ubuntu.local   22         192.168.0.248  9691  

本文的实验 并没有实现怎么样获取TCP的窗口值cwnd和RTT值,在ss源码中我看到了他利用了/proc

文件来实现获取窗口和RTT值,怎么样用netlink套接字实现呢?还请教各位指点

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/inet_diag.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include<arpa/inet.h>
struct sk_req {
    struct nlmsghdr nlh;
    struct inet_diag_req r;
};


typedef struct{
 __u8 family;
 __u8 bytelen;
 __s16 bitlen;
 __u32 flags;
 __u32 data[8];
} inet_prefix;

/*struct namerec
{
        struct namerec *next;
        const char *name;
        inet_prefix addr;
};*/

struct tcpstat
{
        inet_prefix     local;
        inet_prefix     remote;
        int             lport;
        int             rport;
        int             state;
        int             rq, wq;
        int             timer;
		 int             rq, wq;
        int             timer;
        int             timeout;
        int             retrs;
        unsigned        ino;
        int             probes;
        unsigned        uid;
        int             refcnt;
        unsigned long long sk;
        int             rto, ato, qack, cwnd, ssthresh;
};
enum {
        SS_UNKNOWN,
        SS_ESTABLISHED,
        SS_SYN_SENT,
        SS_SYN_RECV,
        SS_FIN_WAIT1,
        SS_FIN_WAIT2,
        SS_TIME_WAIT,
        SS_CLOSE,
        SS_CLOSE_WAIT,
        SS_LAST_ACK,
        SS_LISTEN,
        SS_CLOSING,
        SS_MAX
};

static const char *sstate_name[] = {
        "UNKNOWN",
        [SS_ESTABLISHED] = "ESTAB",
        [SS_SYN_SENT] = "SYN-SENT",
        [SS_SYN_RECV] = "SYN-RECV",
        [SS_FIN_WAIT1] = "FIN-WAIT-1",
        [SS_FIN_WAIT2] = "FIN-WAIT-2",
        [SS_TIME_WAIT] = "TIME-WAIT",
        [SS_CLOSE] = "UNCONN",
        [SS_CLOSE_WAIT] = "CLOSE-WAIT",
        [SS_LAST_ACK] = "LAST-ACK",
        [SS_LISTEN] =   "LISTEN",
        [SS_CLOSING] = "CLOSING",
};

/*  Base info structure. It contains socket identity (addrs/ports/cookie)
 * and, alas, the information shown by netstat.
/*  Base info structure. It contains socket identity (addrs/ports/cookie)
 * and, alas, the information shown by netstat. 

struct nlmsghdr {
        __u32           nlmsg_len;      // Length of message including header 
        __u16           nlmsg_type;     // Message content 
        __u16           nlmsg_flags;    //Additional flags 
        __u32           nlmsg_seq;      // Sequence number 
        __u32           nlmsg_pid;      // Sending process port ID 
};*/

//#ifdef RESOLVE_HOSTNAMES
struct namerec
{
        struct namerec *next;
        const char *name;
        inet_prefix addr;
};

#define NHASH 257
static struct namerec *nht[NHASH];

static const char *resolve_address(const void *addr, int len, int af)
{
        struct namerec *n;
        struct hostent *h_ent;
        unsigned hash;
        static int notfirst;


        if (af == AF_INET6 && ((__u32*)addr)[0] == 0 &&
            ((__u32*)addr)[1] == 0 && ((__u32*)addr)[2] == htonl(0xffff)) {
                af = AF_INET;
                addr += 12;
                len = 4;
        }

        hash = *(__u32 *)(addr + len - 4) % NHASH;

        for (n = nht[hash]; n; n = n->next) {
                if (n->addr.family == af &&
                    n->addr.bytelen == len &&
                    memcmp(n->addr.data, addr, len) == 0)
                        return n->name;
  memcmp(n->addr.data, addr, len) == 0)
                        return n->name;
        }
        if ((n = malloc(sizeof(*n))) == NULL)
                return NULL;
        n->addr.family = af;
        n->addr.bytelen = len;
        n->name = NULL;
        memcpy(n->addr.data, addr, len);
        n->next = nht[hash];
        nht[hash] = n;
        if (++notfirst == 1)
                sethostent(1);
        fflush(stdout);

        if ((h_ent = gethostbyaddr(addr, len, af)) != NULL)
                n->name = strdup(h_ent->h_name);

        /* Even if we fail, "negative" entry is remembered. */
        return n->name;
}
//#endif


const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen)
{
        switch (af) {
        case AF_INET:
        case AF_INET6:
                return inet_ntop(af, addr, buf, buflen);
        /*case AF_IPX:
                return ipx_ntop(af, addr, buf, buflen);
        case AF_DECnet:
        {
                struct dn_naddr dna = { 2, { 0, 0, }};
                memcpy(dna.a_addr, addr, 2);
                return dnet_ntop(af, &dna, buf, buflen);
        }*/
        default:
                return "???";
        }
}



void print_info(struct inet_diag_msg *pkg,struct nlmsghdr *h)
{
                struct tcpstat s;
                char buf[1024];
                const char *ap = buf;//存放ip地址 
                char buf2[1024];
                const char *ap2 = buf2;//存放ip地址 

                struct inet_diag_msg *r = NLMSG_DATA(h);
                s.state = r->idiag_state;
                s.local.family = s.remote.family = r->idiag_family;
                s.lport = ntohs(r->id.idiag_sport);
                s.rport = ntohs(r->id.idiag_dport);
                s.local.family = s.remote.family = r->idiag_family;
                if (s.local.family == AF_INET) {
                //这里 打印
                s.local.bytelen = s.remote.bytelen = 4;
                } else {
                s.local.bytelen = s.remote.bytelen = 16;
                }
                memcpy(s.local.data, r->id.idiag_src, s.local.bytelen);
                memcpy(s.remote.data, r->id.idiag_dst, s.local.bytelen);
                //printf("\n%-*s %-*s %-*s %-*s %-*s %-*s\n", 10,"state",10,"family",10,"l.addr",10,"l.port",10,"r.addr",10,"r.rport");
                printf("%-*s ", 10, sstate_name[s.state]);
                printf("%-*s",10,"AF_INET" );

                //printf("\n--------------\n");
                const inet_prefix *a=&s.remote;
                const void *addr =  a->data;

                ap=resolve_address(&s.local.data,4,AF_INET);
                printf("%-*s",15, ap);
                printf("%-*d ", 10,s.lport );
                //ap2=resolve_address(&s.remote.data,4,AF_INET);
                ap2=rt_addr_n2a(AF_INET,4,addr,buf2,sizeof(buf2));
                printf("%-*s", 15, ap2);
                printf("%-*d\n",10,s.rport);

                //printf("L.port:%-*d R.prot:%-*d\n", 10,s.lport ,10,s.rport);

                //printf("idiag_state:%d\n", pkg->idiag_state);
 
                //printf("idiag_state:%d\n", pkg->idiag_state);
                //printf("Family:%s\n", pkg->idiag_family == AF_INET ? "AF_INET" : "AF_INET6");
       // printf("dport:%d, sprot:%d\n", ntohs(pkg->id.idiag_sport), ntohs(pkg->id.idiag_sport));
                //printf("idiag_state:%d\n", pkg->idiag_state);

}


int main(int argc, char **argv)
{
        int fd;
    struct sk_req req;
    struct sockaddr_nl dest_addr;
    struct msghdr msg;
    char buf[8192];
    char src_ip[20];
    char dest_ip[20];
    struct iovec iov;
    if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) {
        //eprint(__LINE__, errno, "socket");
                printf("socket error\n");
        return -1;
    }

req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
req.nlh.nlmsg_pid = 0;

memset(&req.r, 0, sizeof(req.r));
req.r.idiag_family = AF_INET;
req.r.idiag_states = ((1 << TCP_CLOSING + 1) - 1);
iov.iov_base = &req;
iov.iov_len = sizeof(req);

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0;
dest_addr.nl_groups = 0;


memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

if (sendmsg(fd, &msg, 0) < 0) {
    //eprint(__LINE__, errno, "sendmsg");
    printf("socket error\n");
        return -1;
}
printf("\n%-*s %-*s %-*s %-*s   %-*s %-*s\n", 10,"state",10,"family",10,"l.addr",10,"l.port",10,"r.addr",10,"r.rport");
memset(buf, 0 ,sizeof(buf));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
while (1) {
    int status;
    struct nlmsghdr *h;
    msg = (struct msghdr) {
        (void *)&dest_addr, sizeof(dest_addr),
            &iov, 1, NULL, 0, 0
    };
    status = recvmsg(fd, &msg, 0);//recvmsg函数的返回值是读取的字节数,
    if (status < 0) {
        if (errno == EINTR)
            continue;
        //eprint(__LINE__, errno, "recvmsg");
        printf("socket error\n");
                continue;
    }
    if (status == 0) {
        printf("EOF on netlink\n");
        close(fd);
        return 0;
    }
    h = (struct nlmsghdr *)buf;
//      #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) &&                            (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) &&                            (nlh)->nlmsg_len <= (len))
    while (NLMSG_OK(h, status)) {
        struct inet_diag_msg *pkg = NULL;
                /*
  struct inet_diag_msg *pkg = NULL;
                /*
                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;
};
                */
        if (h->nlmsg_type == NLMSG_DONE) {
            close(fd);
            printf("NLMSG_DONE\n");
            return 0;
        }
        if (h->nlmsg_type == NLMSG_ERROR) {
            struct nlmsgerr *err;
            err = (struct nlmsgerr*)NLMSG_DATA(h);
            fprintf(stderr, "%d Error %d:%s\n", __LINE__, -(err->error), strerror(-(err->error)));
            close(fd);
            printf("NLMSG_ERROR\n");
            return 0;
        }
        pkg = (struct inet_diag_msg *)NLMSG_DATA(h);
        //print_skinfo(pkg);
        //printf("\n%-*s %-*s %-*s %-*s %-*s %-*s\n", 10,"state",10,"family",10,"l.addr",10,"l.port",10,"r.addr",10,"r.rport");
                print_info(pkg,h);

        //get_tcp_state(pkg->idiag_state);
        h = NLMSG_NEXT(h, status);
                //#define NLMSG_NEXT(nlh,len)    ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len),                                   (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
                //#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
    }//while
}//while
close(fd);
return 0;
}