netlink 用法

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/netdevice.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <netinet/ether.h>

int main()
{
    int nSocket, nLen, nAttrLen;
    char szBuffer[4096];
    struct {
        struct nlmsghdr nh;
        struct ifinfomsg ifi;
    }struReq;
    struct sockaddr_nl struAddr;
    struct nlmsghdr *pstruNL;
    struct ifinfomsg *pstruIF;
    struct rtattr *pstruAttr;
    struct net_device_stats *pstruInfo;
    struct ether_addr *pstruEther;

    /*
     * 创建一个PF_NETLINK的SOCKET,使用NETLINK_ROUTE协议
     */
    nSocket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
    if(nSocket < 0)
    {
        fprintf(stderr, "创建SOCKET错误:%s/n", strerror(errno));
        return -1;
    }

    /*
     * 绑定地址
     */
    memset(&struAddr, 0, sizeof(struAddr));
    struAddr.nl_family = AF_NETLINK;
    struAddr.nl_pid = getpid();
    struAddr.nl_groups = 0;
    if(bind(nSocket, (struct sockaddr *)&struAddr, sizeof(struAddr)) < 0)
    {
        fprintf(stderr, "绑定SOCKET错误:%s/n", strerror(errno));
        return -1;
    }

    /*
     * 发送一个请求
     */
    memset(&struReq, 0, sizeof(struReq));
    struReq.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struReq));
    struReq.nh.nlmsg_type = RTM_GETLINK;
    struReq.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    struReq.ifi.ifi_family = AF_UNSPEC;
    memset(&struAddr, 0, sizeof(struAddr));
    struAddr.nl_family = AF_NETLINK;
    struAddr.nl_pid = 0;
    struAddr.nl_groups = 0;
    if(sendto(nSocket, &struReq, struReq.nh.nlmsg_len, 0,
        (struct sockaddr *)&struAddr, sizeof(struAddr)) < 0)
    {
        fprintf(stderr, "发送数据错误:%s/n", strerror(errno));
        return -1;
    }

    /*
     * 循环接收数据,直到超时
     */
    alarm(30);
    memset(szBuffer, 0, sizeof(szBuffer));
    while((nLen = recv(nSocket, szBuffer, sizeof(szBuffer), 0)))
    {
        alarm(0);
        pstruNL = (struct nlmsghdr *)szBuffer;
        /*
         * 判断是否继续有数据
         */
        while(NLMSG_OK(pstruNL, nLen))
        {
            /*
             * 数据已经获取完成
             */
            if(pstruNL -> nlmsg_type == NLMSG_DONE)
                break;
            if(pstruNL -> nlmsg_type == NLMSG_ERROR)
            {
                /*
                 * 发生一个错误
                 */
                struct nlmsgerr *pstruError;

                pstruError = (struct nlmsgerr *)NLMSG_DATA(pstruNL);
                fprintf(stderr, "发生错误[%s]/n",
                    strerror(-pstruError -> error));
                break;
            }

            /*
             * 下面通过宏获取数据
             */
            pstruIF = NLMSG_DATA(pstruNL);
            fprintf(stderr, "获取到设备[%d]信息/n", pstruIF -> ifi_index);
            fprintf(stderr, "/t设备类型:");
            switch(pstruIF -> ifi_type)
            {
                case ARPHRD_ETHER:
                    fprintf(stderr, "以太网/n");
                    break;
                case ARPHRD_PPP:
                    fprintf(stderr, "PPP拨号/n");
                    break;
                case ARPHRD_LOOPBACK:
                    fprintf(stderr, "环路设备/n");
                    break;
                default:
                    fprintf(stderr, "未知/n");
                    break;
            }
            fprintf(stderr, "/t设备状态:");
            if((pstruIF -> ifi_flags & IFF_UP )== IFF_UP)
                fprintf(stderr, " UP");
            if((pstruIF -> ifi_flags & IFF_BROADCAST) == IFF_BROADCAST)
                fprintf(stderr, " BROADCAST");
            if((pstruIF -> ifi_flags & IFF_DEBUG) == IFF_DEBUG)
                fprintf(stderr, " DEBUG");
            if((pstruIF -> ifi_flags & IFF_LOOPBACK) == IFF_LOOPBACK)
                fprintf(stderr, " LOOPBACK");
            if((pstruIF -> ifi_flags & IFF_POINTOPOINT) == IFF_POINTOPOINT)
                fprintf(stderr, " POINTOPOINT");
            if((pstruIF -> ifi_flags & IFF_RUNNING) == IFF_RUNNING)
                fprintf(stderr, " RUNNING");
            if((pstruIF -> ifi_flags & IFF_NOARP) == IFF_NOARP)
                fprintf(stderr, " NOARP");
            if((pstruIF -> ifi_flags & IFF_PROMISC) == IFF_PROMISC)
                fprintf(stderr, " PROMISC");
            if((pstruIF -> ifi_flags & IFF_NOTRAILERS) == IFF_NOTRAILERS)
                fprintf(stderr, " NOTRAILERS");
            if((pstruIF -> ifi_flags & IFF_ALLMULTI) == IFF_ALLMULTI)
                fprintf(stderr, " ALLMULTI");
            if((pstruIF -> ifi_flags & IFF_MASTER) == IFF_MASTER)
                fprintf(stderr, " MASTER");
            if((pstruIF -> ifi_flags & IFF_SLAVE) == IFF_SLAVE)
                fprintf(stderr, " SLAVE");
            if((pstruIF -> ifi_flags & IFF_MULTICAST) == IFF_MULTICAST)
                fprintf(stderr, " MULTICAST");
            if((pstruIF -> ifi_flags & IFF_PORTSEL) == IFF_PORTSEL)
                fprintf(stderr, " SLAVE");
            if((pstruIF -> ifi_flags & IFF_AUTOMEDIA) == IFF_AUTOMEDIA)
                fprintf(stderr, " AUTOMEDIA");
            if((pstruIF -> ifi_flags & IFF_DYNAMIC) == IFF_DYNAMIC)
                fprintf(stderr, " DYNAMIC");
            fprintf(stderr, "/n");

            /*
             * 下面通过宏获取属性
             */
            pstruAttr = IFLA_RTA(pstruIF);
            nAttrLen = NLMSG_PAYLOAD(pstruNL, sizeof(struct ifinfomsg));
            while(RTA_OK(pstruAttr, nAttrLen))
            {
                switch(pstruAttr->rta_type)
                {
                    case IFLA_IFNAME:
                        fprintf(stderr, "/t设备名称:%s/n",
                            (char *)RTA_DATA(pstruAttr));
                        break;
                    case IFLA_MTU:
                        fprintf(stderr, "/t设备MTU:%d/n",
                            *(unsigned int *)RTA_DATA(pstruAttr));
                        break;
                    case IFLA_QDISC:
                        fprintf(stderr, "/t设备队列:%s/n",
                            (char *)RTA_DATA(pstruAttr));
                        break;
                    case IFLA_ADDRESS:
                        if(pstruIF -> ifi_type == ARPHRD_ETHER)
                        {
                            pstruEther = (struct ether_addr *)
                                RTA_DATA(pstruAttr);
                            fprintf(stderr, "/tMAC地址:%s/n",
                                ether_ntoa(pstruEther));
                        }
                        break;
                    case IFLA_BROADCAST:
                        if(pstruIF -> ifi_type == ARPHRD_ETHER)
                        {
                            pstruEther = (struct ether_addr *)
                                RTA_DATA(pstruAttr);
                            fprintf(stderr, "/t广播MAC地址:%s/n",
                                ether_ntoa(pstruEther));
                        }
                        break;
                    case IFLA_STATS:
                        pstruInfo = (struct net_device_stats *)
                            RTA_DATA(pstruAttr);
                        fprintf(stderr, "/t接收信息:/n");
                        fprintf(stderr, "/t/t接收报文:%lu 字节:%lu/n",
                            pstruInfo -> rx_packets, pstruInfo -> rx_bytes);
                        fprintf(stderr, "/t/terrors:%lu dropped:%lu "
                                "multicast:%lu collisions:%lu/n",
                            pstruInfo -> rx_errors, pstruInfo -> rx_dropped,
                            pstruInfo -> multicast, pstruInfo -> collisions);
                        fprintf(stderr, "/t/tlength:%lu over:%lu crc:%lu "
                                "frame:%lu fifo:%lu missed:%lu/n",
                            pstruInfo -> rx_length_errors,
                            pstruInfo -> rx_over_errors,
                            pstruInfo -> rx_crc_errors,
                            pstruInfo -> rx_frame_errors,
                            pstruInfo -> rx_fifo_errors,
                            pstruInfo -> rx_missed_errors);
                        fprintf(stderr, "/t发送信息:/n");
                        fprintf(stderr, "/t/t发送报文:%lu 字节:%lu/n",
                            pstruInfo -> tx_packets, pstruInfo -> tx_bytes);
                        fprintf(stderr, "/t/terrors:%lu dropped:%lu/n",
                            pstruInfo -> tx_errors, pstruInfo -> tx_dropped);
                        fprintf(stderr, "/t/taborted:%lu carrier:%lu fifo:%lu"
                                " heartbeat:%lu window:%lu/n",
                            pstruInfo -> tx_aborted_errors,
                            pstruInfo -> tx_carrier_errors,
                            pstruInfo -> tx_fifo_errors,
                            pstruInfo -> tx_heartbeat_errors,
                            pstruInfo -> tx_window_errors);
                        break;
                    default:
                        break;

                }
                /*
                 * 继续下一个属性
                 */
                pstruAttr = RTA_NEXT(pstruAttr, nAttrLen);
            }
            /*
             * 继续下一个数据
             */
            pstruNL = NLMSG_NEXT(pstruNL, nLen);
        }
        memset(szBuffer, 0, sizeof(szBuffer));
        alarm(30);
    }
    return 0;
}

netlink


root@wangxiaoming:~/Desktop/root/aodv/nelink_test/test1# ./netlink_test 
获取到设备[1]信息
	设备类型:环路设备
	设备状态: UP LOOPBACK RUNNING
	设备名称:lo
	设备MTU:16436
	设备队列:noqueue
	接收信息:
		接收报文:212 字节:12720
		errors:0 dropped:0 multicast:0 collisions:0
		length:0 over:0 crc:0 frame:0 fifo:0 missed:0
	发送信息:
		发送报文:212 字节:12720
		errors:0 dropped:0
		aborted:0 carrier:0 fifo:0 heartbeat:0 window:0
获取到设备[2]信息
	设备类型:以太网
	设备状态: UP BROADCAST RUNNING MULTICAST
	设备名称:eth0
	设备MTU:1500
	设备队列:pfifo_fast
	MAC地址:0:c:29:97:c2:4a
	广播MAC地址:ff:ff:ff:ff:ff:ff
	接收信息:
		接收报文:5079 字节:1657655
		errors:0 dropped:0 multicast:0 collisions:0
		length:0 over:0 crc:0 frame:0 fifo:0 missed:0
	发送信息:
		发送报文:4520 字节:693301
		errors:0 dropped:0
		aborted:0 carrier:0 fifo:0 heartbeat:0 window:0


### Netlink 的基本概念 Netlink 是一种 Linux 内核与用户空间之间的通信机制,支持双向消息传递。它主要用于内核子系统(如路由表更新、网络配置更改等)与用户空间程序之间的交互。 --- ### 用户态下的 Netlink 使用方法 在用户态下使用 Netlink 需要创建并操作一个特殊的 socket。以下是主要步骤: 1. **创建 Netlink Socket** 调用 `socket()` 函数创建一个 Netlink 类型的 socket。 2. **绑定本地地址** 使用 `bind()` 将该 socket 绑定到特定的 PID 和协议类型[^1]。 3. **发送和接收数据** 利用 `sendto()` 或 `recvfrom()` 进行消息的收发。 #### 示例代码 以下是一个简单的用户态 Netlink 客户端示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <linux/netlink.h> #define NETLINK_USER 31 int main() { struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; int sock_fd, ret; // 创建 Netlink 套接字 sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER); if (sock_fd < 0) { perror("Socket creation failed"); exit(EXIT_FAILURE); } // 设置源地址 memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); // 当前进程 ID bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)); // 设置目标地址 memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; // 发送给内核 // 构造 Netlink 消息头部 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(1024)); memset(nlh, 0, NLMSG_SPACE(1024)); nlh->nlmsg_len = NLMSG_SPACE(1024); // 总长度 nlh->nlmsg_pid = getpid(); // 返回给当前进程 nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh), "Hello from user space"); // 数据传输结构体 iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; // 发送消息 ret = sendmsg(sock_fd, &iov, 0); if (ret < 0) { perror("Send message failed"); close(sock_fd); free(nlh); exit(EXIT_FAILURE); } printf("Message sent\n"); close(sock_fd); free(nlh); return 0; } ``` --- ### 内核态下的 Netlink 实现 在内核中实现 Netlink 功能需要编写相应的模块,并注册处理函数。具体过程如下: 1. **包含必要的头文件** 包含 `<linux/module.h>` 和 `<linux/netlink.h>` 头文件[^2]。 2. **定义回调函数** 注册用于处理来自用户空间的消息的回调函数。 3. **初始化和清理工作** 在模块加载时完成初始化,在卸载时释放资源。 #### 示例代码 以下是一个简单的内核态 Netlink 接收器示例: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <net/sock.h> #include <linux/netlink.h> #include <linux/skbuff.h> #define NETLINK_USER 31 static struct sock *nl_sk = NULL; // 回调函数:处理收到的消息 static void recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; char *data; nlh = nlmsg_hdr(skb); data = (char *)NLMSG_DATA(nlh); printk(KERN_INFO "Received message payload: %s\n", data); } // 初始化函数 static int __init hello_init(void) { struct netlink_kernel_cfg cfg = { .input = recv_msg, }; nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); if (!nl_sk) { printk(KERN_ALERT "Error creating netlink socket.\n"); return -10; } printk(KERN_INFO "Netlink module loaded successfully.\n"); return 0; } // 清理函数 static void __exit hello_exit(void) { if (nl_sk != NULL) netlink_kernel_release(nl_sk); printk(KERN_INFO "Netlink module unloaded.\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple Netlink kernel example."); ``` --- ### 关键点说明 - 如果需要新增自定义的 Netlink 协议类型,则可以通过修改 `linux/netlink.h` 文件来扩展功能。 - 内核中的 Netlink 核心逻辑位于 `.c` 文件 `net/core/af_netlink.c` 中。 - 用户态和内核态之间不存在编译时依赖关系,因此可以独立开发和部署[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值