Netlink是一种内核层与应用层通信的一种机制,比如说在做一个内核模块的时候,往往会需要应用层提供一些配置信息,这时候就可以使用netlink。netlink包括内核层和应用层,内核层注册一个netlink协议,然后定义一些消息类型,之后就可以等待来自应用层的消息了。这篇博文给出一个简单的netlink内核模块使用例子,应用层会在接下来的博文中给出。
内核版本:3.4.39。 不同内核版本可能带来编译问题,可以升级内核来解决。升级3.4.39内核可以参考这一篇博客(https://blog.csdn.net/fuyuande/article/details/79429441)
netlink 内核端代码如下:nlkernel.c
/*
* Description : 内核netlink编程
* Date :20180528
* Author :mason
* Mail : mrsonko@126.com
*
*/
#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");
nlkernel.h
#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
Makefile
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
代码托管在github 上:
git@github.com:FuYuanDe/nlnetlink.git
编译完成后加载模块查看效果:
insmod nlkernel.ko
dmesg