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
源代码:
#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; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。