netlink通信——用户态与内核态通信具体实现

这篇文章主要讲解通过netlink通信机制,具体实现用户态与内核态的数据传送。我们的内核环境是2.6.35.6版本。

</usr/include/linux/netlink.h>文件里包含了Netlink协议簇已经定义好的一些预定义协议:

#define NETLINK_ROUTE           0       /* Routing/device hook                          */
#define NETLINK_UNUSED          1       /* Unused number                                */
#define NETLINK_USERSOCK        2       /* Reserved for user mode socket protocols      */
#define NETLINK_FIREWALL        3       /* Firewalling hook                             */
#define NETLINK_INET_DIAG       4       /* INET socket monitoring                       */
#define NETLINK_NFLOG           5       /* netfilter/iptables ULOG */
#define NETLINK_XFRM            6       /* ipsec */
#define NETLINK_SELINUX         7       /* SELinux event notifications */
#define NETLINK_ISCSI           8       /* Open-iSCSI */
#define NETLINK_AUDIT           9       /* auditing */
#define NETLINK_FIB_LOOKUP      10
#define NETLINK_CONNECTOR       11
#define NETLINK_NETFILTER       12      /* netfilter subsystem */
#define NETLINK_IP6_FW          13
#define NETLINK_DNRTMSG         14      /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT  15      /* Kernel messages to userspace */
#define NETLINK_GENERIC         16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT   18      /* SCSI Transports */
#define NETLINK_ECRYPTFS        19

#define NETLINK_MYTEST 20

如果我们在Netlink协议簇里开发一个新的协议,只要在该文件中定义协议号即可,例如我们定义一种基于Netlink协议簇的、协议号是20的自定义协议,如上所示。同时记得,将内核头文件目录中的netlink.h也做对应的修改,在我的系统中它的路径是:/usr/src/kernels/2.6.35.6-45.fc14.i686/include/linux/netlink.h

接下来我们在用户空间以及内核空间模块的开发过程中就可以使用这种协议了,需要说明的是,如果在这两个文件中不添加协议号,就需要在具体通信实现代码文件中添加这两个协议号,在我的具体实现中,就是采用这种方法。

用户态通信代码(mynlusr.c):

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define NETLINK_MYTEST 20
#define MAX_PAYLOAD 1024

int main(int argc, char ** argv)
{
	struct sockaddr_nl src_addr,dest_addr;
	struct nlmsghdr *nlh = NULL;
	struct msghdr msg;
	struct iovec iov;
	int sock_fd = -1;
	/*创建套接字*/
	if(-1 == (sock_fd = socket(AF_NETLINK,SOCK_RAW,NETLINK_MYTEST))){
		printf("can't create netlink socket!\n");
		return -1;
	}
	/*设置src_addr结构*/
	memset(&src_addr, 0, sizeof(src_addr));
	src_addr.nl_family = AF_NETLINK;
	src_addr.nl_pid = getpid();/*自己的pid*/
	src_addr.nl_groups = 0;
	
	/*将套接字和netlink地址结构进行绑定*/
	if(-1 == bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){
		printf("can't bind sock_fd with sockaddr_nl");
		return -1;
	}
	if(NULL == (nlh=(struct nlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD)))){
		printf("alloc mem failed!\n");
		return -1;
	}
	memset(nlh, 0, MAX_PAYLOAD);
	/*填充netlink的消息头部*/
	nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
	nlh->nlmsg_pid = getpid();
	nlh->nlmsg_type = NLMSG_NOOP;
	nlh->nlmsg_flags = 0;
	
	/*设置netlink的消息内容*/
	strcpy(NLMSG_DATA(nlh), argv[1]);
	
	memset(&iov, 0, sizeof(iov));
	iov.iov_base = (void *)nlh;
	iov.iov_len = nlh->nlmsg_len;
	
	/*设置dest_addr结构*/
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_family = AF_NETLINK;
	dest_addr.nl_pid = 0;/*目的地址的pid,这里是发送给内核*/
	dest_addr.nl_groups = 0;
	
	memset(&msg, 0, sizeof(msg));
	msg.msg_name = (void *)&dest_addr;
	msg.msg_namelen = sizeof(dest_addr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	
	/*通过netlink socket向内涵发送消息*/
	sendmsg(sock_fd, &msg, 0);
	
	/*接受内核消息*/
	printf("waiting message from kernel!\n");
	memset((char *)NLMSG_DATA(nlh),0,1024);
	recvmsg(sock_fd,&msg,0);
	printf("response:%s\n",NLMSG_DATA(nlh));
	
	/*关闭netlink套接字*/
	close(sock_fd);
	free(nlh);
	return 0;
}

内核态通信代码(mynlkern.c):

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <net/netlink.h>

#define NETLINK_MYTEST 20
#define MAX_MSGSIZE 1024
struct sock *nl_sk = NULL;

static void sendnlmsg(char *message, int dstpid)
{
	struct sk_buff *skb;
	struct nlmsghdr *nlh;
	int len = NLMSG_SPACE(MAX_MSGSIZE);
	int slen = 0;
	if(!message || !nl_sk){
		return ;
	}
	/*为新的sk_buffer申请空间*/
	skb = alloc_skb(len,GFP_KERNEL);
	if(!skb){
		printk(KERN_ERR "my_net_link:alloc_skb Error!\n");
		return ;
	}
	slen = strlen(message);
	
	/*用nlmsg_put来设置netlink消息头部*/
	nlh = nlmsg_put(skb,0,0,0,MAX_MSGSIZE,0);
	
	/*设置Netlink的控制块*/
	NETLINK_CB(skb).pid = 0;/*消息发送者的id标识,如果是内核发的则置0*/
	NETLINK_CB(skb).dst_group = 0;/*如果目的的组为内核或某一进程,该字段为0*/
	
	message[slen] = '\0';
	memcpy(NLMSG_DATA(nlh),message,slen+1);
	
	/*通过netlink_unicast将消息发送给用户空间*/
	printk("kernel:begin send\n");
	netlink_unicast(nl_sk,skb,dstpid,0);
	printk("kernel:send OK!\n");
	return ;
}

static void nl_data_ready(struct sk_buff* skb)
{
	struct nlmsghdr *nlh;
	u64 rlen;
	void *data;
	
	while(skb->len >= NLMSG_SPACE(0)){
		nlh = nlmsg_hdr(skb);
		printk("nlh->nlmsg_pid=%u skb->len =%d NLMSG_SPACE(0)=%d\n",nlh->nlmsg_pid,skb->len,NLMSG_SPACE(0));
		if(nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
			return ;
		
		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
		if(rlen > skb->len)
			rlen = skb->len;
		
		data = NLMSG_DATA(nlh);
		printk("received netlink message: %s\n",(char *)data);
		skb_pull(skb, rlen);
		
		sendnlmsg("send from kernel",nlh->nlmsg_pid);
	}
	printk("received finished!\n");
}

static int __init myinit_module(void)
{
	printk("my netlink in!\n");
	nl_sk = netlink_kernel_create(&init_net, NETLINK_MYTEST, 0, nl_data_ready, NULL, THIS_MODULE);
	if(!nl_sk){
        printk(KERN_ERR "my_net_link: create netlink socket error!\n");
        return 1;
    }else{
		return 0;
	}
}

static void __exit myexit_module(void)
{
	printk("my netlink out!\n");
	if(nl_sk != NULL){
		sock_release(nl_sk->sk_socket);
	}
}

MODULE_AUTHOR("Fang Xieyun");
MODULE_LICENSE("GPL");

module_init(myinit_module);
module_exit(myexit_module);


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内核中,可以通过注册 Netlink 套接字的回调函数来监测 Netlink 连接的断开。当连接断开时,内核会调用注册的回调函数,从而可以在回调函数中执行相应的处理逻辑。 以下是一个示例代码,展示了如何在内核监测 Netlink 连接的断开: ```c #include <linux/netlink.h> struct sock *nl_sock; // Netlink 套接字 void netlink_disconnect(struct sock *sk) { // 处理连接断开的逻辑 } struct netlink_kernel_cfg cfg = { .input = NULL, .cb_flags = NL_CB_DISABLE, .groups = 0, .flags = 0, .bind = NULL, .unbind = netlink_disconnect, // 注册连接断开的回调函数 }; int init_module(void) { nl_sock = netlink_kernel_create(&init_net, NETLINK_GENERIC, &cfg); if (!nl_sock) { printk(KERN_ERR "Failed to create Netlink socket\n"); return -ENOMEM; } // 其他初始化操作 return 0; } void cleanup_module(void) { if (nl_sock) { netlink_kernel_release(nl_sock); nl_sock = NULL; } // 其他清理操作 } ``` 在上述示例中,我们定义了一个 `netlink_disconnect` 函数作为连接断开的回调函数。当连接断开时,内核会调用这个函数,并将对应的 Netlink 套接字传递给它。您可以在这个函数中编写处理连接断开的逻辑,例如释放资源、清理状等。 在 `cfg` 结构体中,我们将 `unbind` 字段设置为 `netlink_disconnect` 函数,从而注册连接断开的回调函数。 在 `init_module` 函数中,我们使用 `netlink_kernel_create` 函数创建 Netlink 套接字,并将配置结构体 `cfg` 传递给它。在这个函数中,我们也进行了其他的初始化操作。 在 `cleanup_module` 函数中,我们释放了创建的 Netlink 套接字,并进行了其他的清理操作。 请注意,这只是一个简单的示例代码,您可能需要根据实际需求进行修改和扩展。同时,还可以根据需要在回调函数中执行其他操作,例如发送通知、记录日志等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值