Linux Netlink 代码示例

✅作者简介:嵌入式领域优质创作者,博客专家
✨个人主页:咸鱼弟
🔥系列专栏:Linux专栏 

Netlink是一种内核层与应用层通信的一种机制,比如说在做一个内核模块的时候,往往会需要应用层提供一些配置信息,这时候就可以使用netlink。netlink包括内核层和应用层,内核层注册一个netlink协议,然后定义一些消息类型,之后就可以等待来自应用层的消息了。下面写了一个简单的代码例子。

内核态代码如下:


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <net/ip.h>
#include "nlkernel.h"
 
static int number;
static struct sock *nlsock = NULL;  // netlink 套接字
 
// netlink消息回复,用于向应用层传递数据
static void netlink_sendto_userapp(unsigned int dst_pid) {
    struct sk_buff *skb = NULL;
    struct nlmsghdr *nlh = NULL;
    int datalen;
    int *pnum;
    datalen = NLMSG_SPACE(sizeof(int));
 
    skb = alloc_skb(datalen, GFP_KERNEL);
    if(!skb)
    {
        log("alloc skb error.\r\n");
        return ;
    }
    // 数据初始化
    nlh = nlmsg_put(skb, 0, 0, 0, sizeof(int), 0);
    nlh->nlmsg_pid = 0;
    nlh->nlmsg_type = NLKERNEL_GET;
    
    pnum = (int *)NLMSG_DATA(nlh);
    *pnum = number;    
    netlink_unicast(nlsock, skb, dst_pid, MSG_DONTWAIT);
    log("netlink send done \r\n");
    
    return;    
}
 
/* netlink消息处理函数 */
static int netlink_kernel_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
    int *value = NULL;
    switch(nlh->nlmsg_type){
        // 设置
        case NLKERNEL_SET :
            log("kernel receive netlink set msg!\r\n");
            value = (int *)NLMSG_DATA(nlh);            
            number = *value;
            break;
        // 获取
        case NLKERNEL_GET :
            log("kernel receive netlink get msg!\r\n");
            netlink_sendto_userapp(nlh->nlmsg_pid);
            break;
        default:
            log("unrecognized netlink message type : %u \r\n",nlh->nlmsg_type);
            break;
    }   
    return 0;
}   
 
 
 
 
static void netlink_kernel_rcv(struct sk_buff *skb)
{
    int res;
	res = netlink_rcv_skb(skb, &netlink_kernel_rcv_msg);
    return;
}
 
 
 
 
// 模块入口函数
static int __init nlkernel_init(void) {
    log("nlkernel init \r\n");
    // 注册netlink协议
	nlsock = netlink_kernel_create(&init_net, NETLINK_TEST_MODULE, 0, netlink_kernel_rcv, NULL, THIS_MODULE);
    if (!nlsock) {
        log("netlink module init fail \r\n");
        return -1;
    }
    return 0;
}
 
 
// 模块退出函数
static void __exit nlkernel_exit(void) {
    // 注销netlink协议
	if(nlsock)
	{
        netlink_kernel_release(nlsock);
        nlsock = NULL;
	}
    log("nlkernel exit \r\n");
    return ;
}
 
module_init(nlkernel_init)
module_exit(nlkernel_exit)
MODULE_AUTHOR("mason");
MODULE_DESCRIPTION("netlink kernel test");
MODULE_LICENSE("GPL");
 

头文件

#ifndef __NLKERNEL_H__
#define __NLKERNEL_H__
#define log(fmt, arg...)  printk(KERN_INFO"[bfd] %s:%d "fmt, __FUNCTION__, __LINE__, ##arg)
 
#ifndef NIPQUAD
#define NIPQUAD(addr) \
	((unsigned char *)&addr)[0], \
	((unsigned char *)&addr)[1], \
	((unsigned char *)&addr)[2], \
	((unsigned char *)&addr)[3]
#endif
 
#define NETLINK_TEST_MODULE         17      /* 定义 netlink 协议, */
 
typedef enum netlink_msg_type {             /* 定义 netlink 消息类型 */
    NLKERNEL_GET = NLMSG_MIN_TYPE +1,        /* value : 17 */
    NLKERNEL_SET,                            /* value : 18 */
 
    NLKERNEL_END,
}NETLINK_MSG_TYPE;
 
#endif
obj-m := nlkernel.o
 
PWD := $(shell pwd)
KERNEL_DIR := "/usr/src/linux-headers-"$(shell uname -r)/
 
modules:
	@$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
clean:
	@rm -rf *.ko *.o *.mod.c *symvers *order *cmd
 

用户态代码如下:

/*
 *  Description : 应用层netlink编程
 *  Date        :20180529
 *  Author      :mason
 *  Mail        : mrsonko@126.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 ; 
}

头文件

#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
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值