Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这里主要是关于内核空间中的netlink函数的使用。
extern struct sock *netlink_kernel_create(struct net *net, int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module);
|
struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用 init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。
在内核中,
audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0, audit_receive, NULL, THIS_MODULE);
|
模块调用函数 netlink_unicast 来发送单播消息:
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock) |
参数ssk为函数 netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而 skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用 定时睡眠。
netlink的内核实现在.c文件 net/core/af_netlink.c中,内核模块要想使用netlink,也必须包含头文件linux/netlink.h。内核使用 netlink需要专门的API,这完全不同于用户态应用对netlink的使用。如果用户需要增加新的netlink协议类型,必须通过修改 linux/netlink.h来实现,当然,目前的netlink实现已经包含了一个通用的协议类型NETLINK_GENERIC以方便用户使用,用户可以直接使用它而不必增加新的协议类型。前面讲到,为了增加新的netlink协议类型,用户仅需增加如下定义到linux/netlink.h就可以:
只要增加这个定义之后,用户就可以在内核的任何地方引用该协议。
在内核中,为了创建一个netlink socket用户需要调用如下函数:
extern struct sock *netlink_kernel_create(struct net *net, int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module); |
struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量
参数unit表示netlink协议类型,如 NETLINK_MYTEST,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被引用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct sock结构来表示。
函数input()会在发送进程执行sendmsg()时被调用,这样处理消息比较及时,但是,如果消息特别长时,这样处理将增加系统调用sendmsg()的执行时间,也就是说当用户的程序调用sendmsg ()函数时,如果input()函数处理时间过长,也就是说input()函数不执行不完,用户程序调用的sendmsg()函数就不会返回。只有当内核空间中的input()函数返回时,用户调用的sendmsg()函数才会返回。对于这种情况,可以定义一个内核线程专门负责消息接收,而函数input 的工作只是唤醒该内核线程,这样sendmsg将很快返回。(这里网上的的说明)不过在查看Linux2.6.37版本的内核时并没有发现这种处理过程,一般都是按下面的方法进行处理。
这里注册的netlink协议为NETLINK_XFRM。
nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, xfrm_netlink_rcv, NULL, THIS_MODULE);
static void xfrm_netlink_rcv(struct sk_buff *skb) { mutex_lock(&xfrm_cfg_mutex);
netlink_rcv_skb(skb, &xfrm_user_rcv_msg); mutex_unlock(&xfrm_cfg_mutex); } 在netlink_rcv_skb()函数中进行接收处理。
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)
|
前面的三个参数与 netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = 0;
NETLINK_CB(skb).dst_group = 1;
|
字段pid表示消息发送者进程 ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group 应当设置为 0。
下面是参考网上使用netlink写的和内核通信的两个程序,一个是用户空间,一个是内核空间。
内核空间:
#include <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/time.h> #include <linux/types.h> #include <net/sock.h> #include <net/netlink.h>
#define NETLINK_TEST 25 #define MAX_MSGSIZE 1024 int stringlength(char *s); void sendnlmsg(char * message); int pid; int err; struct sock *nl_sk = NULL; int flag = 0;
void sendnlmsg(char *message) { struct sk_buff *skb_1; struct nlmsghdr *nlh; int len = NLMSG_SPACE(MAX_MSGSIZE); int slen = 0; if(!message || !nl_sk) { return ; } skb_1 = alloc_skb(len,GFP_KERNEL); if(!skb_1) { printk(KERN_ERR "my_net_link:alloc_skb_1 error\n"); } slen = stringlength(message); nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);
NETLINK_CB(skb_1).pid = 0; NETLINK_CB(skb_1).dst_group = 0;
message[slen]= '\0'; memcpy(NLMSG_DATA(nlh),message,slen+1); printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));
netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);
}
int stringlength(char *s) { int slen = 0;
for(; *s; s++){ slen++; }
return slen; }
void nl_data_ready(struct sk_buff *__skb) { struct sk_buff *skb; struct nlmsghdr *nlh; char str[100]; struct completion cmpl; int i=10; skb = skb_get (__skb); if(skb->len >= NLMSG_SPACE(0)) { nlh = nlmsg_hdr(skb);
memcpy(str, NLMSG_DATA(nlh), sizeof(str)); printk("Message received:%s\n",str) ; pid = nlh->nlmsg_pid; while(i--) { init_completion(&cmpl); wait_for_completion_timeout(&cmpl,3 * HZ); sendnlmsg("I am from kernel!"); } flag = 1; kfree_skb(skb); }
}
// Initialize netlink
int netlink_init(void) {
nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1, nl_data_ready, NULL, THIS_MODULE);
if(!nl_sk){ printk(KERN_ERR "my_net_link: create netlink socket error.\n"); return 1; }
printk("my_net_link_3: create netlink socket ok.\n");
return 0; }
static void netlink_exit(void) { if(nl_sk != NULL){ sock_release(nl_sk->sk_socket); }
printk("my_net_link: self module exited\n"); }
module_init(netlink_init); module_exit(netlink_exit);
MODULE_AUTHOR("frankzfz"); MODULE_LICENSE("GPL");
|
下面是用户空间的程序:
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <string.h> #include <asm/types.h> #include <linux/netlink.h> #include <linux/socket.h> #include <errno.h>
#define NETLINK_TEST 25 #define MAX_PAYLOAD 1024 // maximum payload size
int main(int argc, char* argv[]) { int state; struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; struct msghdr msg; int sock_fd, retval; int state_smg = 0; // Create a socket
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); if(sock_fd == -1){ printf("error getting socket: %s", strerror(errno)); return -1; }
// To prepare binding
memset(&msg,0,sizeof(msg)); memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); // self pid
src_addr.nl_groups = 0; // multi cast
retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); if(retval < 0){ printf("bind failed: %s", strerror(errno)); close(sock_fd); return -1; }
// To prepare recvmsg
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); if(!nlh){ printf("malloc nlmsghdr error!\n"); close(sock_fd); return -1; }
memset(&dest_addr,0,sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 0;
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh),"Hello you!");
iov.iov_base = (void *)nlh; iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD); // iov.iov_len = nlh->nlmsg_len;
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;
printf("state_smg\n"); state_smg = sendmsg(sock_fd,&msg,0);
if(state_smg == -1) { printf("get error sendmsg = %s\n",strerror(errno)); }
memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD)); printf("waiting received!\n"); // Read message from kernel
while(1){ printf("In while recvmsg\n"); state = recvmsg(sock_fd, &msg, 0); if(state<0) { printf("state<1"); } printf("In while\n"); printf("Received message: %s\n",(char *) NLMSG_DATA(nlh)); }
close(sock_fd);
return 0; }
|
下面是Makefile文件:
obj-m := netlink_k.o KERNELBUILD := /lib/modules/`uname -r`/build default: @echo "BUILE Kmod" @make -C $(KERNELBUILD) M=$(shell pwd) modules gcc -o netlink_2 netlink_2.c clean: @echo " CLEAN kmod" @rm -rf *.o @rm -rf .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers .*.d
|
其中,netlink_k.c为内核的空间的程序。
先运行内核代码netlink_k.ko,也就是在执行完makefile文件后,会生成一个netlink_k.ko文件,可以使用下面的命令进行安装,insmod netlink_k.ko,使用lsmod查看,当安装成功后,然后,执行./netlink用户空间程序,可以在另一个终端下执行dmesg命令,查看内核通信的情况。这里netlink程序向内核空间发送一个hello you!内核返回给一个I am from kernel!在这里使用了一个定时器,也就是每3秒中发送一次I am from kernel!只有内核把10个字符串全部发送完毕后,用户空间的sendmsg()才会返回,也就是在用户空间的netlink才会输出内核空间发送过来的数据,这里只有一个简单的程序,并没有什么实际的意义,因为,正如前面所说的一般情况下不会在回调函数中处理太多的东西,以免sendmsg()函数返回不及时。下面是使用dmesg命令输出的信息。
[873791.498039] my_net_link_3: create netlink socket ok. [873810.263676] Message received:Hello [873813.260848] my_net_link_4:send message 'I am from kernel!'. [873816.260821] my_net_link_4:send message 'I am from kernel!'. [873819.260860] my_net_link_4:send message 'I am from kernel!'. [873822.260762] my_net_link_4:send message 'I am from kernel!'. [873825.260883] my_net_link_4:send message 'I am from kernel!'. [873828.260669] my_net_link_4:send message 'I am from kernel!'. [873831.260714] my_net_link_4:send message 'I am from kernel!'. [873834.260683] my_net_link_4:send message 'I am from kernel!'. [873837.260666] my_net_link_4:send message 'I am from kernel!'. [873840.260632] my_net_link_4:send message 'I am from kernel!'.
|
参考网址:
http://blog.csdn.net/wangjingfei/archive/2010/02/05/5288460.aspx
http://blog.csdn.net/liumang_D/archive/2010/03/25/5413042.aspx
http://www.ibm.com/developerworks/cn/linux/l-netlink/?ca=dwcn-newsletter-linu
阅读(1464) | 评论(0) | 转发(0) |