- 采用Netlink进行内核与用户空间交互数据
参考:netlink学习笔记之NETLINK_INET_DIAG获取TCP连接状态(netstat) – 浮生笔记
比如获取连接状态
#if 0 #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> int main(int argc, char **argv) { int fd; struct sockaddr_nl src_addr, dest_addr; struct { struct nlmsghdr nlh; struct inet_diag_req r; } req; struct inet_diag_msg *pkg; struct msghdr msg; char buf[8192]; char src_ip[40]; char dest_ip[40]; struct iovec iov; if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) return -1; int ret; ret = fcntl(fd, F_SETFL, O_NONBLOCK); if (ret < 0) { fprintf(stderr, "Can't set socket flags"); close(fd); return -1; } //src addr memset(&src_addr, 0, sizeof(struct sockaddr_nl)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; if (bind(fd, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl)) < 0) { fprintf(stderr, "bind socket error %s\n", strerror(errno)); } memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = TCPDIAG_GETSOCK; req.nlh.nlmsg_flags = NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ROOT; // req.nlh.nlmsg_flags = NLM_F_REQUEST ; 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); //states to dump //send msg to kernel iov.iov_base = &req; iov.iov_len = sizeof(req); //dest addr memset(&dest_addr, 0, sizeof(struct sockaddr_nl)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 0; 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) { printf("%s\n", strerror(errno)); return -1; } //recv msg from kernel iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { //printf("while1\n"); int status; struct nlmsghdr *h; msg = (struct msghdr) { (void *)&dest_addr, sizeof(struct sockaddr_nl), &iov, 1, NULL, 0, 0 }; //length of recv data status = recvmsg(fd, &msg, 0); //status = recv(fd, buf, sizeof(buf), 0); printf("status = %d\n", status); if (status < 0) { if (errno == EINTR) { continue; } printf("errno = %d\n", errno); continue; } if (status == 0) { close(fd); printf("EOF\n"); return 0; } h = (struct nlmsghdr *)buf; while (NLMSG_OK(h, status)) { //printf("while2\n"); printf("nlmsg_type = %d\n",h->nlmsg_type ); 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); memset(src_ip, 0, sizeof(src_ip)); memset(dest_ip, 0, sizeof(dest_ip)); inet_ntop(pkg->idiag_family, pkg->id.idiag_src, src_ip, sizeof(src_ip)); inet_ntop(pkg->idiag_family, pkg->id.idiag_dst, dest_ip, sizeof(dest_ip)); printf("%-8s %4d %40s:%-6hu %40s:%-6hu\n", pkg->idiag_family == AF_INET ? "AF_INET" : "AF_INET6", pkg->idiag_state , src_ip, ntohs(pkg->id.idiag_sport), dest_ip, ntohs(pkg->id.idiag_dport)); // get_tcp_state(pkg->idiag_state); h = NLMSG_NEXT(h, status); //printf("status = %d\n\n", status); }//while }//while close(fd); return 0; } #else /** * @file NET_LINK_NET_STATE.c * @author dennisthink (https://www.dennisthink.com/) * @brief NETLINK方式获取 网络连接状态 * @version 0.1 * @date 2020-11-21 * * @copyright Copyright (c) 2020 * */ #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 <arpa/inet.h> /** * @brief 创建NetLinkDiag的socket * * @return int > 0 创建成功 * - 1 创建失败 */ int createNetLinkDiagSocket() { int fd = 0; if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) { return -1; } else { return fd; } } /** * @brief 设置socket为非阻塞模式 * * @param fd 被设置的socket描述符 * @return int >= 0 -- 成功 * -1 -- 失败 */ int setSocketOpt(int fd) { int ret = 0; ret = fcntl(fd, F_SETFL, O_NONBLOCK); if (ret < 0) { fprintf(stderr, "Can't set socket flags"); close(fd); return -1; } else { return ret; } } /** * @brief 绑定socket到NetLink的地址 * * @param fd 待绑定的socket * @return int 0 --- 成功 * -1 --- 失败 */ int bindSocketToNetLink(int fd) { struct sockaddr_nl src_addr; { //src addr memset(&src_addr, 0, sizeof(struct sockaddr_nl)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; } if (bind(fd, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl)) >=0 ) { return 0; } else { return -1; } } /** * @brief 发送消息到内核 * * @param fd netlink的socket * @return int 0 -- 成功 * -1 -- 失败 */ int sendMsgToNetLink(int fd) { //4.1 初始化目标地址 struct sockaddr_nl dest_addr; { memset(&dest_addr, 0, sizeof(struct sockaddr_nl)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 0; } //4.2 初始化请求的数据 struct { struct nlmsghdr nlh; struct inet_diag_req r; } req; { memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = TCPDIAG_GETSOCK; req.nlh.nlmsg_flags = NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ROOT; // req.nlh.nlmsg_flags = NLM_F_REQUEST ; 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); //states to dump } //4.3 将请求数据绑定到消息上 //4.3.1 将req数据绑定到iov上 //注意:此处要定义一个数组,保存足够的buff,要不会出现 errno=105的错误 struct iovec iov[2]; char buff[4096]={0}; { //send msg to kernel iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = buff; iov[1].iov_len = sizeof(buff); } struct msghdr msgForSend; //4.3.2 将目标地址和请求数据绑定到msg上 { msgForSend.msg_name = (void *)&dest_addr; msgForSend.msg_namelen = sizeof(dest_addr); msgForSend.msg_iov = iov; msgForSend.msg_iovlen = 2; } //4.4 发送消息 if (sendmsg(fd, &msgForSend, 0) < 0) { return -1; } return 0; } /** * @brief 解析TCP的状态 * * @param nState tcp状态 */ void parseTcpState(const int nState) { switch(nState) { case TCP_ESTABLISHED: { printf("ESTABLISHED"); }break; case TCP_SYN_SENT: { printf("SYN_SENT"); }break; case TCP_SYN_RECV: { printf("SYN_RECV"); }break; case TCP_FIN_WAIT1: { printf("FIN_WAIT1"); }break; case TCP_FIN_WAIT2: { printf("FIN_WAIT2"); }break; case TCP_TIME_WAIT: { printf("TIME_WAIT"); }break; case TCP_CLOSE: { printf("CLOSE"); }break; case TCP_CLOSE_WAIT: { printf("CLOSE_WAIT"); }break; case TCP_LAST_ACK: { printf("LAST_ACK"); }break; case TCP_CLOSING: { printf("CLOSING"); }break; case TCP_LISTEN: { printf("LISTEN"); }break; default: { printf("DEFAULT"); }break; } } /** * @brief 解析收到的数据 * * @param buff 数据起始地址 * @param status 数据的长度 * @return int 默认返回0 */ int parseRecvMsg(char *buff,int status) { struct nlmsghdr * h = (struct nlmsghdr *)buff; while (NLMSG_OK(h, status)) { if (h->nlmsg_type == NLMSG_DONE) { 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))); return 0; } struct inet_diag_msg *pkg = (struct inet_diag_msg *)NLMSG_DATA(h); char src_ip[40]; char dest_ip[40]; memset(src_ip, 0, sizeof(src_ip)); memset(dest_ip, 0, sizeof(dest_ip)); inet_ntop(pkg->idiag_family, pkg->id.idiag_src, src_ip, sizeof(src_ip)); inet_ntop(pkg->idiag_family, pkg->id.idiag_dst, dest_ip, sizeof(dest_ip)); printf("%-8s %40s:%-6hu %40s:%-6hu", pkg->idiag_family == AF_INET ? "AF_INET" : "AF_INET6", src_ip, ntohs(pkg->id.idiag_sport), dest_ip, ntohs(pkg->id.idiag_dport)); parseTcpState(pkg->idiag_state); printf("\n"); h = NLMSG_NEXT(h, status); } //while return 0; } /** * @brief 从netlink接收消息 * * @param fd netlink套接字 * @return int 默认返回0 */ int recvMsgFromNetLink(int fd) { char buf[8192]={0}; //recv msg from kernel struct iovec iov; iov.iov_base = buf; iov.iov_len = sizeof(buf); struct sockaddr_nl destAddrForRecv; { memset(&destAddrForRecv, 0, sizeof(struct sockaddr_nl)); destAddrForRecv.nl_family = AF_NETLINK; destAddrForRecv.nl_pid = 0; destAddrForRecv.nl_groups = 0; } struct msghdr msgForRecv; while (1) { msgForRecv.msg_name = (void *)(&destAddrForRecv); msgForRecv.msg_namelen = sizeof(destAddrForRecv); msgForRecv.msg_iov = &iov; msgForRecv.msg_iovlen = 1; //length of recv data int status = recvmsg(fd, &msgForRecv, 0); if (status == 0) { return 0; } if(status > 0) { parseRecvMsg(buf,status); return 0; } usleep(1000); }//while return 0; } int main(int argc, char **argv) { //创建socket int fd = createNetLinkDiagSocket(); if(fd > 0) { printf("Create Socket Succeed\n"); } else { printf("Create Socket Failed ErrNo:%d ErrMsg:%s\n",errno,strerror(errno)); return -1; } //2. 设置socket选项 if(setSocketOpt(fd) >= 0) { printf("Set socketOpt succeed \n"); } else { printf("Set sockOpt faild ErrNo:%d ErrMsg:%s\n",errno,strerror(errno)); return -1; } //3. 绑定socket if(bindSocketToNetLink(fd) >= 0) { printf("bind Socket To NetLink Succeed\n"); } else { printf("bind Socket To NetLink faild ErrNo:%d ErrMsg:%s\n",errno,strerror(errno)); return -1; } //4. 发送消息到内核 if(sendMsgToNetLink(fd) >= 0) { printf("sendMsgToNetLink Succeed \n"); } else { printf("sendMsgToNetLink Failed ErrNo:%d ErrMsg:%s\n",errno,strerror(errno)); return -1; } if(recvMsgFromNetLink(fd)>=0) { printf("recvMsgFromNetLink Succeed\n"); } else { printf("recvMsgFromNetLink Failed ErrNo:%d ErrMsg:%s\n",errno,strerror(errno)); } close(fd); return 0; } #endif