Netlink的简单例子

  • 采用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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值