欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux netlink 编程示例(二)应用层

程序员文章站 2024-03-23 13:20:52
...

这篇文章给出一个netlink应用层程序用例,和上一篇博客内核篇结合起来参考。

内核版本:3.4.39

nlclient.c

/*
 *  Description : 应用层netlink编程
 *  Date        :20180529
 *  Author      :mason
 *  Mail        : aaa@qq.com
 *
 */
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "nlclient.h"
void main() {
    int nlfd;
    int *value;
    int opt, arg;
    unsigned int len;
	struct iovec iov[1];
	struct msghdr msg;
	struct sockaddr_nl src, dst;  
	struct nlmsghdr *nlh, *nlh1, *nlh2 = NULL;

    // 创建netlink套接字
    nlfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_TEST_MODULE);
    if (nlfd == -1) {
        log("create netlink socket fail\n");
        return;
    }
    
    memset(&src, 0, sizeof(struct sockaddr_nl));
    memset(&dst, 0, sizeof(struct sockaddr_nl));
    memset(&msg, 0, sizeof(struct msghdr)); 

    // 设置本地地址
    src.nl_family = AF_NETLINK; 
    src.nl_pid = getpid(); 
    src.nl_groups = 0;  

    // 设置内核netlink地址
    dst.nl_family = AF_NETLINK;
    dst.nl_pid = 0;    
    dst.nl_groups = 0;

    // 绑定本地地址
    bind(nlfd, (struct sockaddr*)&src, sizeof(struct sockaddr_nl));

    // 申请netlink消息头域
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(int));
    while(1) {
        log("input option :\n"
            
"1 : set \n"
            "2 : get \n"
            "default : quit\n");
        scanf("%d", &opt);
        switch (opt) {
            // set 
            case 1 :     
                log("input value to set:");
                scanf("%d", &arg);        
                memset(nlh, 0, NLMSG_SPACE(sizeof(int)));
                nlh->nlmsg_len = NLMSG_SPACE(sizeof(int));

                // 设置netlink 应用层的pid
                nlh->nlmsg_pid = getpid();

                // 设置消息类型
                nlh->nlmsg_type = NLKERNEL_SET;

                // 设置标志位
                nlh->nlmsg_flags = NLM_F_REQUEST;

                // 填充发送消息结构体
                iov[0].iov_base = (void *)nlh;
                iov[0].iov_len = nlh->nlmsg_len;
                value = (int *)NLMSG_DATA(nlh);
                *value = arg;
                msg.msg_name = (void *)&dst;
                msg.msg_namelen = sizeof(struct sockaddr_nl);
                msg.msg_iov = &iov[0];
                msg.msg_iovlen = 1;

                // 发送netlink 消息给内核
                sendmsg(nlfd, &msg, 0);
                log("send set msg to kernel success \n\n");
                break;
                
            // get
            case 2 :  
                memset(nlh, 0, NLMSG_SPACE(int));
                nlh->nlmsg_len = NLMSG_SPACE(int);
                nlh->nlmsg_pid = getpid();

                // 设置netlink消息类型
                nlh->nlmsg_type = NLKERNEL_GET;
                nlh->nlmsg_flags = NLM_F_REQUEST;
                iov[0].iov_base = (void *)nlh;
                iov[0].iov_len = nlh->nlmsg_len;
                msg.msg_name = (void *)&dst;
                msg.msg_namelen = sizeof(struct sockaddr_nl);
                msg.msg_iov = &iov[0];
                msg.msg_iovlen = 1;

                // 发送消息
                log("send get msg to kernel success \n");
                sendmsg(nlfd, &msg, 0);

                memset(nlh, 0, NLMSG_SPACE(sizeof(int)));
                nlh->nlmsg_len = NLMSG_SPACE(sizeof(int));
                iov[0].iov_base = (void *)nlh;
                iov[0].iov_len = nlh->nlmsg_len;
                msg.msg_iov = &iov[0];
                msg.msg_iovlen = 1;    

                // 接收netlink内核端消息
                len = recvmsg(nlfd, &msg, 0);
                value = (int *)NLMSG_DATA(nlh);
                log("kernel return : %d \r\n\n", *value);
                
            break;
            default :
                goto end;
        }
    }

 end:         
    close(nlfd);
    if (nlh)
        free(nlh);          

    return ; 
}

nlclient.h

#ifndef _NLCLIENT_H
#define _NLCLIENT_H
#define log(fmt,arg...) \
	printf("[nlclient] "fmt,##arg)

#define NETLINK_TEST_MODULE         17      /* 抓包 netlink 协议 */

typedef enum netlink_msg_type {
    NLKERNEL_GET = NLMSG_MIN_TYPE +1,        /* value : 17 */
    NLKERNEL_SET,                            /* value : 18 */

    NLKERNEL_END,
}NETLINK_MSG_TYPE;


#endif

Makefile

nlclient:
	$(CC) -o nlclient nlclient.c
clean:
	@rm -rf *.o nlclient


代码可以在github上 clone下来运行

aaa@qq.com:FuYuanDe/nlnetlink.git

编译完成后先运行netlink内核端再运行应用层程序,否则会出现创建netlink套接字失败问题。

运行截图如下:

应用层:分别执行了get, set, get 操作,通过netlink与内核通信

linux netlink 编程示例(二)应用层

内核端:

linux netlink 编程示例(二)应用层

相比其它的用户空间与内核空间通信方式,netlink的交互性非常好。此外在实际应用的时候要注意字节对齐问题。